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