MXS-1683: Use non-interactive mode when terminal setup fails

When the terminal configuration fails, it usually means that no terminal
is attached to the controlling process. This is the case when scripts are
executed automatically by daemon processes. To allow maxadmin use without
a controlling terminal, the editline library functionality must not be
used when the terminal setup fails.

To facilitate this fix, a minor refactoring of the code was done to split
the parts that use editline into separate functions. This allows simple
and easy fallback to non-editline code in the case that editline is
available but the terminal is not interactive.
This commit is contained in:
Markus Mäkelä
2018-03-05 18:22:15 +02:00
parent d5226fa7d1
commit 0366736759

View File

@ -43,6 +43,9 @@
#ifdef HISTORY
#include <histedit.h>
#define USE_HIST 1
#else
#define USE_HIST 0
#endif
#define MAX_PASSWORD_LEN 80
@ -62,6 +65,7 @@ static void read_inifile(char **socket,
char **hostname, char **port, char **user, char **passwd,
int *editor);
static bool getPassword(char *password, size_t length);
static void rtrim(char *str);
#ifdef HISTORY
@ -93,6 +97,128 @@ static struct option long_options[] =
#define MAXADMIN_DEFAULT_USER "admin"
#define MAXADMIN_BUFFER_SIZE 2048
static bool term_error = false;
bool process_command(int so, char* buf)
{
bool rval = true;
if (isquit(buf))
{
rval = false;
}
else if (!strncasecmp(buf, "source", 6))
{
char *ptr;
/* Find the filename */
ptr = &buf[strlen("source")];
while (*ptr && isspace(*ptr))
{
ptr++;
}
DoSource(so, ptr);
}
else if (*buf)
{
if (!sendCommand(so, buf))
{
rval = false;
}
}
return rval;
}
void cmd_with_history(int so, char** argv, bool use_emacs)
{
#ifdef HISTORY
char *buf;
EditLine *el = NULL;
Tokenizer *tok;
History *hist;
HistEvent ev;
hist = history_init(); /* Init the builtin history */
/* Remember 100 events */
history(hist, &ev, H_SETSIZE, 100);
tok = tok_init(NULL); /* Initialize the tokenizer */
/* Initialize editline */
el = el_init(*argv, stdin, stdout, stderr);
if (use_emacs)
{
el_set(el, EL_EDITOR, "emacs"); /** Editor is emacs */
}
else
{
el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */
}
el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */
el_set(el, EL_PROMPT, prompt); /* Set the prompt function */
/* Tell editline to use this history interface */
el_set(el, EL_HIST, history, hist);
/*
* Bind j, k in vi command mode to previous and next line, instead
* of previous and next history.
*/
el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL);
el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL);
/*
* Source the user's defaults file.
*/
el_source(el, NULL);
int num = 0;
while ((buf = (char *) el_gets(el, &num)))
{
rtrim(buf);
history(hist, &ev, H_ENTER, buf);
if (!strcasecmp(buf, "history"))
{
for (int rv = history(hist, &ev, H_LAST); rv != -1;
rv = history(hist, &ev, H_PREV))
{
fprintf(stdout, "%4d %s\n", ev.num, ev.str);
}
}
else if (!process_command(so, buf))
{
break;
}
}
el_end(el);
tok_end(tok);
history_end(hist);
#endif
}
void cmd_no_history(int so)
{
char buf[MAXADMIN_BUFFER_SIZE];
while (printf("MaxScale> ") && fgets(buf, 1024, stdin) != NULL)
{
rtrim(buf);
if (!strcasecmp(buf, "history"))
{
fprintf(stderr, "History not supported in this version.\n");
}
else if (!process_command(so, buf))
{
break;
}
}
}
/**
* The main for the maxadmin client
*
@ -102,15 +228,6 @@ static struct option long_options[] =
int
main(int argc, char **argv)
{
#ifdef HISTORY
char *buf;
EditLine *el = NULL;
Tokenizer *tok;
History *hist;
HistEvent ev;
#else
char buf[MAXADMIN_BUFFER_SIZE];
#endif
char *hostname = NULL;
char *port = NULL;
char *user = NULL;
@ -293,105 +410,16 @@ main(int argc, char **argv)
}
(void) setlocale(LC_CTYPE, "");
#ifdef HISTORY
hist = history_init(); /* Init the builtin history */
/* Remember 100 events */
history(hist, &ev, H_SETSIZE, 100);
tok = tok_init(NULL); /* Initialize the tokenizer */
/* Initialize editline */
el = el_init(*argv, stdin, stdout, stderr);
if (use_emacs)
if (!term_error && USE_HIST)
{
el_set(el, EL_EDITOR, "emacs"); /** Editor is emacs */
cmd_with_history(so, argv, use_emacs);
}
else
{
el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */
}
el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */
el_set(el, EL_PROMPT, prompt); /* Set the prompt function */
/* Tell editline to use this history interface */
el_set(el, EL_HIST, history, hist);
/*
* Bind j, k in vi command mode to previous and next line, instead
* of previous and next history.
*/
el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL);
el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL);
/*
* Source the user's defaults file.
*/
el_source(el, NULL);
int num;
while ((buf = (char *) el_gets(el, &num)) != NULL && num != 0)
{
#else
while (printf("MaxScale> ") && fgets(buf, 1024, stdin) != NULL)
{
int num = strlen(buf);
#endif
/* Strip trailing \n\r */
for (int i = num - 1; buf[i] == '\r' || buf[i] == '\n'; i--)
{
buf[i] = 0;
}
#ifdef HISTORY
history(hist, &ev, H_ENTER, buf);
#endif
if (isquit(buf))
{
break;
}
else if (!strcasecmp(buf, "history"))
{
#ifdef HISTORY
int rv;
for (rv = history(hist, &ev, H_LAST); rv != -1;
rv = history(hist, &ev, H_PREV))
{
fprintf(stdout, "%4d %s\n",
ev.num, ev.str);
}
#else
fprintf(stderr, "History not supported in this version.\n");
#endif
}
else if (!strncasecmp(buf, "source", 6))
{
char *ptr;
/* Find the filename */
ptr = &buf[strlen("source")];
while (*ptr && isspace(*ptr))
{
ptr++;
}
DoSource(so, ptr);
}
else if (*buf)
{
if (!sendCommand(so, buf))
{
return 0;
}
}
cmd_no_history(so);
}
#ifdef HISTORY
el_end(el);
tok_end(tok);
history_end(hist);
#endif
close(so);
return 0;
}
@ -990,5 +1018,12 @@ bool getPassword(char *passwd, size_t len)
printf("\n");
// Store failure globally so that interactive parts are skipped
if (err)
{
term_error = true;
}
return *passwd;
}