]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
add lmgr to bconsole
[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 #endif
113
114
115 #define CONFIG_FILE "bconsole.conf"   /* default configuration file */
116
117 static void usage()
118 {
119    fprintf(stderr, _(
120 PROG_COPYRIGHT
121 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
122 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
123 "       -c <file>   set configuration file to file\n"
124 "       -d <nn>     set debug level to <nn>\n"
125 "       -dt         print timestamp in debug output\n"
126 "       -n          no conio\n"
127 "       -s          no signals\n"
128 "       -u <nn>     set command execution timeout to <nn> seconds\n"
129 "       -t          test - read configuration and exit\n"
130 "       -?          print this message.\n"
131 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
132 }
133
134
135 extern "C"
136 void got_sigstop(int sig)
137 {
138    stop = true;
139 }
140
141 extern "C"
142 void got_sigcontinue(int sig)
143 {
144    stop = false;
145 }
146
147 extern "C"
148 void got_sigtout(int sig)
149 {
150 // printf("Got tout\n");
151 }
152
153 extern "C"
154 void got_sigtin(int sig)
155 {
156 // printf("Got tin\n");
157 }
158
159
160 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
161 {
162    con_set_zed_keys();
163    return 1;
164 }
165
166 /*
167  * These are the @command
168  */
169 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
170 static struct cmdstruct commands[] = {
171  { N_("input"),      inputcmd,     _("input from file")},
172  { N_("output"),     outputcmd,    _("output to file")},
173  { N_("quit"),       quitcmd,      _("quit")},
174  { N_("tee"),        teecmd,       _("output to file and terminal")},
175  { N_("sleep"),      sleepcmd,     _("sleep specified time")},
176  { N_("time"),       timecmd,      _("print current time")},
177  { N_("version"),    versioncmd,   _("print Console's version")},
178  { N_("echo"),       echocmd,      _("echo command string")},
179  { N_("exec"),       execcmd,      _("execute an external command")},
180  { N_("exit"),       quitcmd,      _("exit = quit")},
181  { N_("zed_keys"),   zed_keyscmd,  _("zed_keys = use zed keys instead of bash keys")},
182  { N_("help"),       helpcmd,      _("help listing")},
183 #ifdef HAVE_READLINE
184  { N_("separator"),  eolcmd,       _("set command separator")},
185 #endif
186              };
187 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
188
189 static int do_a_command(FILE *input, BSOCK *UA_sock)
190 {
191    unsigned int i;
192    int stat;
193    int found;
194    int len;
195    char *cmd;
196
197    found = 0;
198    stat = 1;
199
200    Dmsg1(120, "Command: %s\n", UA_sock->msg);
201    if (argc == 0) {
202       return 1;
203    }
204
205    cmd = argk[0]+1;
206    if (*cmd == '#') {                 /* comment */
207       return 1;
208    }
209    len = strlen(cmd);
210    for (i=0; i<comsize; i++) {     /* search for command */
211       if (strncasecmp(cmd,  _(commands[i].key), len) == 0) {
212          stat = (*commands[i].func)(input, UA_sock);   /* go execute command */
213          found = 1;
214          break;
215       }
216    }
217    if (!found) {
218       pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
219       UA_sock->msglen = strlen(UA_sock->msg);
220       sendit(UA_sock->msg);
221    }
222    return stat;
223 }
224
225
226 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
227 {
228    const char *prompt = "*";
229    bool at_prompt = false;
230    int tty_input = isatty(fileno(input));
231    int stat;
232    btimer_t *tid=NULL;
233
234    for ( ;; ) {
235       if (at_prompt) {                /* don't prompt multiple times */
236          prompt = "";
237       } else {
238          prompt = "*";
239          at_prompt = true;
240       }
241       if (tty_input) {
242          stat = get_cmd(input, prompt, UA_sock, 30);
243          if (usrbrk() == 1) {
244             clrbrk();
245          }
246          if (usrbrk()) {
247             break;
248          }
249       } else {
250          /* Reading input from a file */
251          int len = sizeof_pool_memory(UA_sock->msg) - 1;
252          if (usrbrk()) {
253             break;
254          }
255          if (fgets(UA_sock->msg, len, input) == NULL) {
256             stat = -1;
257          } else {
258             sendit(UA_sock->msg);  /* echo to terminal */
259             strip_trailing_junk(UA_sock->msg);
260             UA_sock->msglen = strlen(UA_sock->msg);
261             stat = 1;
262          }
263       }
264       if (stat < 0) {
265          break;                       /* error or interrupt */
266       } else if (stat == 0) {         /* timeout */
267          if (strcmp(prompt, "*") == 0) {
268             tid = start_bsock_timer(UA_sock, timeout);
269             bnet_fsend(UA_sock, ".messages");
270             stop_bsock_timer(tid);
271          } else {
272             continue;
273          }
274       } else {
275          at_prompt = FALSE;
276          /* @ => internal command for us */
277          if (UA_sock->msg[0] == '@') {
278             parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
279             if (!do_a_command(input, UA_sock)) {
280                break;
281             }
282             continue;
283          }
284          tid = start_bsock_timer(UA_sock, timeout);
285          if (!bnet_send(UA_sock)) {   /* send command */
286             stop_bsock_timer(tid);
287             break;                    /* error */
288          }
289          stop_bsock_timer(tid);
290       }
291       if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
292          break;
293       }
294       tid = start_bsock_timer(UA_sock, timeout);
295       while ((stat = bnet_recv(UA_sock)) >= 0) {
296          if (at_prompt) {
297             if (!stop) {
298                sendit("\n");
299             }
300             at_prompt = false;
301          }
302          /* Suppress output if running in background or user hit ctl-c */
303          if (!stop && !usrbrk()) {
304             sendit(UA_sock->msg);
305          }
306       }
307       stop_bsock_timer(tid);
308       if (usrbrk() > 1) {
309          break;
310       } else {
311          clrbrk();
312       }
313       if (!stop) {
314          fflush(stdout);
315       }
316       if (is_bnet_stop(UA_sock)) {
317          break;                       /* error or term */
318       } else if (stat == BNET_SIGNAL) {
319          if (UA_sock->msglen == BNET_PROMPT) {
320             at_prompt = true;
321          }
322          Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
323       }
324    }
325 }
326
327 /*
328  * Call-back for reading a passphrase for an encrypted PEM file
329  * This function uses getpass(), 
330  *  which uses a static buffer and is NOT thread-safe.
331  */
332 static int tls_pem_callback(char *buf, int size, const void *userdata)
333 {
334 #ifdef HAVE_TLS
335    const char *prompt = (const char *)userdata;
336 # if defined(HAVE_WIN32)
337    sendit(prompt);
338    if (win32_cgets(buf, size) == NULL) {
339       buf[0] = 0;
340       return 0;
341    } else {
342       return strlen(buf);
343    }
344 # else
345    char *passwd;
346
347    passwd = getpass(prompt);
348    bstrncpy(buf, passwd, size);
349    return strlen(buf);
350 # endif
351 #else
352    buf[0] = 0;
353    return 0;
354 #endif
355 }
356
357 #ifdef HAVE_READLINE
358 #define READLINE_LIBRARY 1
359 #include "readline.h"
360 #include "history.h"
361
362 static char eol = '\0';
363 static int eolcmd(FILE *input, BSOCK *UA_sock)
364 {
365    if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
366       eol = argk[1][0];
367    } else if (argc == 1) {
368       eol = '\0';
369    } else {
370       sendit(_("Illegal separator character.\n"));
371    }
372    return 1;
373 }
374
375 int
376 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
377 {
378    static char *line = NULL;
379    static char *next = NULL;
380    static int do_history = 0;
381    char *command;
382
383    if (line == NULL) {
384       do_history = 0;
385       rl_catch_signals = 0;              /* do it ourselves */
386       /* Here, readline does ***real*** malloc
387        * so, be we have to use the real free
388        */
389       line = readline((char *)prompt);   /* cast needed for old readlines */
390       if (!line) {
391          exit(1);
392       }
393       strip_trailing_junk(line);
394       command = line;
395    } else if (next) {
396       command = next + 1;
397    } else {
398      sendit(_("Command logic problem\n"));
399      sock->msglen = 0;
400      sock->msg[0] = 0;
401      return 0;
402    }
403
404    /*
405     * Split "line" into multiple commands separated by the eol character.
406     *   Each part is pointed to by "next" until finally it becomes null.
407     */
408    if (eol == '\0') {
409       next = NULL;
410    } else {
411       next = strchr(command, eol);
412       if (next) {
413          *next = '\0';
414       }
415    }
416    if (command != line && isatty(fileno(input))) {
417       senditf("%s%s\n", prompt, command);
418    }
419
420    sock->msglen = pm_strcpy(&sock->msg, command);
421    if (sock->msglen) {
422       do_history++;
423    }
424
425    if (!next) {
426       if (do_history) {
427         add_history(line);
428       }
429       actuallyfree(line);       /* allocated by readline() malloc */
430       line = NULL;
431    }
432    return 1;
433 }
434
435 #else /* no readline, do it ourselves */
436
437 #ifdef HAVE_CONIO
438 static bool bisatty(int fd)
439 {
440    if (no_conio) {
441       return false;
442    }
443    return isatty(fd);
444 }
445 #endif
446
447 /*
448  *   Returns: 1 if data available
449  *            0 if timeout
450  *           -1 if error
451  */
452 static int
453 wait_for_data(int fd, int sec)
454 {
455 #if defined(HAVE_WIN32)
456    return 1;
457 #else
458    fd_set fdset;
459    struct timeval tv;
460
461    tv.tv_sec = sec;
462    tv.tv_usec = 0;
463    for ( ;; ) {
464       FD_ZERO(&fdset);
465       FD_SET((unsigned)fd, &fdset);
466       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
467       case 0:                         /* timeout */
468          return 0;
469       case -1:
470          if (errno == EINTR || errno == EAGAIN) {
471             continue;
472          }
473          return -1;                  /* error return */
474       default:
475          return 1;
476       }
477    }
478 #endif
479 }
480
481 /*
482  * Get next input command from terminal.
483  *
484  *   Returns: 1 if got input
485  *            0 if timeout
486  *           -1 if EOF or error
487  */
488 int
489 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
490 {
491    int len;
492    if (!stop) {
493       if (output == stdout || teeout) {
494          sendit(prompt);
495       }
496    }
497 again:
498    switch (wait_for_data(fileno(input), sec)) {
499    case 0:
500       return 0;                    /* timeout */
501    case -1:
502       return -1;                   /* error */
503    default:
504       len = sizeof_pool_memory(sock->msg) - 1;
505       if (stop) {
506          sleep(1);
507          goto again;
508       }
509 #ifdef HAVE_CONIO
510       if (bisatty(fileno(input))) {
511          input_line(sock->msg, len);
512          break;
513       }
514 #endif
515 #ifdef HAVE_WIN32 /* use special console for input on win32 */
516       if (input == stdin) {
517          if (win32_cgets(sock->msg, len) == NULL) {
518             return -1;
519          }
520       }
521       else
522 #endif
523       if (fgets(sock->msg, len, input) == NULL) {
524          return -1;
525
526       }
527       break;
528    }
529    if (usrbrk()) {
530       clrbrk();
531    }
532    strip_trailing_junk(sock->msg);
533    sock->msglen = strlen(sock->msg);
534    return 1;
535 }
536
537 #endif /* ! HAVE_READLINE */
538
539
540 static int console_update_history(const char *histfile)
541 {
542    int ret=0;
543
544 #ifdef HAVE_READLINE
545 /* first, try to truncate the history file, and if it
546  * fail, the file is probably not present, and we
547  * can use write_history to create it
548  */
549
550    if (history_truncate_file(histfile, 100) == 0) {
551       ret = append_history(history_length, histfile);
552    } else {
553       ret = write_history(histfile);
554    }
555
556 #endif
557
558    return ret;
559 }
560
561 static int console_init_history(const char *histfile)
562 {
563    int ret=0;
564
565 #ifdef HAVE_READLINE
566
567    using_history();
568    ret = read_history(histfile);
569
570 #endif
571
572    return ret;
573 }
574
575 /*********************************************************************
576  *
577  *         Main Bacula Console -- User Interface Program
578  *
579  */
580 int main(int argc, char *argv[])
581 {
582    int ch, i, item;
583    bool no_signals = false;
584    bool test_config = false;
585    JCR jcr;
586    utime_t heart_beat;
587
588    setlocale(LC_ALL, "");
589    bindtextdomain("bacula", LOCALEDIR);
590    textdomain("bacula");
591
592    init_stack_dump();
593    lmgr_init_thread();
594    my_name_is(argc, argv, "bconsole");
595    init_msg(NULL, NULL);
596    working_directory = "/tmp";
597    args = get_pool_memory(PM_FNAME);
598
599    while ((ch = getopt(argc, argv, "bc:d:nstu:?")) != -1) {
600       switch (ch) {
601       case 'c':                    /* configuration file */
602          if (configfile != NULL) {
603             free(configfile);
604          }
605          configfile = bstrdup(optarg);
606          break;
607
608       case 'd':
609          if (*optarg == 't') {
610             dbg_timestamp = true;
611          } else {
612             debug_level = atoi(optarg);
613             if (debug_level <= 0) {
614                debug_level = 1;
615             }
616          }
617          break;
618
619       case 'n':                    /* no conio */
620          no_conio = true;
621          break;
622
623       case 's':                    /* turn off signals */
624          no_signals = true;
625          break;
626
627       case 't':
628          test_config = true;
629          break;
630
631       case 'u':
632          timeout = atoi(optarg);
633          break;
634
635       case '?':
636       default:
637          usage();
638          exit(1);
639       }
640    }
641    argc -= optind;
642    argv += optind;
643
644    if (!no_signals) {
645       init_signals(terminate_console);
646    }
647
648
649 #if !defined(HAVE_WIN32)
650    /* Override Bacula default signals */
651    signal(SIGQUIT, SIG_IGN);
652    signal(SIGTSTP, got_sigstop);
653    signal(SIGCONT, got_sigcontinue);
654    signal(SIGTTIN, got_sigtin);
655    signal(SIGTTOU, got_sigtout);
656    trapctlc();
657 #endif
658
659    OSDependentInit();
660
661    if (argc) {
662       usage();
663       exit(1);
664    }
665
666    if (configfile == NULL) {
667       configfile = bstrdup(CONFIG_FILE);
668    }
669
670    config = new_config_parser();
671    parse_cons_config(config, configfile, M_ERROR_TERM);
672
673    if (init_crypto() != 0) {
674       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
675    }
676
677    if (!check_resources()) {
678       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
679    }
680
681    if (!no_conio) {
682       con_init(stdin);
683    }
684
685    if (test_config) {
686       terminate_console(0);
687       exit(0);
688    }
689
690    memset(&jcr, 0, sizeof(jcr));
691
692    (void)WSA_Init();                        /* Initialize Windows sockets */
693
694    start_watchdog();                        /* Start socket watchdog */
695
696    LockRes();
697    numdir = 0;
698    foreach_res(dir, R_DIRECTOR) {
699       numdir++;
700    }
701    numcon = 0;
702    foreach_res(cons, R_CONSOLE) {
703       numcon++;
704    }
705    UnlockRes();
706
707    if (numdir > 1) {
708       struct sockaddr client_addr;
709       memset(&client_addr, 0, sizeof(client_addr));
710       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
711 try_again:
712       sendit(_("Available Directors:\n"));
713       LockRes();
714       numdir = 0;
715       foreach_res(dir, R_DIRECTOR) {
716          senditf( _("%2d:  %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
717             dir->DIRport);
718       }
719       UnlockRes();
720       if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
721          (void)WSACleanup();               /* Cleanup Windows sockets */
722          return 1;
723       }
724       if (!is_a_number(UA_sock->msg)) {
725          senditf(_("%s is not a number. You must enter a number between 1 and %d\n"), 
726                  UA_sock->msg, numdir);
727          goto try_again;
728       }
729       item = atoi(UA_sock->msg);
730       if (item < 0 || item > numdir) {
731          senditf(_("You must enter a number between 1 and %d\n"), numdir);
732          goto try_again;
733       }
734       term_bsock(UA_sock);
735       LockRes();
736       for (i=0; i<item; i++) {
737          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
738       }
739       /* Look for a console linked to this director */
740       for (i=0; i<numcon; i++) {
741          cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
742          if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
743             break;
744          }
745          cons = NULL;
746       }
747       /* Look for the first non-linked console */
748       if (cons == NULL) {
749          for (i=0; i<numcon; i++) {
750             cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
751             if (cons->director == NULL)
752                break;
753             cons = NULL;
754         }
755       }
756       UnlockRes();
757    }
758    /* If no director, take first one */
759    if (!dir) {
760       LockRes();
761       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
762       UnlockRes();
763    }
764    /* If no console, take first one */
765    if (!cons) {
766       LockRes();
767       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
768       UnlockRes();
769    }
770
771    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
772
773    char buf[1024];
774    /* Initialize Console TLS context */
775    if (cons && (cons->tls_enable || cons->tls_require)) {
776       /* Generate passphrase prompt */
777       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
778
779       /* Initialize TLS context:
780        * Args: CA certfile, CA certdir, Certfile, Keyfile,
781        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer   
782        */
783       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
784          cons->tls_ca_certdir, cons->tls_certfile,
785          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
786
787       if (!cons->tls_ctx) {
788          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
789             dir->hdr.name);
790          terminate_console(0);
791          return 1;
792       }
793    }
794
795    /* Initialize Director TLS context */
796    if (dir->tls_enable || dir->tls_require) {
797       /* Generate passphrase prompt */
798       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
799
800       /* Initialize TLS context:
801        * Args: CA certfile, CA certdir, Certfile, Keyfile,
802        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
803       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
804          dir->tls_ca_certdir, dir->tls_certfile,
805          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
806
807       if (!dir->tls_ctx) {
808          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
809             dir->hdr.name);
810          terminate_console(0);
811          return 1;
812       }
813    }
814
815    if (dir->heartbeat_interval) {
816       heart_beat = dir->heartbeat_interval;
817    } else if (cons) {
818       heart_beat = cons->heartbeat_interval;
819    } else {
820       heart_beat = 0;
821    }
822    UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
823                           NULL, dir->DIRport, 0);
824    if (UA_sock == NULL) {
825       terminate_console(0);
826       return 1;
827    }
828    jcr.dir_bsock = UA_sock;
829
830    /* If cons==NULL, default console will be used */
831    if (!authenticate_director(&jcr, dir, cons)) {
832       terminate_console(0);
833       return 1;
834    }
835
836    Dmsg0(40, "Opened connection with Director daemon\n");
837
838    sendit(_("Enter a period to cancel a command.\n"));
839
840    /* Read/Update history file if HOME exists */
841    POOL_MEM history_file;
842
843    /* Run commands in ~/.bconsolerc if any */
844    char *env = getenv("HOME");
845    if (env) {
846       FILE *fd;
847       pm_strcpy(&UA_sock->msg, env);
848       pm_strcat(&UA_sock->msg, "/.bconsolerc");
849       fd = fopen(UA_sock->msg, "rb");
850       if (fd) {
851          read_and_process_input(fd, UA_sock);
852          fclose(fd);
853       }
854
855       pm_strcpy(history_file, env);
856       pm_strcat(history_file, "/.bconsole_history");
857       console_init_history(history_file.c_str());
858    }
859
860    read_and_process_input(stdin, UA_sock);
861
862    if (UA_sock) {
863       UA_sock->signal(BNET_TERMINATE); /* send EOF */
864       UA_sock->close();
865    }
866
867    if (env) {
868       console_update_history(history_file.c_str());
869    }
870
871    terminate_console(0);
872    return 0;
873 }
874
875 /* Cleanup and then exit */
876 static void terminate_console(int sig)
877 {
878
879    static bool already_here = false;
880
881    if (already_here) {                /* avoid recursive temination problems */
882       exit(1);
883    }
884    already_here = true;
885    stop_watchdog();
886    config->free_resources();
887    free(config);
888    config = NULL;
889    cleanup_crypto();
890    free_pool_memory(args);
891    if (!no_conio) {
892       con_term();
893    }
894    (void)WSACleanup();               /* Cleanup Windows sockets */
895    lmgr_cleanup_main();
896
897    if (sig != 0) {
898       exit(1);
899    }
900    return;
901 }
902
903 /*
904  * Make a quick check to see that we have all the
905  * resources needed.
906  */
907 static int check_resources()
908 {
909    bool OK = true;
910    DIRRES *director;
911    bool tls_needed;
912
913    LockRes();
914
915    numdir = 0;
916    foreach_res(director, R_DIRECTOR) {
917
918       numdir++;
919       /* tls_require implies tls_enable */
920       if (director->tls_require) {
921          if (have_tls) {
922             director->tls_enable = true;
923          } else {
924             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
925             OK = false;
926             continue;
927          }
928       }
929       tls_needed = director->tls_enable || director->tls_authenticate;
930
931       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
932          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
933                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
934                              " At least one CA certificate store is required.\n"),
935                              director->hdr.name, configfile);
936          OK = false;
937       }
938    }
939    
940    if (numdir == 0) {
941       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
942                           "Without that I don't how to speak to the Director :-(\n"), configfile);
943       OK = false;
944    }
945
946    CONRES *cons;
947    /* Loop over Consoles */
948    foreach_res(cons, R_CONSOLE) {
949       /* tls_require implies tls_enable */
950       if (cons->tls_require) {
951          if (have_tls) {
952             cons->tls_enable = true;
953          } else {
954             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
955             OK = false;
956             continue;
957          }
958       }
959       tls_needed = cons->tls_enable || cons->tls_authenticate;
960       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
961          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
962                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
963                              cons->hdr.name, configfile);
964          OK = false;
965       }
966    }
967
968    UnlockRes();
969
970    return OK;
971 }
972
973 static int versioncmd(FILE *input, BSOCK *UA_sock)
974 {
975    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
976       HOST_OS, DISTNAME, DISTVER);
977    return 1;
978 }
979
980 static int inputcmd(FILE *input, BSOCK *UA_sock)
981 {
982    FILE *fd;
983
984    if (argc > 2) {
985       sendit(_("Too many arguments on input command.\n"));
986       return 1;
987    }
988    if (argc == 1) {
989       sendit(_("First argument to input command must be a filename.\n"));
990       return 1;
991    }
992    fd = fopen(argk[1], "rb");
993    if (!fd) {
994       berrno be;
995       senditf(_("Cannot open file %s for input. ERR=%s\n"),
996          argk[1], be.bstrerror());
997       return 1;
998    }
999    read_and_process_input(fd, UA_sock);
1000    fclose(fd);
1001    return 1;
1002 }
1003
1004 /* Send output to both termina and specified file */
1005 static int teecmd(FILE *input, BSOCK *UA_sock)
1006 {
1007    teeout = true;
1008    return do_outputcmd(input, UA_sock);
1009 }
1010
1011 /* Send output to specified "file" */
1012 static int outputcmd(FILE *input, BSOCK *UA_sock)
1013 {
1014    teeout = false;
1015    return do_outputcmd(input, UA_sock);
1016 }
1017
1018
1019 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1020 {
1021    FILE *fd;
1022    const char *mode = "a+b";
1023
1024    if (argc > 3) {
1025       sendit(_("Too many arguments on output/tee command.\n"));
1026       return 1;
1027    }
1028    if (argc == 1) {
1029       if (output != stdout) {
1030          fclose(output);
1031          output = stdout;
1032          teeout = false;
1033       }
1034       return 1;
1035    }
1036    if (argc == 3) {
1037       mode = argk[2];
1038    }
1039    fd = fopen(argk[1], mode);
1040    if (!fd) {
1041       berrno be;
1042       senditf(_("Cannot open file %s for output. ERR=%s\n"),
1043          argk[1], be.bstrerror(errno));
1044       return 1;
1045    }
1046    output = fd;
1047    return 1;
1048 }
1049
1050 /*
1051  * exec "some-command" [wait-seconds]
1052 */
1053 static int execcmd(FILE *input, BSOCK *UA_sock)
1054 {
1055    BPIPE *bpipe;
1056    char line[5000];
1057    int stat;
1058    int wait = 0;
1059
1060    if (argc > 3) {
1061       sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1062       return 1;
1063    }
1064    if (argc == 3) {
1065       wait = atoi(argk[2]);
1066    }
1067    bpipe = open_bpipe(argk[1], wait, "r");
1068    if (!bpipe) {
1069       berrno be;
1070       senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1071          argk[1], be.bstrerror(errno));
1072       return 1;
1073    }
1074   
1075    while (fgets(line, sizeof(line), bpipe->rfd)) {
1076       senditf("%s", line);
1077    }
1078    stat = close_bpipe(bpipe);
1079    if (stat != 0) {
1080       berrno be;
1081       be.set_errno(stat);
1082      senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1083    }
1084    return 1;
1085 }
1086
1087
1088 static int echocmd(FILE *input, BSOCK *UA_sock)
1089 {
1090    for (int i=1; i < argc; i++) {
1091       senditf("%s ", argk[i]);
1092    }
1093    sendit("\n");
1094    return 1;
1095 }
1096
1097 static int quitcmd(FILE *input, BSOCK *UA_sock)
1098 {
1099    return 0;
1100 }
1101
1102 static int helpcmd(FILE *input, BSOCK *UA_sock)
1103 {
1104    int i;
1105    for (i=0; i<comsize; i++) { 
1106       senditf("  %-10s %s\n", commands[i].key, commands[i].help);
1107    }
1108    return 1;   
1109 }
1110
1111
1112 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1113 {
1114    if (argc > 1) {
1115       sleep(atoi(argk[1]));
1116    }
1117    return 1;
1118 }
1119
1120
1121 static int timecmd(FILE *input, BSOCK *UA_sock)
1122 {
1123    char sdt[50];
1124    time_t ttime = time(NULL);
1125    struct tm tm;
1126    (void)localtime_r(&ttime, &tm);
1127    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1128    sendit("\n");
1129    return 1;
1130 }
1131
1132 /*
1133  * Send a line to the output file and or the terminal
1134  */
1135 void senditf(const char *fmt,...)
1136 {
1137    char buf[3000];
1138    va_list arg_ptr;
1139
1140    va_start(arg_ptr, fmt);
1141    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1142    va_end(arg_ptr);
1143    sendit(buf);
1144 }
1145
1146 void sendit(const char *buf)
1147 {
1148 #ifdef CONIO_FIX
1149    char obuf[3000];
1150    if (output == stdout || teeout) {
1151       const char *p, *q;
1152       /*
1153        * Here, we convert every \n into \r\n because the
1154        *  terminal is in raw mode when we are using
1155        *  conio.
1156        */
1157       for (p=q=buf; (p=strchr(q, '\n')); ) {
1158          int len = p - q;
1159          if (len > 0) {
1160             memcpy(obuf, q, len);
1161          }
1162          memcpy(obuf+len, "\r\n", 3);
1163          q = ++p;                    /* point after \n */
1164          fputs(obuf, output);
1165       }
1166       if (*q) {
1167          fputs(q, output);
1168       }
1169       fflush(output);
1170    }
1171    if (output != stdout) {
1172       fputs(buf, output);
1173    }
1174 #else
1175
1176    fputs(buf, output);
1177    fflush(output);
1178    if (teeout) {
1179       fputs(buf, stdout);
1180       fflush(stdout);
1181    }
1182 #endif
1183 }