]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Add restore_job= option to restore command
[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 .jobs */
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=",       ".jobs"          },
601    {"restore_job=",".jobs type=R"  },
602    {"level=",     ".level"         },
603    {"storage=",   ".storage"       },
604    {"schedule=",  ".schedule"      },
605    {"volume=",    ".media"         },
606    {"oldvolume=", ".media"         },
607    {"volstatus=", ".volstatus"     },
608    {"ls",         ".ls"            },
609    {"cd",         ".lsdir"         },
610    {"mark",       ".ls"            },
611    {"m",          ".ls"            },
612    {"unmark",     ".lsmark"        },
613    {"actiononpurge=", ".actiononpurge" }
614 };
615 #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
616
617 /* Attempt to complete on the contents of TEXT.  START and END bound the
618  * region of rl_line_buffer that contains the word to complete.  TEXT is
619  * the word to complete.  We can use the entire contents of rl_line_buffer
620  * in case we want to do some simple parsing.  Return the array of matches,
621  * or NULL if there aren't any. 
622  */
623 static char **readline_completion(const char *text, int start, int end)
624 {
625    bool found=false;
626    char **matches;
627    char *s, *cmd;
628    matches = (char **)NULL;
629
630    /* If this word is at the start of the line, then it is a command
631     * to complete.  Otherwise it is the name of a file in the current
632     * directory. 
633     */
634    s = get_previous_keyword(start, 0);
635    cmd = get_first_keyword();
636    if (s) {
637       for (int i=0; i < key_size; i++) {
638          if (!strcasecmp(s, cpl_keywords[i].key)) {
639             cpl_item = cpl_keywords[i].cmd;
640             cpl_type = ITEM_ARG;
641             matches = rl_completion_matches(text, cpl_generator);
642             found=true;
643             break;
644          }
645       }
646       
647       if (!found) {             /* we try to get help with the first command */
648          cpl_item = cmd;
649          cpl_type = ITEM_HELP;
650          /* we don't want to append " " at the end */
651          rl_completion_suppress_append=true; 
652          matches = rl_completion_matches(text, cpl_generator);
653       } 
654       free(s);
655    } else {                     /* nothing on the line, display all commands */
656       cpl_item = ".help all";
657       cpl_type = ITEM_ARG;
658       matches = rl_completion_matches(text, cpl_generator);
659    }
660    if (cmd) {
661       free(cmd);
662    }
663    return (matches);
664 }
665
666 static char eol = '\0';
667 static int eolcmd(FILE *input, BSOCK *UA_sock)
668 {
669    if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
670       eol = argk[1][0];
671    } else if (argc == 1) {
672       eol = '\0';
673    } else {
674       sendit(_("Illegal separator character.\n"));
675    }
676    return 1;
677 }
678
679 /*
680  * Return 1 if OK
681  *        0 if no input
682  *       -1 error (must stop)
683  */
684 int
685 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
686 {
687    static char *line = NULL;
688    static char *next = NULL;
689    static int do_history = 0;
690    char *command;
691
692    if (line == NULL) {
693       do_history = 0;
694       rl_catch_signals = 0;              /* do it ourselves */
695       /* Here, readline does ***real*** malloc
696        * so, be we have to use the real free
697        */
698       line = readline((char *)prompt);   /* cast needed for old readlines */
699       if (!line) {
700          return -1;                      /* error return and exit */
701       }
702       strip_trailing_junk(line);
703       command = line;
704    } else if (next) {
705       command = next + 1;
706    } else {
707      sendit(_("Command logic problem\n"));
708      sock->msglen = 0;
709      sock->msg[0] = 0;
710      return 0;                  /* No input */
711    }
712
713    /*
714     * Split "line" into multiple commands separated by the eol character.
715     *   Each part is pointed to by "next" until finally it becomes null.
716     */
717    if (eol == '\0') {
718       next = NULL;
719    } else {
720       next = strchr(command, eol);
721       if (next) {
722          *next = '\0';
723       }
724    }
725    if (command != line && isatty(fileno(input))) {
726       senditf("%s%s\n", prompt, command);
727    }
728
729    sock->msglen = pm_strcpy(&sock->msg, command);
730    if (sock->msglen) {
731       do_history++;
732    }
733
734    if (!next) {
735       if (do_history) {
736         add_history(line);
737       }
738       actuallyfree(line);       /* allocated by readline() malloc */
739       line = NULL;
740    }
741    return 1;                    /* OK */
742 }
743
744 #else /* no readline, do it ourselves */
745
746 #ifdef HAVE_CONIO
747 static bool bisatty(int fd)
748 {
749    if (no_conio) {
750       return false;
751    }
752    return isatty(fd);
753 }
754 #endif
755
756 /*
757  *   Returns: 1 if data available
758  *            0 if timeout
759  *           -1 if error
760  */
761 static int
762 wait_for_data(int fd, int sec)
763 {
764 #if defined(HAVE_WIN32)
765    return 1;
766 #else
767    fd_set fdset;
768    struct timeval tv;
769
770    tv.tv_sec = sec;
771    tv.tv_usec = 0;
772    for ( ;; ) {
773       FD_ZERO(&fdset);
774       FD_SET((unsigned)fd, &fdset);
775       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
776       case 0:                         /* timeout */
777          return 0;
778       case -1:
779          if (errno == EINTR || errno == EAGAIN) {
780             continue;
781          }
782          return -1;                  /* error return */
783       default:
784          return 1;
785       }
786    }
787 #endif
788 }
789
790 /*
791  * Get next input command from terminal.
792  *
793  *   Returns: 1 if got input
794  *            0 if timeout
795  *           -1 if EOF or error
796  */
797 int
798 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
799 {
800    int len;
801    if (!stop) {
802       if (output == stdout || teeout) {
803          sendit(prompt);
804       }
805    }
806 again:
807    switch (wait_for_data(fileno(input), sec)) {
808    case 0:
809       return 0;                    /* timeout */
810    case -1:
811       return -1;                   /* error */
812    default:
813       len = sizeof_pool_memory(sock->msg) - 1;
814       if (stop) {
815          sleep(1);
816          goto again;
817       }
818 #ifdef HAVE_CONIO
819       if (bisatty(fileno(input))) {
820          input_line(sock->msg, len);
821          break;
822       }
823 #endif
824 #ifdef HAVE_WIN32 /* use special console for input on win32 */
825       if (input == stdin) {
826          if (win32_cgets(sock->msg, len) == NULL) {
827             return -1;
828          }
829       }
830       else
831 #endif
832       if (fgets(sock->msg, len, input) == NULL) {
833          return -1;
834
835       }
836       break;
837    }
838    if (usrbrk()) {
839       clrbrk();
840    }
841    strip_trailing_junk(sock->msg);
842    sock->msglen = strlen(sock->msg);
843    return 1;
844 }
845
846 #endif /* ! HAVE_READLINE */
847
848
849 static int console_update_history(const char *histfile)
850 {
851    int ret=0;
852
853 #ifdef HAVE_READLINE
854 /*
855  * first, try to truncate the history file, and if it
856  * fails, the file is probably not present, and we
857  * can use write_history to create it
858  */
859
860    if (history_truncate_file(histfile, 100) == 0) {
861       ret = append_history(history_length, histfile);
862    } else {
863       ret = write_history(histfile);
864    }
865 #endif
866
867    return ret;
868 }
869
870 static int console_init_history(const char *histfile)
871 {
872    int ret=0;
873
874 #ifdef HAVE_READLINE
875    using_history();
876    ret = read_history(histfile);
877    /* Tell the completer that we want a complete . */
878    rl_completion_entry_function = dummy_completion_function;
879    rl_attempted_completion_function = readline_completion;
880    rl_filename_completion_desired = 0;
881    stifle_history(100);
882 #endif
883
884    return ret;
885 }
886
887 bool select_director(const char *director, DIRRES **ret_dir, CONRES **ret_cons)
888 {
889    int numcon=0, numdir=0;
890    int i=0, item=0;
891    BSOCK *UA_sock;
892    DIRRES *dir=NULL;
893    CONRES *cons=NULL;
894    struct sockaddr client_addr;
895    memset(&client_addr, 0, sizeof(client_addr));
896
897    *ret_cons = NULL;
898    *ret_dir = NULL;
899
900    LockRes();
901    numdir = 0;
902    foreach_res(dir, R_DIRECTOR) {
903       numdir++;
904    }
905    numcon = 0;
906    foreach_res(cons, R_CONSOLE) {
907       numcon++;
908    }
909    UnlockRes();
910
911    if (numdir == 1) {           /* No choose */
912       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
913    } 
914  
915    if (director) {    /* Command line choice overwrite the no choose option */
916       LockRes();
917       foreach_res(dir, R_DIRECTOR) {
918          if (bstrcmp(dir->hdr.name, director)) {
919             break;
920          }
921       }
922       UnlockRes();
923       if (!dir) {               /* Can't find Director used as argument */
924          senditf(_("Can't find %s in Director list\n"), director);
925          return 0;
926       }
927    }
928
929    if (!dir) {                  /* prompt for director */
930       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
931 try_again:
932       sendit(_("Available Directors:\n"));
933       LockRes();
934       numdir = 0;
935       foreach_res(dir, R_DIRECTOR) {
936          senditf( _("%2d:  %s at %s:%d\n"), 1+numdir++, dir->hdr.name, 
937                   dir->address, dir->DIRport);
938       }
939       UnlockRes();
940       if (get_cmd(stdin, _("Select Director by entering a number: "), 
941                   UA_sock, 600) < 0) 
942       {
943          (void)WSACleanup();               /* Cleanup Windows sockets */
944          return 0;
945       }
946       if (!is_a_number(UA_sock->msg)) {
947          senditf(_("%s is not a number. You must enter a number between "
948                    "1 and %d\n"), 
949                  UA_sock->msg, numdir);
950          goto try_again;
951       }
952       item = atoi(UA_sock->msg);
953       if (item < 0 || item > numdir) {
954          senditf(_("You must enter a number between 1 and %d\n"), numdir);
955          goto try_again;
956       }
957       term_bsock(UA_sock);
958       LockRes();
959       for (i=0; i<item; i++) {
960          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
961       }
962       UnlockRes();
963    }
964    LockRes();
965    /* Look for a console linked to this director */
966    for (i=0; i<numcon; i++) {
967       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
968       if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
969          break;
970       }
971       cons = NULL;
972    }
973    /* Look for the first non-linked console */
974    if (cons == NULL) {
975       for (i=0; i<numcon; i++) {
976          cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
977          if (cons->director == NULL)
978             break;
979          cons = NULL;
980       }
981    }
982
983    /* If no console, take first one */
984    if (!cons) {
985       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
986    }
987    UnlockRes();
988
989    *ret_dir = dir;
990    *ret_cons = cons;
991    
992    return 1;
993 }
994
995 /*********************************************************************
996  *
997  *         Main Bacula Console -- User Interface Program
998  *
999  */
1000 int main(int argc, char *argv[])
1001 {
1002    int ch;
1003    char *director=NULL;
1004    bool list_directors=false;
1005    bool no_signals = false;
1006    bool test_config = false;
1007    JCR jcr;
1008    utime_t heart_beat;
1009
1010    setlocale(LC_ALL, "");
1011    bindtextdomain("bacula", LOCALEDIR);
1012    textdomain("bacula");
1013
1014    init_stack_dump();
1015    lmgr_init_thread();
1016    my_name_is(argc, argv, "bconsole");
1017    init_msg(NULL, NULL);
1018    working_directory = "/tmp";
1019    args = get_pool_memory(PM_FNAME);
1020
1021    while ((ch = getopt(argc, argv, "D:lbc:d:nstu:?")) != -1) {
1022       switch (ch) {
1023       case 'D':                    /* Director */
1024          if (director) {
1025             free(director);
1026          }
1027          director = bstrdup(optarg);
1028          break;
1029
1030       case 'l':
1031          list_directors = true;
1032          test_config = true;
1033          break;
1034
1035       case 'c':                    /* configuration file */
1036          if (configfile != NULL) {
1037             free(configfile);
1038          }
1039          configfile = bstrdup(optarg);
1040          break;
1041
1042       case 'd':
1043          if (*optarg == 't') {
1044             dbg_timestamp = true;
1045          } else {
1046             debug_level = atoi(optarg);
1047             if (debug_level <= 0) {
1048                debug_level = 1;
1049             }
1050          }
1051          break;
1052
1053       case 'n':                    /* no conio */
1054          no_conio = true;
1055          break;
1056
1057       case 's':                    /* turn off signals */
1058          no_signals = true;
1059          break;
1060
1061       case 't':
1062          test_config = true;
1063          break;
1064
1065       case 'u':
1066          timeout = atoi(optarg);
1067          break;
1068
1069       case '?':
1070       default:
1071          usage();
1072          exit(1);
1073       }
1074    }
1075    argc -= optind;
1076    argv += optind;
1077
1078    if (!no_signals) {
1079       init_signals(terminate_console);
1080    }
1081
1082
1083 #if !defined(HAVE_WIN32)
1084    /* Override Bacula default signals */
1085    signal(SIGQUIT, SIG_IGN);
1086    signal(SIGTSTP, got_sigstop);
1087    signal(SIGCONT, got_sigcontinue);
1088    signal(SIGTTIN, got_sigtin);
1089    signal(SIGTTOU, got_sigtout);
1090    trapctlc();
1091 #endif
1092
1093    OSDependentInit();
1094
1095    if (argc) {
1096       usage();
1097       exit(1);
1098    }
1099
1100    if (configfile == NULL) {
1101       configfile = bstrdup(CONFIG_FILE);
1102    }
1103
1104    config = new_config_parser();
1105    parse_cons_config(config, configfile, M_ERROR_TERM);
1106
1107    if (init_crypto() != 0) {
1108       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
1109    }
1110
1111    if (!check_resources()) {
1112       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
1113    }
1114
1115    if (!no_conio) {
1116       con_init(stdin);
1117    }
1118
1119    if (list_directors) {
1120       LockRes();
1121       foreach_res(dir, R_DIRECTOR) {
1122          senditf("%s\n", dir->hdr.name);
1123       }
1124       UnlockRes();
1125    }
1126
1127    if (test_config) {
1128       terminate_console(0);
1129       exit(0);
1130    }
1131
1132    memset(&jcr, 0, sizeof(jcr));
1133
1134    (void)WSA_Init();                        /* Initialize Windows sockets */
1135
1136    start_watchdog();                        /* Start socket watchdog */
1137
1138    if(!select_director(director, &dir, &cons)) {
1139       return 1;
1140    }
1141
1142    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1143
1144    char buf[1024];
1145    /* Initialize Console TLS context */
1146    if (cons && (cons->tls_enable || cons->tls_require)) {
1147       /* Generate passphrase prompt */
1148       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1149
1150       /* Initialize TLS context:
1151        * Args: CA certfile, CA certdir, Certfile, Keyfile,
1152        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer   
1153        */
1154       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1155          cons->tls_ca_certdir, cons->tls_certfile,
1156          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1157
1158       if (!cons->tls_ctx) {
1159          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1160             dir->hdr.name);
1161          terminate_console(0);
1162          return 1;
1163       }
1164    }
1165
1166    /* Initialize Director TLS context */
1167    if (dir->tls_enable || dir->tls_require) {
1168       /* Generate passphrase prompt */
1169       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1170
1171       /* Initialize TLS context:
1172        * Args: CA certfile, CA certdir, Certfile, Keyfile,
1173        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1174       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1175          dir->tls_ca_certdir, dir->tls_certfile,
1176          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1177
1178       if (!dir->tls_ctx) {
1179          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1180             dir->hdr.name);
1181          terminate_console(0);
1182          return 1;
1183       }
1184    }
1185
1186    if (dir->heartbeat_interval) {
1187       heart_beat = dir->heartbeat_interval;
1188    } else if (cons) {
1189       heart_beat = cons->heartbeat_interval;
1190    } else {
1191       heart_beat = 0;
1192    }
1193    UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1194                           NULL, dir->DIRport, 0);
1195    if (UA_sock == NULL) {
1196       terminate_console(0);
1197       return 1;
1198    }
1199    jcr.dir_bsock = UA_sock;
1200
1201    /* If cons==NULL, default console will be used */
1202    if (!authenticate_director(&jcr, dir, cons)) {
1203       terminate_console(0);
1204       return 1;
1205    }
1206
1207    Dmsg0(40, "Opened connection with Director daemon\n");
1208
1209    sendit(_("Enter a period to cancel a command.\n"));
1210
1211    /* Read/Update history file if HOME exists */
1212    POOL_MEM history_file;
1213
1214    /* Run commands in ~/.bconsolerc if any */
1215    char *env = getenv("HOME");
1216    if (env) {
1217       FILE *fd;
1218       pm_strcpy(&UA_sock->msg, env);
1219       pm_strcat(&UA_sock->msg, "/.bconsolerc");
1220       fd = fopen(UA_sock->msg, "rb");
1221       if (fd) {
1222          read_and_process_input(fd, UA_sock);
1223          fclose(fd);
1224       }
1225
1226       pm_strcpy(history_file, env);
1227       pm_strcat(history_file, "/.bconsole_history");
1228       console_init_history(history_file.c_str());
1229    }
1230
1231    read_and_process_input(stdin, UA_sock);
1232
1233    if (UA_sock) {
1234       UA_sock->signal(BNET_TERMINATE); /* send EOF */
1235       UA_sock->close();
1236    }
1237
1238    if (env) {
1239       console_update_history(history_file.c_str());
1240    }
1241
1242    terminate_console(0);
1243    return 0;
1244 }
1245
1246 /* Cleanup and then exit */
1247 static void terminate_console(int sig)
1248 {
1249
1250    static bool already_here = false;
1251
1252    if (already_here) {                /* avoid recursive temination problems */
1253       exit(1);
1254    }
1255    already_here = true;
1256    stop_watchdog();
1257    config->free_resources();
1258    free(config);
1259    config = NULL;
1260    cleanup_crypto();
1261    free_pool_memory(args);
1262    if (!no_conio) {
1263       con_term();
1264    }
1265    (void)WSACleanup();               /* Cleanup Windows sockets */
1266    lmgr_cleanup_main();
1267
1268    if (sig != 0) {
1269       exit(1);
1270    }
1271    return;
1272 }
1273
1274 /*
1275  * Make a quick check to see that we have all the
1276  * resources needed.
1277  */
1278 static int check_resources()
1279 {
1280    bool OK = true;
1281    DIRRES *director;
1282    bool tls_needed;
1283
1284    LockRes();
1285
1286    numdir = 0;
1287    foreach_res(director, R_DIRECTOR) {
1288
1289       numdir++;
1290       /* tls_require implies tls_enable */
1291       if (director->tls_require) {
1292          if (have_tls) {
1293             director->tls_enable = true;
1294          } else {
1295             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1296             OK = false;
1297             continue;
1298          }
1299       }
1300       tls_needed = director->tls_enable || director->tls_authenticate;
1301
1302       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1303          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1304                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1305                              " At least one CA certificate store is required.\n"),
1306                              director->hdr.name, configfile);
1307          OK = false;
1308       }
1309    }
1310    
1311    if (numdir == 0) {
1312       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1313                           "Without that I don't how to speak to the Director :-(\n"), configfile);
1314       OK = false;
1315    }
1316
1317    CONRES *cons;
1318    /* Loop over Consoles */
1319    foreach_res(cons, R_CONSOLE) {
1320       /* tls_require implies tls_enable */
1321       if (cons->tls_require) {
1322          if (have_tls) {
1323             cons->tls_enable = true;
1324          } else {
1325             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1326             OK = false;
1327             continue;
1328          }
1329       }
1330       tls_needed = cons->tls_enable || cons->tls_authenticate;
1331       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1332          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1333                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1334                              cons->hdr.name, configfile);
1335          OK = false;
1336       }
1337    }
1338
1339    UnlockRes();
1340
1341    return OK;
1342 }
1343
1344 static int versioncmd(FILE *input, BSOCK *UA_sock)
1345 {
1346    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1347       HOST_OS, DISTNAME, DISTVER);
1348    return 1;
1349 }
1350
1351 static int inputcmd(FILE *input, BSOCK *UA_sock)
1352 {
1353    FILE *fd;
1354
1355    if (argc > 2) {
1356       sendit(_("Too many arguments on input command.\n"));
1357       return 1;
1358    }
1359    if (argc == 1) {
1360       sendit(_("First argument to input command must be a filename.\n"));
1361       return 1;
1362    }
1363    fd = fopen(argk[1], "rb");
1364    if (!fd) {
1365       berrno be;
1366       senditf(_("Cannot open file %s for input. ERR=%s\n"),
1367          argk[1], be.bstrerror());
1368       return 1;
1369    }
1370    read_and_process_input(fd, UA_sock);
1371    fclose(fd);
1372    return 1;
1373 }
1374
1375 /* Send output to both termina and specified file */
1376 static int teecmd(FILE *input, BSOCK *UA_sock)
1377 {
1378    teeout = true;
1379    return do_outputcmd(input, UA_sock);
1380 }
1381
1382 /* Send output to specified "file" */
1383 static int outputcmd(FILE *input, BSOCK *UA_sock)
1384 {
1385    teeout = false;
1386    return do_outputcmd(input, UA_sock);
1387 }
1388
1389
1390 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1391 {
1392    FILE *fd;
1393    const char *mode = "a+b";
1394
1395    if (argc > 3) {
1396       sendit(_("Too many arguments on output/tee command.\n"));
1397       return 1;
1398    }
1399    if (argc == 1) {
1400       if (output != stdout) {
1401          fclose(output);
1402          output = stdout;
1403          teeout = false;
1404       }
1405       return 1;
1406    }
1407    if (argc == 3) {
1408       mode = argk[2];
1409    }
1410    fd = fopen(argk[1], mode);
1411    if (!fd) {
1412       berrno be;
1413       senditf(_("Cannot open file %s for output. ERR=%s\n"),
1414          argk[1], be.bstrerror(errno));
1415       return 1;
1416    }
1417    output = fd;
1418    return 1;
1419 }
1420
1421 /*
1422  * exec "some-command" [wait-seconds]
1423 */
1424 static int execcmd(FILE *input, BSOCK *UA_sock)
1425 {
1426    BPIPE *bpipe;
1427    char line[5000];
1428    int stat;
1429    int wait = 0;
1430
1431    if (argc > 3) {
1432       sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1433       return 1;
1434    }
1435    if (argc == 3) {
1436       wait = atoi(argk[2]);
1437    }
1438    bpipe = open_bpipe(argk[1], wait, "r");
1439    if (!bpipe) {
1440       berrno be;
1441       senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1442          argk[1], be.bstrerror(errno));
1443       return 1;
1444    }
1445   
1446    while (fgets(line, sizeof(line), bpipe->rfd)) {
1447       senditf("%s", line);
1448    }
1449    stat = close_bpipe(bpipe);
1450    if (stat != 0) {
1451       berrno be;
1452       be.set_errno(stat);
1453      senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1454    }
1455    return 1;
1456 }
1457
1458
1459 static int echocmd(FILE *input, BSOCK *UA_sock)
1460 {
1461    for (int i=1; i < argc; i++) {
1462       senditf("%s ", argk[i]);
1463    }
1464    sendit("\n");
1465    return 1;
1466 }
1467
1468 static int quitcmd(FILE *input, BSOCK *UA_sock)
1469 {
1470    return 0;
1471 }
1472
1473 static int helpcmd(FILE *input, BSOCK *UA_sock)
1474 {
1475    int i;
1476    for (i=0; i<comsize; i++) { 
1477       senditf("  %-10s %s\n", commands[i].key, commands[i].help);
1478    }
1479    return 1;   
1480 }
1481
1482
1483 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1484 {
1485    if (argc > 1) {
1486       sleep(atoi(argk[1]));
1487    }
1488    return 1;
1489 }
1490
1491
1492 static int timecmd(FILE *input, BSOCK *UA_sock)
1493 {
1494    char sdt[50];
1495    time_t ttime = time(NULL);
1496    struct tm tm;
1497    (void)localtime_r(&ttime, &tm);
1498    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1499    sendit("\n");
1500    return 1;
1501 }
1502
1503 /*
1504  * Send a line to the output file and or the terminal
1505  */
1506 void senditf(const char *fmt,...)
1507 {
1508    char buf[3000];
1509    va_list arg_ptr;
1510
1511    va_start(arg_ptr, fmt);
1512    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1513    va_end(arg_ptr);
1514    sendit(buf);
1515 }
1516
1517 void sendit(const char *buf)
1518 {
1519 #ifdef CONIO_FIX
1520    char obuf[3000];
1521    if (output == stdout || teeout) {
1522       const char *p, *q;
1523       /*
1524        * Here, we convert every \n into \r\n because the
1525        *  terminal is in raw mode when we are using
1526        *  conio.
1527        */
1528       for (p=q=buf; (p=strchr(q, '\n')); ) {
1529          int len = p - q;
1530          if (len > 0) {
1531             memcpy(obuf, q, len);
1532          }
1533          memcpy(obuf+len, "\r\n", 3);
1534          q = ++p;                    /* point after \n */
1535          fputs(obuf, output);
1536       }
1537       if (*q) {
1538          fputs(q, output);
1539       }
1540       fflush(output);
1541    }
1542    if (output != stdout) {
1543       fputs(buf, output);
1544    }
1545 #else
1546
1547    fputs(buf, output);
1548    fflush(output);
1549    if (teeout) {
1550       fputs(buf, stdout);
1551       fflush(stdout);
1552    }
1553 #endif
1554 }