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