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