]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Backport from BEE
[bacula/bacula] / bacula / src / console / console.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *
18  *   Bacula Console interface to the Director
19  *
20  *     Kern Sibbald, September MM
21  *
22  */
23
24 #include "bacula.h"
25 #include "console_conf.h"
26 #include "jcr.h"
27
28
29 #ifdef HAVE_CONIO
30 #include "conio.h"
31 //#define CONIO_FIX 1
32 #else
33 #define con_init(x)
34 #define con_term()
35 #define con_set_zed_keys();
36 #define trapctlc()
37 #define clrbrk()
38 #define usrbrk() 0
39 #endif
40
41 #if defined(HAVE_WIN32)
42 #define isatty(fd) (fd==0)
43 #endif
44
45 /* Exported variables */
46
47 //extern int rl_catch_signals;
48
49 /* Imported functions */
50 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
51 extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
52
53 /* Forward referenced functions */
54 static void terminate_console(int sig);
55 static int check_resources();
56 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
57 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
58 void senditf(const char *fmt, ...);
59 void sendit(const char *buf);
60
61 extern "C" void got_sigstop(int sig);
62 extern "C" void got_sigcontinue(int sig);
63 extern "C" void got_sigtout(int sig);
64 extern "C" void got_sigtin(int sig);
65
66
67 /* Static variables */
68 static char *configfile = NULL;
69 static BSOCK *UA_sock = NULL;
70 static DIRRES *dir = NULL;
71 static CONRES *cons = NULL;
72 static FILE *output = stdout;
73 static bool teeout = false;               /* output to output and stdout */
74 static bool stop = false;
75 static bool no_conio = false;
76 static int timeout = 0;
77 static int argc;
78 static int numdir;
79 static POOLMEM *args;
80 static char *argk[MAX_CMD_ARGS];
81 static char *argv[MAX_CMD_ARGS];
82 static CONFIG *config;
83
84
85 /* Command prototypes */
86 static int versioncmd(FILE *input, BSOCK *UA_sock);
87 static int inputcmd(FILE *input, BSOCK *UA_sock);
88 static int outputcmd(FILE *input, BSOCK *UA_sock);
89 static int teecmd(FILE *input, BSOCK *UA_sock);
90 static int quitcmd(FILE *input, BSOCK *UA_sock);
91 static int helpcmd(FILE *input, BSOCK *UA_sock);
92 static int echocmd(FILE *input, BSOCK *UA_sock);
93 static int timecmd(FILE *input, BSOCK *UA_sock);
94 static int sleepcmd(FILE *input, BSOCK *UA_sock);
95 static int execcmd(FILE *input, BSOCK *UA_sock);
96 #ifdef HAVE_READLINE
97 static int eolcmd(FILE *input, BSOCK *UA_sock);
98
99 # ifndef HAVE_REGEX_H
100 #  include "lib/bregex.h"
101 # else
102 #  include <regex.h>
103 # endif
104
105 #endif
106
107
108 #define CONFIG_FILE "bconsole.conf"   /* default configuration file */
109
110 static void usage()
111 {
112    fprintf(stderr, _(
113 PROG_COPYRIGHT
114 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
115 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
116 "       -D <dir>    select a Director\n"
117 "       -l          list Directors defined\n"
118 "       -c <file>   set configuration file to file\n"
119 "       -d <nn>     set debug level to <nn>\n"
120 "       -dt         print timestamp in debug output\n"
121 "       -n          no conio\n"
122 "       -s          no signals\n"
123 "       -u <nn>     set command execution timeout to <nn> seconds\n"
124 "       -t          test - read configuration and exit\n"
125 "       -?          print this message.\n"
126 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
127 }
128
129
130 extern "C"
131 void got_sigstop(int sig)
132 {
133    stop = true;
134 }
135
136 extern "C"
137 void got_sigcontinue(int sig)
138 {
139    stop = false;
140 }
141
142 extern "C"
143 void got_sigtout(int sig)
144 {
145 // printf("Got tout\n");
146 }
147
148 extern "C"
149 void got_sigtin(int sig)
150 {
151 // printf("Got tin\n");
152 }
153
154
155 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
156 {
157    con_set_zed_keys();
158    return 1;
159 }
160
161 /*
162  * These are the @command
163  */
164 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
165 static struct cmdstruct commands[] = {
166  { N_("input"),      inputcmd,     _("input from file")},
167  { N_("output"),     outputcmd,    _("output to file")},
168  { N_("quit"),       quitcmd,      _("quit")},
169  { N_("tee"),        teecmd,       _("output to file and terminal")},
170  { N_("sleep"),      sleepcmd,     _("sleep specified time")},
171  { N_("time"),       timecmd,      _("print current time")},
172  { N_("version"),    versioncmd,   _("print Console's version")},
173  { N_("echo"),       echocmd,      _("echo command string")},
174  { N_("exec"),       execcmd,      _("execute an external command")},
175  { N_("exit"),       quitcmd,      _("exit = quit")},
176  { N_("zed_keys"),   zed_keyscmd,  _("zed_keys = use zed keys instead of bash keys")},
177  { N_("help"),       helpcmd,      _("help listing")},
178 #ifdef HAVE_READLINE
179  { N_("separator"),  eolcmd,       _("set command separator")},
180 #endif
181              };
182 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
183
184 static int do_a_command(FILE *input, BSOCK *UA_sock)
185 {
186    unsigned int i;
187    int stat;
188    int found;
189    int len;
190    char *cmd;
191
192    found = 0;
193    stat = 1;
194
195    Dmsg1(120, "Command: %s\n", UA_sock->msg);
196    if (argc == 0) {
197       return 1;
198    }
199
200    cmd = argk[0]+1;
201    if (*cmd == '#') {                 /* comment */
202       return 1;
203    }
204    len = strlen(cmd);
205    for (i=0; i<comsize; i++) {     /* search for command */
206       if (strncasecmp(cmd,  _(commands[i].key), len) == 0) {
207          stat = (*commands[i].func)(input, UA_sock);   /* go execute command */
208          found = 1;
209          break;
210       }
211    }
212    if (!found) {
213       pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
214       UA_sock->msglen = strlen(UA_sock->msg);
215       sendit(UA_sock->msg);
216    }
217    return stat;
218 }
219
220
221 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
222 {
223    const char *prompt = "*";
224    bool at_prompt = false;
225    int tty_input = isatty(fileno(input));
226    int stat;
227    btimer_t *tid=NULL;
228
229    for ( ;; ) {
230       if (at_prompt) {                /* don't prompt multiple times */
231          prompt = "";
232       } else {
233          prompt = "*";
234          at_prompt = true;
235       }
236       if (tty_input) {
237          stat = get_cmd(input, prompt, UA_sock, 30);
238          if (usrbrk() == 1) {
239             clrbrk();
240          }
241          if (usrbrk()) {
242             break;
243          }
244       } else {
245          /* Reading input from a file */
246          int len = sizeof_pool_memory(UA_sock->msg) - 1;
247          if (usrbrk()) {
248             break;
249          }
250          if (fgets(UA_sock->msg, len, input) == NULL) {
251             stat = -1;
252          } else {
253             sendit(UA_sock->msg);  /* echo to terminal */
254             strip_trailing_junk(UA_sock->msg);
255             UA_sock->msglen = strlen(UA_sock->msg);
256             stat = 1;
257          }
258       }
259       if (stat < 0) {
260          break;                       /* error or interrupt */
261       } else if (stat == 0) {         /* timeout */
262          if (strcmp(prompt, "*") == 0) {
263             tid = start_bsock_timer(UA_sock, timeout);
264             UA_sock->fsend(".messages");
265             stop_bsock_timer(tid);
266          } else {
267             continue;
268          }
269       } else {
270          at_prompt = false;
271          /* @ => internal command for us */
272          if (UA_sock->msg[0] == '@') {
273             parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
274             if (!do_a_command(input, UA_sock)) {
275                break;
276             }
277             continue;
278          }
279          tid = start_bsock_timer(UA_sock, timeout);
280          if (!UA_sock->send()) {   /* send command */
281             stop_bsock_timer(tid);
282             break;                    /* error */
283          }
284          stop_bsock_timer(tid);
285       }
286       if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
287          break;
288       }
289       tid = start_bsock_timer(UA_sock, timeout);
290       while ((stat = UA_sock->recv()) >= 0) {
291          if (at_prompt) {
292             if (!stop) {
293                sendit("\n");
294             }
295             at_prompt = false;
296          }
297          /* Suppress output if running in background or user hit ctl-c */
298          if (!stop && !usrbrk()) {
299             sendit(UA_sock->msg);
300          }
301       }
302       stop_bsock_timer(tid);
303       if (usrbrk() > 1) {
304          break;
305       } else {
306          clrbrk();
307       }
308       if (!stop) {
309          fflush(stdout);
310       }
311       if (UA_sock->is_stop()) {
312          break;                       /* error or term */
313       } else if (stat == BNET_SIGNAL) {
314          if (UA_sock->msglen == BNET_SUB_PROMPT) {
315             at_prompt = true;
316          }
317          Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
318       }
319    }
320 }
321
322 /*
323  * Call-back for reading a passphrase for an encrypted PEM file
324  * This function uses getpass(),
325  *  which uses a static buffer and is NOT thread-safe.
326  */
327 static int tls_pem_callback(char *buf, int size, const void *userdata)
328 {
329 #ifdef HAVE_TLS
330    const char *prompt = (const char *)userdata;
331 # if defined(HAVE_WIN32)
332    sendit(prompt);
333    if (win32_cgets(buf, size) == NULL) {
334       buf[0] = 0;
335       return 0;
336    } else {
337       return strlen(buf);
338    }
339 # else
340    char *passwd;
341
342    passwd = getpass(prompt);
343    bstrncpy(buf, passwd, size);
344    return strlen(buf);
345 # endif
346 #else
347    buf[0] = 0;
348    return 0;
349 #endif
350 }
351
352 #ifdef HAVE_READLINE
353 #define READLINE_LIBRARY 1
354 #include "readline.h"
355 #include "history.h"
356
357 /* Get the first keyword of the line */
358 static char *
359 get_first_keyword()
360 {
361    char *ret=NULL;
362    int len;
363    char *first_space = strchr(rl_line_buffer, ' ');
364    if (first_space) {
365       len = first_space - rl_line_buffer;
366       ret = (char *) malloc((len + 1) * sizeof(char));
367       memcpy(ret, rl_line_buffer, len);
368       ret[len]=0;
369    }
370    return ret;
371 }
372
373 /*
374  * Return the command before the current point.
375  * Set nb to the number of command to skip
376  */
377 static char *
378 get_previous_keyword(int current_point, int nb)
379 {
380    int i, end=-1, start, inquotes=0;
381    char *s=NULL;
382
383    while (nb-- >= 0) {
384       /* first we look for a space before the current word */
385       for (i = current_point; i >= 0; i--) {
386          if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
387             break;
388          }
389       }
390
391       /* find the end of the command */
392       for (; i >= 0; i--) {
393          if (rl_line_buffer[i] != ' ') {
394             end = i;
395             break;
396          }
397       }
398
399       /* no end of string */
400       if (end == -1) {
401          return NULL;
402       }
403
404       /* look for the start of the command */
405       for (start = end; start > 0; start--) {
406          if (rl_line_buffer[start] == '"') {
407             inquotes = !inquotes;
408          }
409          if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
410             break;
411          }
412          current_point = start;
413       }
414    }
415
416    s = (char *)malloc(end - start + 2);
417    memcpy(s, rl_line_buffer + start, end - start + 1);
418    s[end - start + 1] = 0;
419
420    //  printf("=======> %i:%i <%s>\n", start, end, s);
421
422    return s;
423 }
424
425 /* Simple structure that will contain the completion list */
426 struct ItemList {
427    alist list;
428 };
429
430 static ItemList *items = NULL;
431 void init_items()
432 {
433    if (!items) {
434       items = (ItemList*) malloc(sizeof(ItemList));
435       memset(items, 0, sizeof(ItemList));
436
437    } else {
438       items->list.destroy();
439    }
440
441    items->list.init();
442 }
443
444 /* Match a regexp and add the result to the items list
445  * This function is recursive
446  */
447 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
448 {
449    int rc, size;
450    int nmatch=20;
451    regmatch_t pmatch[20];
452
453    if (len <= 0) {
454       return;
455    }
456    rc = regexec(preg, what, nmatch, pmatch, 0);
457    if (rc == 0) {
458 #if 0
459       Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n        10         20         30\n", what);
460       Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo);
461       Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo);
462       Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo);
463       Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo);
464 #endif
465       size = pmatch[1].rm_eo - pmatch[1].rm_so;
466       *buf = check_pool_memory_size(*buf, size + 1);
467       memcpy(*buf, what+pmatch[1].rm_so, size);
468       (*buf)[size] = 0;
469
470       items->list.append(bstrdup(*buf));
471       /* We search for the next keyword in the line */
472       match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf);
473    }
474 }
475
476 /* fill the items list with the output of the help command */
477 void get_arguments(const char *what)
478 {
479    regex_t preg;
480    POOLMEM *buf;
481    int rc;
482    init_items();
483
484    rc = regcomp(&preg, "(([a-z]+=)|([a-z]+)( |$))", REG_EXTENDED);
485    if (rc != 0) {
486       return;
487    }
488
489    buf = get_pool_memory(PM_MESSAGE);
490    UA_sock->fsend(".help item=%s", what);
491    while (UA_sock->recv() > 0) {
492       strip_trailing_junk(UA_sock->msg);
493       match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
494    }
495    free_pool_memory(buf);
496    regfree(&preg);
497 }
498
499 /* retreive a simple list (.pool, .client) and store it into items */
500 void get_items(const char *what)
501 {
502    init_items();
503
504    UA_sock->fsend("%s", what);
505    while (UA_sock->recv() > 0) {
506       strip_trailing_junk(UA_sock->msg);
507       items->list.append(bstrdup(UA_sock->msg));
508    }
509 }
510
511 typedef enum
512 {
513    ITEM_ARG,       /* item with simple list like .jobs */
514    ITEM_HELP       /* use help item=xxx and detect all arguments */
515 } cpl_item_t;
516
517 /* Generator function for command completion.  STATE lets us know whether
518  * to start from scratch; without any state (i.e. STATE == 0), then we
519  * start at the top of the list.
520  */
521 static char *item_generator(const char *text, int state,
522                             const char *item, cpl_item_t type)
523 {
524   static int list_index, len;
525   char *name;
526
527   /* If this is a new word to complete, initialize now.  This includes
528    * saving the length of TEXT for efficiency, and initializing the index
529    *  variable to 0.
530    */
531   if (!state)
532   {
533      list_index = 0;
534      len = strlen(text);
535      switch(type) {
536      case ITEM_ARG:
537         get_items(item);
538         break;
539      case ITEM_HELP:
540         get_arguments(item);
541         break;
542      }
543   }
544
545   /* Return the next name which partially matches from the command list. */
546   while (items && list_index < items->list.size())
547   {
548      name = (char *)items->list[list_index];
549      list_index++;
550
551      if (strncmp(name, text, len) == 0) {
552         char *ret = (char *) actuallymalloc(strlen(name)+1);
553         strcpy(ret, name);
554         return ret;
555      }
556   }
557
558   /* If no names matched, then return NULL. */
559   return ((char *)NULL);
560 }
561
562 /* gobal variables for the type and the item to search
563  * the readline API doesn' permit to pass user data.
564  */
565 static const char *cpl_item;
566 static cpl_item_t cpl_type;
567
568 static char *cpl_generator(const char *text, int state)
569 {
570    return item_generator(text, state, cpl_item, cpl_type);
571 }
572
573 /* this function is used to not use the default filename completion */
574 static char *dummy_completion_function(const char *text, int state)
575 {
576    return NULL;
577 }
578
579 struct cpl_keywords_t {
580    const char *key;
581    const char *cmd;
582 };
583
584 static struct cpl_keywords_t cpl_keywords[] = {
585    {"pool=",      ".pool"          },
586    {"fileset=",   ".fileset"       },
587    {"client=",    ".client"        },
588    {"job=",       ".jobs"          },
589    {"restore_job=",".jobs type=R"  },
590    {"level=",     ".level"         },
591    {"storage=",   ".storage"       },
592    {"schedule=",  ".schedule"      },
593    {"volume=",    ".media"         },
594    {"oldvolume=", ".media"         },
595    {"volstatus=", ".volstatus"     },
596    {"ls",         ".ls"            },
597    {"cd",         ".lsdir"         },
598    {"mark",       ".ls"            },
599    {"m",          ".ls"            },
600    {"unmark",     ".lsmark"        },
601    {"catalog=",   ".catalogs"      },
602    {"actiononpurge=", ".actiononpurge" }
603 };
604 #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
605
606 /* Attempt to complete on the contents of TEXT.  START and END bound the
607  * region of rl_line_buffer that contains the word to complete.  TEXT is
608  * the word to complete.  We can use the entire contents of rl_line_buffer
609  * in case we want to do some simple parsing.  Return the array of matches,
610  * or NULL if there aren't any.
611  */
612 static char **readline_completion(const char *text, int start, int end)
613 {
614    bool found=false;
615    char **matches;
616    char *s, *cmd;
617    matches = (char **)NULL;
618
619    /* If this word is at the start of the line, then it is a command
620     * to complete.  Otherwise it is the name of a file in the current
621     * directory.
622     */
623    s = get_previous_keyword(start, 0);
624    cmd = get_first_keyword();
625    if (s) {
626       for (int i=0; i < key_size; i++) {
627          if (!strcasecmp(s, cpl_keywords[i].key)) {
628             cpl_item = cpl_keywords[i].cmd;
629             cpl_type = ITEM_ARG;
630             matches = rl_completion_matches(text, cpl_generator);
631             found=true;
632             break;
633          }
634       }
635
636       if (!found) {             /* we try to get help with the first command */
637          cpl_item = cmd;
638          cpl_type = ITEM_HELP;
639          /* we don't want to append " " at the end */
640          rl_completion_suppress_append=true;
641          matches = rl_completion_matches(text, cpl_generator);
642       }
643       free(s);
644    } else {                     /* nothing on the line, display all commands */
645       cpl_item = ".help all";
646       cpl_type = ITEM_ARG;
647       matches = rl_completion_matches(text, cpl_generator);
648    }
649    if (cmd) {
650       free(cmd);
651    }
652    return (matches);
653 }
654
655 static char eol = '\0';
656 static int eolcmd(FILE *input, BSOCK *UA_sock)
657 {
658    if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
659       eol = argk[1][0];
660    } else if (argc == 1) {
661       eol = '\0';
662    } else {
663       sendit(_("Illegal separator character.\n"));
664    }
665    return 1;
666 }
667
668 /*
669  * Return 1 if OK
670  *        0 if no input
671  *       -1 error (must stop)
672  */
673 int
674 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
675 {
676    static char *line = NULL;
677    static char *next = NULL;
678    static int do_history = 0;
679    char *command;
680
681    if (line == NULL) {
682       do_history = 0;
683       rl_catch_signals = 0;              /* do it ourselves */
684       /* Here, readline does ***real*** malloc
685        * so, be we have to use the real free
686        */
687       line = readline((char *)prompt);   /* cast needed for old readlines */
688       if (!line) {
689          return -1;                      /* error return and exit */
690       }
691       strip_trailing_junk(line);
692       command = line;
693    } else if (next) {
694       command = next + 1;
695    } else {
696      sendit(_("Command logic problem\n"));
697      sock->msglen = 0;
698      sock->msg[0] = 0;
699      return 0;                  /* No input */
700    }
701
702    /*
703     * Split "line" into multiple commands separated by the eol character.
704     *   Each part is pointed to by "next" until finally it becomes null.
705     */
706    if (eol == '\0') {
707       next = NULL;
708    } else {
709       next = strchr(command, eol);
710       if (next) {
711          *next = '\0';
712       }
713    }
714    if (command != line && isatty(fileno(input))) {
715       senditf("%s%s\n", prompt, command);
716    }
717
718    sock->msglen = pm_strcpy(&sock->msg, command);
719    if (sock->msglen) {
720       do_history++;
721    }
722
723    if (!next) {
724       if (do_history) {
725         add_history(line);
726       }
727       actuallyfree(line);       /* allocated by readline() malloc */
728       line = NULL;
729    }
730    return 1;                    /* OK */
731 }
732
733 #else /* no readline, do it ourselves */
734
735 #ifdef HAVE_CONIO
736 static bool bisatty(int fd)
737 {
738    if (no_conio) {
739       return false;
740    }
741    return isatty(fd);
742 }
743 #endif
744
745 /*
746  *   Returns: 1 if data available
747  *            0 if timeout
748  *           -1 if error
749  */
750 static int
751 wait_for_data(int fd, int sec)
752 {
753 #if defined(HAVE_WIN32)
754    return 1;
755 #else
756    fd_set fdset;
757    struct timeval tv;
758
759    tv.tv_sec = sec;
760    tv.tv_usec = 0;
761    for ( ;; ) {
762       FD_ZERO(&fdset);
763       FD_SET((unsigned)fd, &fdset);
764       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
765       case 0:                         /* timeout */
766          return 0;
767       case -1:
768          if (errno == EINTR || errno == EAGAIN) {
769             continue;
770          }
771          return -1;                  /* error return */
772       default:
773          return 1;
774       }
775    }
776 #endif
777 }
778
779 /*
780  * Get next input command from terminal.
781  *
782  *   Returns: 1 if got input
783  *            0 if timeout
784  *           -1 if EOF or error
785  */
786 int
787 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
788 {
789    int len;
790    if (!stop) {
791       if (output == stdout || teeout) {
792          sendit(prompt);
793       }
794    }
795 again:
796    switch (wait_for_data(fileno(input), sec)) {
797    case 0:
798       return 0;                    /* timeout */
799    case -1:
800       return -1;                   /* error */
801    default:
802       len = sizeof_pool_memory(sock->msg) - 1;
803       if (stop) {
804          sleep(1);
805          goto again;
806       }
807 #ifdef HAVE_CONIO
808       if (bisatty(fileno(input))) {
809          input_line(sock->msg, len);
810          break;
811       }
812 #endif
813 #ifdef HAVE_WIN32 /* use special console for input on win32 */
814       if (input == stdin) {
815          if (win32_cgets(sock->msg, len) == NULL) {
816             return -1;
817          }
818       }
819       else
820 #endif
821       if (bfgets(sock->msg, input) == NULL) {
822          return -1;
823
824       }
825       break;
826    }
827    if (usrbrk()) {
828       clrbrk();
829    }
830    strip_trailing_junk(sock->msg);
831    sock->msglen = strlen(sock->msg);
832    return 1;
833 }
834
835 #endif /* ! HAVE_READLINE */
836
837
838 static int console_update_history(const char *histfile)
839 {
840    int ret=0;
841
842 #ifdef HAVE_READLINE
843 /*
844  * first, try to truncate the history file, and if it
845  * fails, the file is probably not present, and we
846  * can use write_history to create it
847  */
848
849    if (history_truncate_file(histfile, 100) == 0) {
850       ret = append_history(history_length, histfile);
851    } else {
852       ret = write_history(histfile);
853    }
854 #endif
855
856    return ret;
857 }
858
859 static int console_init_history(const char *histfile)
860 {
861    int ret=0;
862
863 #ifdef HAVE_READLINE
864    using_history();
865    ret = read_history(histfile);
866    /* Tell the completer that we want a complete . */
867    rl_completion_entry_function = dummy_completion_function;
868    rl_attempted_completion_function = readline_completion;
869    rl_filename_completion_desired = 0;
870    stifle_history(100);
871 #endif
872
873    return ret;
874 }
875
876 bool select_director(const char *director, DIRRES **ret_dir, CONRES **ret_cons)
877 {
878    int numcon=0, numdir=0;
879    int i=0, item=0;
880    BSOCK *UA_sock;
881    DIRRES *dir = NULL;
882    CONRES *cons = NULL;
883
884    *ret_cons = NULL;
885    *ret_dir = NULL;
886
887    LockRes();
888    numdir = 0;
889    foreach_res(dir, R_DIRECTOR) {
890       numdir++;
891    }
892    numcon = 0;
893    foreach_res(cons, R_CONSOLE) {
894       numcon++;
895    }
896    UnlockRes();
897
898    if (numdir == 1) {           /* No choose */
899       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
900    }
901
902    if (director) {    /* Command line choice overwrite the no choose option */
903       LockRes();
904       foreach_res(dir, R_DIRECTOR) {
905          if (bstrcasecmp(dir->hdr.name, director)) {
906             break;
907          }
908       }
909       UnlockRes();
910       if (!dir) {               /* Can't find Director used as argument */
911          senditf(_("Can't find %s in Director list\n"), director);
912          return 0;
913       }
914    }
915
916    if (dir == NULL) {               /* prompt for director */
917       UA_sock = new_bsock();
918 try_again:
919       sendit(_("Available Directors:\n"));
920       LockRes();
921       numdir = 0;
922       foreach_res(dir, R_DIRECTOR) {
923          senditf( _("%2d:  %s at %s:%d\n"), 1+numdir++, dir->hdr.name,
924                   dir->address, dir->DIRport);
925       }
926       UnlockRes();
927       if (get_cmd(stdin, _("Select Director by entering a number: "),
928                   UA_sock, 600) < 0)
929       {
930          (void)WSACleanup();               /* Cleanup Windows sockets */
931          return 0;
932       }
933       if (!is_a_number(UA_sock->msg)) {
934          senditf(_("%s is not a number. You must enter a number between "
935                    "1 and %d\n"),
936                  UA_sock->msg, numdir);
937          goto try_again;
938       }
939       item = atoi(UA_sock->msg);
940       if (item < 0 || item > numdir) {
941          senditf(_("You must enter a number between 1 and %d\n"), numdir);
942          goto try_again;
943       }
944       free_bsock(UA_sock);
945       LockRes();
946       for (i=0; i<item; i++) {
947          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
948       }
949       UnlockRes();
950    }
951    LockRes();
952    /* Look for a console linked to this director */
953    for (i=0; i<numcon; i++) {
954       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
955       if (cons->director && strcasecmp(cons->director, dir->hdr.name) == 0) {
956          break;
957       }
958       if (i == (numcon - 1)) {
959          cons = NULL;
960       }
961    }
962
963    /* Look for the first non-linked console */
964    if (cons == NULL) {
965       for (i=0; i<numcon; i++) {
966          cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
967          if (cons->director == NULL) {
968             break;
969          }
970          if (i == (numcon - 1)) {
971             cons = NULL;
972          }
973       }
974    }
975
976    /* If no console, take first one */
977    if (!cons) {
978       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
979    }
980    UnlockRes();
981
982    *ret_dir = dir;
983    *ret_cons = cons;
984
985    return 1;
986 }
987
988 /*********************************************************************
989  *
990  *         Main Bacula Console -- User Interface Program
991  *
992  */
993 int main(int argc, char *argv[])
994 {
995    int ch;
996    char *director = NULL;
997    bool list_directors=false;
998    bool no_signals = false;
999    bool test_config = false;
1000    JCR jcr;
1001    utime_t heart_beat;
1002
1003    setlocale(LC_ALL, "");
1004    bindtextdomain("bacula", LOCALEDIR);
1005    textdomain("bacula");
1006
1007    init_stack_dump();
1008    lmgr_init_thread();
1009    my_name_is(argc, argv, "bconsole");
1010    init_msg(NULL, NULL);
1011    working_directory = "/tmp";
1012    args = get_pool_memory(PM_FNAME);
1013
1014    while ((ch = getopt(argc, argv, "D:lbc:d:nstu:?")) != -1) {
1015       switch (ch) {
1016       case 'D':                    /* Director */
1017          if (director) {
1018             free(director);
1019          }
1020          director = bstrdup(optarg);
1021          break;
1022
1023       case 'l':
1024          list_directors = true;
1025          test_config = true;
1026          break;
1027
1028       case 'c':                    /* configuration file */
1029          if (configfile != NULL) {
1030             free(configfile);
1031          }
1032          configfile = bstrdup(optarg);
1033          break;
1034
1035       case 'd':
1036          if (*optarg == 't') {
1037             dbg_timestamp = true;
1038          } else {
1039             debug_level = atoi(optarg);
1040             if (debug_level <= 0) {
1041                debug_level = 1;
1042             }
1043          }
1044          break;
1045
1046       case 'n':                    /* no conio */
1047          no_conio = true;
1048          break;
1049
1050       case 's':                    /* turn off signals */
1051          no_signals = true;
1052          break;
1053
1054       case 't':
1055          test_config = true;
1056          break;
1057
1058       case 'u':
1059          timeout = atoi(optarg);
1060          break;
1061
1062       case '?':
1063       default:
1064          usage();
1065          exit(1);
1066       }
1067    }
1068    argc -= optind;
1069    argv += optind;
1070
1071    if (!no_signals) {
1072       init_signals(terminate_console);
1073    }
1074
1075
1076 #if !defined(HAVE_WIN32)
1077    /* Override Bacula default signals */
1078    signal(SIGQUIT, SIG_IGN);
1079    signal(SIGTSTP, got_sigstop);
1080    signal(SIGCONT, got_sigcontinue);
1081    signal(SIGTTIN, got_sigtin);
1082    signal(SIGTTOU, got_sigtout);
1083    trapctlc();
1084 #endif
1085
1086    OSDependentInit();
1087
1088    if (argc) {
1089       usage();
1090       exit(1);
1091    }
1092
1093    if (configfile == NULL) {
1094       configfile = bstrdup(CONFIG_FILE);
1095    }
1096
1097    config = new_config_parser();
1098    parse_cons_config(config, configfile, M_ERROR_TERM);
1099
1100    if (init_crypto() != 0) {
1101       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
1102    }
1103
1104    if (!check_resources()) {
1105       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
1106    }
1107
1108    if (!no_conio) {
1109       con_init(stdin);
1110    }
1111
1112    if (list_directors) {
1113       LockRes();
1114       foreach_res(dir, R_DIRECTOR) {
1115          senditf("%s\n", dir->hdr.name);
1116       }
1117       UnlockRes();
1118    }
1119
1120    if (test_config) {
1121       terminate_console(0);
1122       exit(0);
1123    }
1124
1125    memset(&jcr, 0, sizeof(jcr));
1126
1127    (void)WSA_Init();                        /* Initialize Windows sockets */
1128
1129    start_watchdog();                        /* Start socket watchdog */
1130
1131    if (!select_director(director, &dir, &cons)) {
1132       terminate_console(0);
1133       return 1;
1134    }
1135
1136    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1137
1138    char buf[1024];
1139    /* Initialize Console TLS context */
1140    if (cons && (cons->tls_enable || cons->tls_require)) {
1141       /* Generate passphrase prompt */
1142       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1143
1144       /* Initialize TLS context:
1145        * Args: CA certfile, CA certdir, Certfile, Keyfile,
1146        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1147        */
1148       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1149          cons->tls_ca_certdir, cons->tls_certfile,
1150          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1151
1152       if (!cons->tls_ctx) {
1153          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1154             dir->hdr.name);
1155          terminate_console(0);
1156          return 1;
1157       }
1158    }
1159
1160    /* Initialize Director TLS context */
1161    if (dir->tls_enable || dir->tls_require) {
1162       /* Generate passphrase prompt */
1163       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1164
1165       /* Initialize TLS context:
1166        * Args: CA certfile, CA certdir, Certfile, Keyfile,
1167        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1168       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1169          dir->tls_ca_certdir, dir->tls_certfile,
1170          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1171
1172       if (!dir->tls_ctx) {
1173          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1174             dir->hdr.name);
1175          terminate_console(0);
1176          return 1;
1177       }
1178    }
1179
1180    if (dir->heartbeat_interval) {
1181       heart_beat = dir->heartbeat_interval;
1182    } else if (cons) {
1183       heart_beat = cons->heartbeat_interval;
1184    } else {
1185       heart_beat = 0;
1186    }
1187    if (!UA_sock) {
1188       UA_sock = new_bsock();
1189    }
1190    if (!UA_sock->connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1191                           NULL, dir->DIRport, 0)) {
1192       terminate_console(0);
1193       return 1;
1194    }
1195    jcr.dir_bsock = UA_sock;
1196
1197    /* If cons==NULL, default console will be used */
1198    if (!authenticate_director(&jcr, dir, cons)) {
1199       terminate_console(0);
1200       return 1;
1201    }
1202
1203    Dmsg0(40, "Opened connection with Director daemon\n");
1204
1205    sendit(_("Enter a period to cancel a command.\n"));
1206
1207    /* Read/Update history file if HOME exists */
1208    POOL_MEM history_file;
1209
1210    /* Run commands in ~/.bconsolerc if any */
1211    char *env = getenv("HOME");
1212    if (env) {
1213       FILE *fd;
1214       pm_strcpy(&UA_sock->msg, env);
1215       pm_strcat(&UA_sock->msg, "/.bconsolerc");
1216       fd = fopen(UA_sock->msg, "rb");
1217       if (fd) {
1218          read_and_process_input(fd, UA_sock);
1219          fclose(fd);
1220       }
1221
1222       pm_strcpy(history_file, env);
1223       pm_strcat(history_file, "/.bconsole_history");
1224       console_init_history(history_file.c_str());
1225    }
1226
1227    read_and_process_input(stdin, UA_sock);
1228
1229    if (UA_sock) {
1230       UA_sock->signal(BNET_TERMINATE); /* send EOF */
1231       UA_sock->close();
1232    }
1233
1234    if (env) {
1235       console_update_history(history_file.c_str());
1236    }
1237
1238    terminate_console(0);
1239    return 0;
1240 }
1241
1242 /* Cleanup and then exit */
1243 static void terminate_console(int sig)
1244 {
1245
1246    static bool already_here = false;
1247
1248    if (already_here) {                /* avoid recursive temination problems */
1249       exit(1);
1250    }
1251    already_here = true;
1252    stop_watchdog();
1253    config->free_resources();
1254    free(config);
1255    config = NULL;
1256    cleanup_crypto();
1257    free_pool_memory(args);
1258    if (!no_conio) {
1259       con_term();
1260    }
1261    (void)WSACleanup();               /* Cleanup Windows sockets */
1262    lmgr_cleanup_main();
1263
1264    if (sig != 0) {
1265       exit(1);
1266    }
1267    return;
1268 }
1269
1270 /*
1271  * Make a quick check to see that we have all the
1272  * resources needed.
1273  */
1274 static int check_resources()
1275 {
1276    bool OK = true;
1277    DIRRES *director;
1278    bool tls_needed;
1279
1280    LockRes();
1281
1282    numdir = 0;
1283    foreach_res(director, R_DIRECTOR) {
1284
1285       numdir++;
1286       /* tls_require implies tls_enable */
1287       if (director->tls_require) {
1288          if (have_tls) {
1289             director->tls_enable = true;
1290          } else {
1291             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1292             OK = false;
1293             continue;
1294          }
1295       }
1296       tls_needed = director->tls_enable || director->tls_authenticate;
1297
1298       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1299          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1300                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1301                              " At least one CA certificate store is required.\n"),
1302                              director->hdr.name, configfile);
1303          OK = false;
1304       }
1305    }
1306
1307    if (numdir == 0) {
1308       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1309                           "Without that I don't how to speak to the Director :-(\n"), configfile);
1310       OK = false;
1311    }
1312
1313    CONRES *cons;
1314    /* Loop over Consoles */
1315    foreach_res(cons, R_CONSOLE) {
1316       /* tls_require implies tls_enable */
1317       if (cons->tls_require) {
1318          if (have_tls) {
1319             cons->tls_enable = true;
1320          } else {
1321             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1322             OK = false;
1323             continue;
1324          }
1325       }
1326       tls_needed = cons->tls_enable || cons->tls_authenticate;
1327       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1328          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1329                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1330                              cons->hdr.name, configfile);
1331          OK = false;
1332       }
1333    }
1334
1335    UnlockRes();
1336
1337    return OK;
1338 }
1339
1340 /* @version */
1341 static int versioncmd(FILE *input, BSOCK *UA_sock)
1342 {
1343    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1344       HOST_OS, DISTNAME, DISTVER);
1345    return 1;
1346 }
1347
1348 /* @input <input-filename> */
1349 static int inputcmd(FILE *input, BSOCK *UA_sock)
1350 {
1351    FILE *fd;
1352
1353    if (argc > 2) {
1354       sendit(_("Too many arguments on input command.\n"));
1355       return 1;
1356    }
1357    if (argc == 1) {
1358       sendit(_("First argument to input command must be a filename.\n"));
1359       return 1;
1360    }
1361    fd = fopen(argk[1], "rb");
1362    if (!fd) {
1363       berrno be;
1364       senditf(_("Cannot open file %s for input. ERR=%s\n"),
1365          argk[1], be.bstrerror());
1366       return 1;
1367    }
1368    read_and_process_input(fd, UA_sock);
1369    fclose(fd);
1370    return 1;
1371 }
1372
1373 /* @tee <output-filename> */
1374 /* Send output to both terminal and specified file */
1375 static int teecmd(FILE *input, BSOCK *UA_sock)
1376 {
1377    teeout = true;
1378    return do_outputcmd(input, UA_sock);
1379 }
1380
1381 /* @output <output-filename> */
1382 /* Send output to specified "file" */
1383 static int outputcmd(FILE *input, BSOCK *UA_sock)
1384 {
1385    teeout = false;
1386    return do_outputcmd(input, UA_sock);
1387 }
1388
1389
1390 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1391 {
1392    FILE *fd;
1393    const char *mode = "a+b";
1394
1395    if (argc > 3) {
1396       sendit(_("Too many arguments on output/tee command.\n"));
1397       return 1;
1398    }
1399    if (argc == 1) {
1400       if (output != stdout) {
1401          fclose(output);
1402          output = stdout;
1403          teeout = false;
1404       }
1405       return 1;
1406    }
1407    if (argc == 3) {
1408       mode = argk[2];
1409    }
1410    fd = fopen(argk[1], mode);
1411    if (!fd) {
1412       berrno be;
1413       senditf(_("Cannot open file %s for output. ERR=%s\n"),
1414          argk[1], be.bstrerror(errno));
1415       return 1;
1416    }
1417    output = fd;
1418    return 1;
1419 }
1420
1421 /*
1422  * @exec "some-command" [wait-seconds]
1423 */
1424 static int execcmd(FILE *input, BSOCK *UA_sock)
1425 {
1426    BPIPE *bpipe;
1427    char line[5000];
1428    int stat;
1429    int wait = 0;
1430
1431    if (argc > 3) {
1432       sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1433       return 1;
1434    }
1435    if (argc == 3) {
1436       wait = atoi(argk[2]);
1437    }
1438    bpipe = open_bpipe(argk[1], wait, "r");
1439    if (!bpipe) {
1440       berrno be;
1441       senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1442          argk[1], be.bstrerror(errno));
1443       return 1;
1444    }
1445
1446    while (fgets(line, sizeof(line), bpipe->rfd)) {
1447       senditf("%s", line);
1448    }
1449    stat = close_bpipe(bpipe);
1450    if (stat != 0) {
1451       berrno be;
1452       be.set_errno(stat);
1453      senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1454    }
1455    return 1;
1456 }
1457
1458
1459 /* @echo xxx yyy */
1460 static int echocmd(FILE *input, BSOCK *UA_sock)
1461 {
1462    for (int i=1; i < argc; i++) {
1463       senditf("%s ", argk[i]);
1464    }
1465    sendit("\n");
1466    return 1;
1467 }
1468
1469 /* @quit */
1470 static int quitcmd(FILE *input, BSOCK *UA_sock)
1471 {
1472    return 0;
1473 }
1474
1475 /* @help */
1476 static int helpcmd(FILE *input, BSOCK *UA_sock)
1477 {
1478    int i;
1479    for (i=0; i<comsize; i++) {
1480       senditf("  %-10s %s\n", commands[i].key, commands[i].help);
1481    }
1482    return 1;
1483 }
1484
1485
1486 /* @sleep secs */
1487 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1488 {
1489    if (argc > 1) {
1490       sleep(atoi(argk[1]));
1491    }
1492    return 1;
1493 }
1494
1495 /* @time */
1496 static int timecmd(FILE *input, BSOCK *UA_sock)
1497 {
1498    char sdt[50];
1499    time_t ttime = time(NULL);
1500    struct tm tm;
1501    (void)localtime_r(&ttime, &tm);
1502    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1503    sendit("\n");
1504    return 1;
1505 }
1506
1507 /*
1508  * Send a line to the output file and or the terminal
1509  */
1510 void senditf(const char *fmt,...)
1511 {
1512    char buf[3000];
1513    va_list arg_ptr;
1514
1515    va_start(arg_ptr, fmt);
1516    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1517    va_end(arg_ptr);
1518    sendit(buf);
1519 }
1520
1521 void sendit(const char *buf)
1522 {
1523 #ifdef CONIO_FIX
1524    char obuf[3000];
1525    if (output == stdout || teeout) {
1526       const char *p, *q;
1527       /*
1528        * Here, we convert every \n into \r\n because the
1529        *  terminal is in raw mode when we are using
1530        *  conio.
1531        */
1532       for (p=q=buf; (p=strchr(q, '\n')); ) {
1533          int len = p - q;
1534          if (len > 0) {
1535             memcpy(obuf, q, len);
1536          }
1537          memcpy(obuf+len, "\r\n", 3);
1538          q = ++p;                    /* point after \n */
1539          fputs(obuf, output);
1540       }
1541       if (*q) {
1542          fputs(q, output);
1543       }
1544       fflush(output);
1545    }
1546    if (output != stdout) {
1547       fputs(buf, output);
1548    }
1549 #else
1550
1551    fputs(buf, output);
1552    fflush(output);
1553    if (teeout) {
1554       fputs(buf, stdout);
1555       fflush(stdout);
1556    }
1557 #endif
1558 }