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