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