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