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