]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
c70e2a3558e86bffa2432f774a9c260a61bf34cf
[bacula/bacula] / bacula / src / console / console.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 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 two of the GNU 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 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  *     Version $Id$
35  */
36
37 #include "bacula.h"
38 #include "console_conf.h"
39 #include "jcr.h"
40
41
42 #ifdef HAVE_CONIO
43 #include "conio.h"
44 //#define CONIO_FIX 1
45 #else
46 #define con_init(x)
47 #define con_term()
48 #define con_set_zed_keys();
49 #define trapctlc()
50 #define clrbrk()
51 #define usrbrk() 0
52 #endif
53
54 #if defined(HAVE_WIN32)
55 #define isatty(fd) (fd==0)
56 #endif
57
58 /* Exported variables */
59
60 //extern int rl_catch_signals;
61
62 /* Imported functions */
63 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
64 extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
65
66 /* Forward referenced functions */
67 static void terminate_console(int sig);
68 static int check_resources();
69 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
70 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
71 void senditf(const char *fmt, ...);
72 void sendit(const char *buf);
73
74 extern "C" void got_sigstop(int sig);
75 extern "C" void got_sigcontinue(int sig);
76 extern "C" void got_sigtout(int sig);
77 extern "C" void got_sigtin(int sig);
78
79
80 /* Static variables */
81 static char *configfile = NULL;
82 static BSOCK *UA_sock = NULL;
83 static DIRRES *dir = NULL;
84 static CONRES *cons = NULL;
85 static FILE *output = stdout;
86 static bool teeout = false;               /* output to output and stdout */
87 static bool stop = false;
88 static bool no_conio = false;
89 static int timeout = 0;
90 static int argc;
91 static int numdir;
92 static int numcon;
93 static POOLMEM *args;
94 static char *argk[MAX_CMD_ARGS];
95 static char *argv[MAX_CMD_ARGS];
96 static CONFIG *config;
97
98
99 /* Command prototypes */
100 static int versioncmd(FILE *input, BSOCK *UA_sock);
101 static int inputcmd(FILE *input, BSOCK *UA_sock);
102 static int outputcmd(FILE *input, BSOCK *UA_sock);
103 static int teecmd(FILE *input, BSOCK *UA_sock);
104 static int quitcmd(FILE *input, BSOCK *UA_sock);
105 static int helpcmd(FILE *input, BSOCK *UA_sock);
106 static int echocmd(FILE *input, BSOCK *UA_sock);
107 static int timecmd(FILE *input, BSOCK *UA_sock);
108 static int sleepcmd(FILE *input, BSOCK *UA_sock);
109 static int execcmd(FILE *input, BSOCK *UA_sock);
110 #ifdef HAVE_READLINE
111 static int eolcmd(FILE *input, BSOCK *UA_sock);
112
113 # ifndef HAVE_REGEX_H
114 #  include "lib/bregex.h"
115 # else
116 #  include <regex.h>
117 # endif
118
119 #endif
120
121
122 #define CONFIG_FILE "bconsole.conf"   /* default configuration file */
123
124 static void usage()
125 {
126    fprintf(stderr, _(
127 PROG_COPYRIGHT
128 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
129 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\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             bnet_fsend(UA_sock, ".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 (!bnet_send(UA_sock)) {   /* 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 = bnet_recv(UA_sock)) >= 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_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    {"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 #endif
876
877    return ret;
878 }
879
880 /*********************************************************************
881  *
882  *         Main Bacula Console -- User Interface Program
883  *
884  */
885 int main(int argc, char *argv[])
886 {
887    int ch, i, item;
888    bool no_signals = false;
889    bool test_config = false;
890    JCR jcr;
891    utime_t heart_beat;
892
893    setlocale(LC_ALL, "");
894    bindtextdomain("bacula", LOCALEDIR);
895    textdomain("bacula");
896
897    init_stack_dump();
898    lmgr_init_thread();
899    my_name_is(argc, argv, "bconsole");
900    init_msg(NULL, NULL);
901    working_directory = "/tmp";
902    args = get_pool_memory(PM_FNAME);
903
904    while ((ch = getopt(argc, argv, "bc:d:nstu:?")) != -1) {
905       switch (ch) {
906       case 'c':                    /* configuration file */
907          if (configfile != NULL) {
908             free(configfile);
909          }
910          configfile = bstrdup(optarg);
911          break;
912
913       case 'd':
914          if (*optarg == 't') {
915             dbg_timestamp = true;
916          } else {
917             debug_level = atoi(optarg);
918             if (debug_level <= 0) {
919                debug_level = 1;
920             }
921          }
922          break;
923
924       case 'n':                    /* no conio */
925          no_conio = true;
926          break;
927
928       case 's':                    /* turn off signals */
929          no_signals = true;
930          break;
931
932       case 't':
933          test_config = true;
934          break;
935
936       case 'u':
937          timeout = atoi(optarg);
938          break;
939
940       case '?':
941       default:
942          usage();
943          exit(1);
944       }
945    }
946    argc -= optind;
947    argv += optind;
948
949    if (!no_signals) {
950       init_signals(terminate_console);
951    }
952
953
954 #if !defined(HAVE_WIN32)
955    /* Override Bacula default signals */
956    signal(SIGQUIT, SIG_IGN);
957    signal(SIGTSTP, got_sigstop);
958    signal(SIGCONT, got_sigcontinue);
959    signal(SIGTTIN, got_sigtin);
960    signal(SIGTTOU, got_sigtout);
961    trapctlc();
962 #endif
963
964    OSDependentInit();
965
966    if (argc) {
967       usage();
968       exit(1);
969    }
970
971    if (configfile == NULL) {
972       configfile = bstrdup(CONFIG_FILE);
973    }
974
975    config = new_config_parser();
976    parse_cons_config(config, configfile, M_ERROR_TERM);
977
978    if (init_crypto() != 0) {
979       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
980    }
981
982    if (!check_resources()) {
983       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
984    }
985
986    if (!no_conio) {
987       con_init(stdin);
988    }
989
990    if (test_config) {
991       terminate_console(0);
992       exit(0);
993    }
994
995    memset(&jcr, 0, sizeof(jcr));
996
997    (void)WSA_Init();                        /* Initialize Windows sockets */
998
999    start_watchdog();                        /* Start socket watchdog */
1000
1001    LockRes();
1002    numdir = 0;
1003    foreach_res(dir, R_DIRECTOR) {
1004       numdir++;
1005    }
1006    numcon = 0;
1007    foreach_res(cons, R_CONSOLE) {
1008       numcon++;
1009    }
1010    UnlockRes();
1011
1012    if (numdir > 1) {
1013       struct sockaddr client_addr;
1014       memset(&client_addr, 0, sizeof(client_addr));
1015       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
1016 try_again:
1017       sendit(_("Available Directors:\n"));
1018       LockRes();
1019       numdir = 0;
1020       foreach_res(dir, R_DIRECTOR) {
1021          senditf( _("%2d:  %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
1022             dir->DIRport);
1023       }
1024       UnlockRes();
1025       if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
1026          (void)WSACleanup();               /* Cleanup Windows sockets */
1027          return 1;
1028       }
1029       if (!is_a_number(UA_sock->msg)) {
1030          senditf(_("%s is not a number. You must enter a number between 1 and %d\n"), 
1031                  UA_sock->msg, numdir);
1032          goto try_again;
1033       }
1034       item = atoi(UA_sock->msg);
1035       if (item < 0 || item > numdir) {
1036          senditf(_("You must enter a number between 1 and %d\n"), numdir);
1037          goto try_again;
1038       }
1039       term_bsock(UA_sock);
1040       LockRes();
1041       for (i=0; i<item; i++) {
1042          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
1043       }
1044       /* Look for a console linked to this director */
1045       for (i=0; i<numcon; i++) {
1046          cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1047          if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
1048             break;
1049          }
1050          cons = NULL;
1051       }
1052       /* Look for the first non-linked console */
1053       if (cons == NULL) {
1054          for (i=0; i<numcon; i++) {
1055             cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1056             if (cons->director == NULL)
1057                break;
1058             cons = NULL;
1059         }
1060       }
1061       UnlockRes();
1062    }
1063    /* If no director, take first one */
1064    if (!dir) {
1065       LockRes();
1066       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
1067       UnlockRes();
1068    }
1069    /* If no console, take first one */
1070    if (!cons) {
1071       LockRes();
1072       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
1073       UnlockRes();
1074    }
1075
1076    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1077
1078    char buf[1024];
1079    /* Initialize Console TLS context */
1080    if (cons && (cons->tls_enable || cons->tls_require)) {
1081       /* Generate passphrase prompt */
1082       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1083
1084       /* Initialize TLS context:
1085        * Args: CA certfile, CA certdir, Certfile, Keyfile,
1086        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer   
1087        */
1088       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1089          cons->tls_ca_certdir, cons->tls_certfile,
1090          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1091
1092       if (!cons->tls_ctx) {
1093          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1094             dir->hdr.name);
1095          terminate_console(0);
1096          return 1;
1097       }
1098    }
1099
1100    /* Initialize Director TLS context */
1101    if (dir->tls_enable || dir->tls_require) {
1102       /* Generate passphrase prompt */
1103       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1104
1105       /* Initialize TLS context:
1106        * Args: CA certfile, CA certdir, Certfile, Keyfile,
1107        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1108       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1109          dir->tls_ca_certdir, dir->tls_certfile,
1110          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1111
1112       if (!dir->tls_ctx) {
1113          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1114             dir->hdr.name);
1115          terminate_console(0);
1116          return 1;
1117       }
1118    }
1119
1120    if (dir->heartbeat_interval) {
1121       heart_beat = dir->heartbeat_interval;
1122    } else if (cons) {
1123       heart_beat = cons->heartbeat_interval;
1124    } else {
1125       heart_beat = 0;
1126    }
1127    UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1128                           NULL, dir->DIRport, 0);
1129    if (UA_sock == NULL) {
1130       terminate_console(0);
1131       return 1;
1132    }
1133    jcr.dir_bsock = UA_sock;
1134
1135    /* If cons==NULL, default console will be used */
1136    if (!authenticate_director(&jcr, dir, cons)) {
1137       terminate_console(0);
1138       return 1;
1139    }
1140
1141    Dmsg0(40, "Opened connection with Director daemon\n");
1142
1143    sendit(_("Enter a period to cancel a command.\n"));
1144
1145    /* Read/Update history file if HOME exists */
1146    POOL_MEM history_file;
1147
1148    /* Run commands in ~/.bconsolerc if any */
1149    char *env = getenv("HOME");
1150    if (env) {
1151       FILE *fd;
1152       pm_strcpy(&UA_sock->msg, env);
1153       pm_strcat(&UA_sock->msg, "/.bconsolerc");
1154       fd = fopen(UA_sock->msg, "rb");
1155       if (fd) {
1156          read_and_process_input(fd, UA_sock);
1157          fclose(fd);
1158       }
1159
1160       pm_strcpy(history_file, env);
1161       pm_strcat(history_file, "/.bconsole_history");
1162       console_init_history(history_file.c_str());
1163    }
1164
1165    read_and_process_input(stdin, UA_sock);
1166
1167    if (UA_sock) {
1168       UA_sock->signal(BNET_TERMINATE); /* send EOF */
1169       UA_sock->close();
1170    }
1171
1172    if (env) {
1173       console_update_history(history_file.c_str());
1174    }
1175
1176    terminate_console(0);
1177    return 0;
1178 }
1179
1180 /* Cleanup and then exit */
1181 static void terminate_console(int sig)
1182 {
1183
1184    static bool already_here = false;
1185
1186    if (already_here) {                /* avoid recursive temination problems */
1187       exit(1);
1188    }
1189    already_here = true;
1190    stop_watchdog();
1191    config->free_resources();
1192    free(config);
1193    config = NULL;
1194    cleanup_crypto();
1195    free_pool_memory(args);
1196    if (!no_conio) {
1197       con_term();
1198    }
1199    (void)WSACleanup();               /* Cleanup Windows sockets */
1200    lmgr_cleanup_main();
1201
1202    if (sig != 0) {
1203       exit(1);
1204    }
1205    return;
1206 }
1207
1208 /*
1209  * Make a quick check to see that we have all the
1210  * resources needed.
1211  */
1212 static int check_resources()
1213 {
1214    bool OK = true;
1215    DIRRES *director;
1216    bool tls_needed;
1217
1218    LockRes();
1219
1220    numdir = 0;
1221    foreach_res(director, R_DIRECTOR) {
1222
1223       numdir++;
1224       /* tls_require implies tls_enable */
1225       if (director->tls_require) {
1226          if (have_tls) {
1227             director->tls_enable = true;
1228          } else {
1229             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1230             OK = false;
1231             continue;
1232          }
1233       }
1234       tls_needed = director->tls_enable || director->tls_authenticate;
1235
1236       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1237          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1238                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1239                              " At least one CA certificate store is required.\n"),
1240                              director->hdr.name, configfile);
1241          OK = false;
1242       }
1243    }
1244    
1245    if (numdir == 0) {
1246       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1247                           "Without that I don't how to speak to the Director :-(\n"), configfile);
1248       OK = false;
1249    }
1250
1251    CONRES *cons;
1252    /* Loop over Consoles */
1253    foreach_res(cons, R_CONSOLE) {
1254       /* tls_require implies tls_enable */
1255       if (cons->tls_require) {
1256          if (have_tls) {
1257             cons->tls_enable = true;
1258          } else {
1259             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1260             OK = false;
1261             continue;
1262          }
1263       }
1264       tls_needed = cons->tls_enable || cons->tls_authenticate;
1265       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1266          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1267                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1268                              cons->hdr.name, configfile);
1269          OK = false;
1270       }
1271    }
1272
1273    UnlockRes();
1274
1275    return OK;
1276 }
1277
1278 static int versioncmd(FILE *input, BSOCK *UA_sock)
1279 {
1280    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1281       HOST_OS, DISTNAME, DISTVER);
1282    return 1;
1283 }
1284
1285 static int inputcmd(FILE *input, BSOCK *UA_sock)
1286 {
1287    FILE *fd;
1288
1289    if (argc > 2) {
1290       sendit(_("Too many arguments on input command.\n"));
1291       return 1;
1292    }
1293    if (argc == 1) {
1294       sendit(_("First argument to input command must be a filename.\n"));
1295       return 1;
1296    }
1297    fd = fopen(argk[1], "rb");
1298    if (!fd) {
1299       berrno be;
1300       senditf(_("Cannot open file %s for input. ERR=%s\n"),
1301          argk[1], be.bstrerror());
1302       return 1;
1303    }
1304    read_and_process_input(fd, UA_sock);
1305    fclose(fd);
1306    return 1;
1307 }
1308
1309 /* Send output to both termina and specified file */
1310 static int teecmd(FILE *input, BSOCK *UA_sock)
1311 {
1312    teeout = true;
1313    return do_outputcmd(input, UA_sock);
1314 }
1315
1316 /* Send output to specified "file" */
1317 static int outputcmd(FILE *input, BSOCK *UA_sock)
1318 {
1319    teeout = false;
1320    return do_outputcmd(input, UA_sock);
1321 }
1322
1323
1324 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1325 {
1326    FILE *fd;
1327    const char *mode = "a+b";
1328
1329    if (argc > 3) {
1330       sendit(_("Too many arguments on output/tee command.\n"));
1331       return 1;
1332    }
1333    if (argc == 1) {
1334       if (output != stdout) {
1335          fclose(output);
1336          output = stdout;
1337          teeout = false;
1338       }
1339       return 1;
1340    }
1341    if (argc == 3) {
1342       mode = argk[2];
1343    }
1344    fd = fopen(argk[1], mode);
1345    if (!fd) {
1346       berrno be;
1347       senditf(_("Cannot open file %s for output. ERR=%s\n"),
1348          argk[1], be.bstrerror(errno));
1349       return 1;
1350    }
1351    output = fd;
1352    return 1;
1353 }
1354
1355 /*
1356  * exec "some-command" [wait-seconds]
1357 */
1358 static int execcmd(FILE *input, BSOCK *UA_sock)
1359 {
1360    BPIPE *bpipe;
1361    char line[5000];
1362    int stat;
1363    int wait = 0;
1364
1365    if (argc > 3) {
1366       sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1367       return 1;
1368    }
1369    if (argc == 3) {
1370       wait = atoi(argk[2]);
1371    }
1372    bpipe = open_bpipe(argk[1], wait, "r");
1373    if (!bpipe) {
1374       berrno be;
1375       senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1376          argk[1], be.bstrerror(errno));
1377       return 1;
1378    }
1379   
1380    while (fgets(line, sizeof(line), bpipe->rfd)) {
1381       senditf("%s", line);
1382    }
1383    stat = close_bpipe(bpipe);
1384    if (stat != 0) {
1385       berrno be;
1386       be.set_errno(stat);
1387      senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1388    }
1389    return 1;
1390 }
1391
1392
1393 static int echocmd(FILE *input, BSOCK *UA_sock)
1394 {
1395    for (int i=1; i < argc; i++) {
1396       senditf("%s ", argk[i]);
1397    }
1398    sendit("\n");
1399    return 1;
1400 }
1401
1402 static int quitcmd(FILE *input, BSOCK *UA_sock)
1403 {
1404    return 0;
1405 }
1406
1407 static int helpcmd(FILE *input, BSOCK *UA_sock)
1408 {
1409    int i;
1410    for (i=0; i<comsize; i++) { 
1411       senditf("  %-10s %s\n", commands[i].key, commands[i].help);
1412    }
1413    return 1;   
1414 }
1415
1416
1417 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1418 {
1419    if (argc > 1) {
1420       sleep(atoi(argk[1]));
1421    }
1422    return 1;
1423 }
1424
1425
1426 static int timecmd(FILE *input, BSOCK *UA_sock)
1427 {
1428    char sdt[50];
1429    time_t ttime = time(NULL);
1430    struct tm tm;
1431    (void)localtime_r(&ttime, &tm);
1432    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1433    sendit("\n");
1434    return 1;
1435 }
1436
1437 /*
1438  * Send a line to the output file and or the terminal
1439  */
1440 void senditf(const char *fmt,...)
1441 {
1442    char buf[3000];
1443    va_list arg_ptr;
1444
1445    va_start(arg_ptr, fmt);
1446    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1447    va_end(arg_ptr);
1448    sendit(buf);
1449 }
1450
1451 void sendit(const char *buf)
1452 {
1453 #ifdef CONIO_FIX
1454    char obuf[3000];
1455    if (output == stdout || teeout) {
1456       const char *p, *q;
1457       /*
1458        * Here, we convert every \n into \r\n because the
1459        *  terminal is in raw mode when we are using
1460        *  conio.
1461        */
1462       for (p=q=buf; (p=strchr(q, '\n')); ) {
1463          int len = p - q;
1464          if (len > 0) {
1465             memcpy(obuf, q, len);
1466          }
1467          memcpy(obuf+len, "\r\n", 3);
1468          q = ++p;                    /* point after \n */
1469          fputs(obuf, output);
1470       }
1471       if (*q) {
1472          fputs(q, output);
1473       }
1474       fflush(output);
1475    }
1476    if (output != stdout) {
1477       fputs(buf, output);
1478    }
1479 #else
1480
1481    fputs(buf, output);
1482    fflush(output);
1483    if (teeout) {
1484       fputs(buf, stdout);
1485       fflush(stdout);
1486    }
1487 #endif
1488 }