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