]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Cleanup new timeout code for 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    my_name_is(argc, argv, "bconsole");
594    init_msg(NULL, NULL);
595    working_directory = "/tmp";
596    args = get_pool_memory(PM_FNAME);
597
598    while ((ch = getopt(argc, argv, "bc:d:nstu:?")) != -1) {
599       switch (ch) {
600       case 'c':                    /* configuration file */
601          if (configfile != NULL) {
602             free(configfile);
603          }
604          configfile = bstrdup(optarg);
605          break;
606
607       case 'd':
608          if (*optarg == 't') {
609             dbg_timestamp = true;
610          } else {
611             debug_level = atoi(optarg);
612             if (debug_level <= 0) {
613                debug_level = 1;
614             }
615          }
616          break;
617
618       case 'n':                    /* no conio */
619          no_conio = true;
620          break;
621
622       case 's':                    /* turn off signals */
623          no_signals = true;
624          break;
625
626       case 't':
627          test_config = true;
628          break;
629
630       case 'u':
631          timeout = atoi(optarg);
632          break;
633
634       case '?':
635       default:
636          usage();
637          exit(1);
638       }
639    }
640    argc -= optind;
641    argv += optind;
642
643    if (!no_signals) {
644       init_signals(terminate_console);
645    }
646
647
648 #if !defined(HAVE_WIN32)
649    /* Override Bacula default signals */
650    signal(SIGQUIT, SIG_IGN);
651    signal(SIGTSTP, got_sigstop);
652    signal(SIGCONT, got_sigcontinue);
653    signal(SIGTTIN, got_sigtin);
654    signal(SIGTTOU, got_sigtout);
655    trapctlc();
656 #endif
657
658    OSDependentInit();
659
660    if (argc) {
661       usage();
662       exit(1);
663    }
664
665    if (configfile == NULL) {
666       configfile = bstrdup(CONFIG_FILE);
667    }
668
669    config = new_config_parser();
670    parse_cons_config(config, configfile, M_ERROR_TERM);
671
672    if (init_crypto() != 0) {
673       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
674    }
675
676    if (!check_resources()) {
677       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
678    }
679
680    if (!no_conio) {
681       con_init(stdin);
682    }
683
684    if (test_config) {
685       terminate_console(0);
686       exit(0);
687    }
688
689    memset(&jcr, 0, sizeof(jcr));
690
691    (void)WSA_Init();                        /* Initialize Windows sockets */
692
693    start_watchdog();                        /* Start socket watchdog */
694
695    LockRes();
696    numdir = 0;
697    foreach_res(dir, R_DIRECTOR) {
698       numdir++;
699    }
700    numcon = 0;
701    foreach_res(cons, R_CONSOLE) {
702       numcon++;
703    }
704    UnlockRes();
705
706    if (numdir > 1) {
707       struct sockaddr client_addr;
708       memset(&client_addr, 0, sizeof(client_addr));
709       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
710 try_again:
711       sendit(_("Available Directors:\n"));
712       LockRes();
713       numdir = 0;
714       foreach_res(dir, R_DIRECTOR) {
715          senditf( _("%2d:  %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
716             dir->DIRport);
717       }
718       UnlockRes();
719       if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
720          (void)WSACleanup();               /* Cleanup Windows sockets */
721          return 1;
722       }
723       if (!is_a_number(UA_sock->msg)) {
724          senditf(_("%s is not a number. You must enter a number between 1 and %d\n"), 
725                  UA_sock->msg, numdir);
726          goto try_again;
727       }
728       item = atoi(UA_sock->msg);
729       if (item < 0 || item > numdir) {
730          senditf(_("You must enter a number between 1 and %d\n"), numdir);
731          goto try_again;
732       }
733       term_bsock(UA_sock);
734       LockRes();
735       for (i=0; i<item; i++) {
736          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
737       }
738       /* Look for a console linked to this director */
739       for (i=0; i<numcon; i++) {
740          cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
741          if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
742             break;
743          }
744          cons = NULL;
745       }
746       /* Look for the first non-linked console */
747       if (cons == NULL) {
748          for (i=0; i<numcon; i++) {
749             cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
750             if (cons->director == NULL)
751                break;
752             cons = NULL;
753         }
754       }
755       UnlockRes();
756    }
757    /* If no director, take first one */
758    if (!dir) {
759       LockRes();
760       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
761       UnlockRes();
762    }
763    /* If no console, take first one */
764    if (!cons) {
765       LockRes();
766       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
767       UnlockRes();
768    }
769
770    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
771
772    char buf[1024];
773    /* Initialize Console TLS context */
774    if (cons && (cons->tls_enable || cons->tls_require)) {
775       /* Generate passphrase prompt */
776       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
777
778       /* Initialize TLS context:
779        * Args: CA certfile, CA certdir, Certfile, Keyfile,
780        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer   
781        */
782       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
783          cons->tls_ca_certdir, cons->tls_certfile,
784          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
785
786       if (!cons->tls_ctx) {
787          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
788             dir->hdr.name);
789          terminate_console(0);
790          return 1;
791       }
792    }
793
794    /* Initialize Director TLS context */
795    if (dir->tls_enable || dir->tls_require) {
796       /* Generate passphrase prompt */
797       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
798
799       /* Initialize TLS context:
800        * Args: CA certfile, CA certdir, Certfile, Keyfile,
801        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
802       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
803          dir->tls_ca_certdir, dir->tls_certfile,
804          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
805
806       if (!dir->tls_ctx) {
807          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
808             dir->hdr.name);
809          terminate_console(0);
810          return 1;
811       }
812    }
813
814    if (dir->heartbeat_interval) {
815       heart_beat = dir->heartbeat_interval;
816    } else if (cons) {
817       heart_beat = cons->heartbeat_interval;
818    } else {
819       heart_beat = 0;
820    }
821    UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
822                           NULL, dir->DIRport, 0);
823    if (UA_sock == NULL) {
824       terminate_console(0);
825       return 1;
826    }
827    jcr.dir_bsock = UA_sock;
828
829    /* If cons==NULL, default console will be used */
830    if (!authenticate_director(&jcr, dir, cons)) {
831       terminate_console(0);
832       return 1;
833    }
834
835    Dmsg0(40, "Opened connection with Director daemon\n");
836
837    sendit(_("Enter a period to cancel a command.\n"));
838
839    /* Read/Update history file if HOME exists */
840    POOL_MEM history_file;
841
842    /* Run commands in ~/.bconsolerc if any */
843    char *env = getenv("HOME");
844    if (env) {
845       FILE *fd;
846       pm_strcpy(&UA_sock->msg, env);
847       pm_strcat(&UA_sock->msg, "/.bconsolerc");
848       fd = fopen(UA_sock->msg, "rb");
849       if (fd) {
850          read_and_process_input(fd, UA_sock);
851          fclose(fd);
852       }
853
854       pm_strcpy(history_file, env);
855       pm_strcat(history_file, "/.bconsole_history");
856       console_init_history(history_file.c_str());
857    }
858
859    read_and_process_input(stdin, UA_sock);
860
861    if (UA_sock) {
862       UA_sock->signal(BNET_TERMINATE); /* send EOF */
863       UA_sock->close();
864    }
865
866    if (env) {
867       console_update_history(history_file.c_str());
868    }
869
870    terminate_console(0);
871    return 0;
872 }
873
874 /* Cleanup and then exit */
875 static void terminate_console(int sig)
876 {
877
878    static bool already_here = false;
879
880    if (already_here) {                /* avoid recursive temination problems */
881       exit(1);
882    }
883    already_here = true;
884    stop_watchdog();
885    config->free_resources();
886    free(config);
887    config = NULL;
888    cleanup_crypto();
889    free_pool_memory(args);
890    if (!no_conio) {
891       con_term();
892    }
893    (void)WSACleanup();               /* Cleanup Windows sockets */
894    lmgr_cleanup_main();
895
896    if (sig != 0) {
897       exit(1);
898    }
899    return;
900 }
901
902 /*
903  * Make a quick check to see that we have all the
904  * resources needed.
905  */
906 static int check_resources()
907 {
908    bool OK = true;
909    DIRRES *director;
910    bool tls_needed;
911
912    LockRes();
913
914    numdir = 0;
915    foreach_res(director, R_DIRECTOR) {
916
917       numdir++;
918       /* tls_require implies tls_enable */
919       if (director->tls_require) {
920          if (have_tls) {
921             director->tls_enable = true;
922          } else {
923             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
924             OK = false;
925             continue;
926          }
927       }
928       tls_needed = director->tls_enable || director->tls_authenticate;
929
930       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
931          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
932                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
933                              " At least one CA certificate store is required.\n"),
934                              director->hdr.name, configfile);
935          OK = false;
936       }
937    }
938    
939    if (numdir == 0) {
940       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
941                           "Without that I don't how to speak to the Director :-(\n"), configfile);
942       OK = false;
943    }
944
945    CONRES *cons;
946    /* Loop over Consoles */
947    foreach_res(cons, R_CONSOLE) {
948       /* tls_require implies tls_enable */
949       if (cons->tls_require) {
950          if (have_tls) {
951             cons->tls_enable = true;
952          } else {
953             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
954             OK = false;
955             continue;
956          }
957       }
958       tls_needed = cons->tls_enable || cons->tls_authenticate;
959       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
960          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
961                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
962                              cons->hdr.name, configfile);
963          OK = false;
964       }
965    }
966
967    UnlockRes();
968
969    return OK;
970 }
971
972 static int versioncmd(FILE *input, BSOCK *UA_sock)
973 {
974    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
975       HOST_OS, DISTNAME, DISTVER);
976    return 1;
977 }
978
979 static int inputcmd(FILE *input, BSOCK *UA_sock)
980 {
981    FILE *fd;
982
983    if (argc > 2) {
984       sendit(_("Too many arguments on input command.\n"));
985       return 1;
986    }
987    if (argc == 1) {
988       sendit(_("First argument to input command must be a filename.\n"));
989       return 1;
990    }
991    fd = fopen(argk[1], "rb");
992    if (!fd) {
993       berrno be;
994       senditf(_("Cannot open file %s for input. ERR=%s\n"),
995          argk[1], be.bstrerror());
996       return 1;
997    }
998    read_and_process_input(fd, UA_sock);
999    fclose(fd);
1000    return 1;
1001 }
1002
1003 /* Send output to both termina and specified file */
1004 static int teecmd(FILE *input, BSOCK *UA_sock)
1005 {
1006    teeout = true;
1007    return do_outputcmd(input, UA_sock);
1008 }
1009
1010 /* Send output to specified "file" */
1011 static int outputcmd(FILE *input, BSOCK *UA_sock)
1012 {
1013    teeout = false;
1014    return do_outputcmd(input, UA_sock);
1015 }
1016
1017
1018 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1019 {
1020    FILE *fd;
1021    const char *mode = "a+b";
1022
1023    if (argc > 3) {
1024       sendit(_("Too many arguments on output/tee command.\n"));
1025       return 1;
1026    }
1027    if (argc == 1) {
1028       if (output != stdout) {
1029          fclose(output);
1030          output = stdout;
1031          teeout = false;
1032       }
1033       return 1;
1034    }
1035    if (argc == 3) {
1036       mode = argk[2];
1037    }
1038    fd = fopen(argk[1], mode);
1039    if (!fd) {
1040       berrno be;
1041       senditf(_("Cannot open file %s for output. ERR=%s\n"),
1042          argk[1], be.bstrerror(errno));
1043       return 1;
1044    }
1045    output = fd;
1046    return 1;
1047 }
1048
1049 /*
1050  * exec "some-command" [wait-seconds]
1051 */
1052 static int execcmd(FILE *input, BSOCK *UA_sock)
1053 {
1054    BPIPE *bpipe;
1055    char line[5000];
1056    int stat;
1057    int wait = 0;
1058
1059    if (argc > 3) {
1060       sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1061       return 1;
1062    }
1063    if (argc == 3) {
1064       wait = atoi(argk[2]);
1065    }
1066    bpipe = open_bpipe(argk[1], wait, "r");
1067    if (!bpipe) {
1068       berrno be;
1069       senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1070          argk[1], be.bstrerror(errno));
1071       return 1;
1072    }
1073   
1074    while (fgets(line, sizeof(line), bpipe->rfd)) {
1075       senditf("%s", line);
1076    }
1077    stat = close_bpipe(bpipe);
1078    if (stat != 0) {
1079       berrno be;
1080       be.set_errno(stat);
1081      senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1082    }
1083    return 1;
1084 }
1085
1086
1087 static int echocmd(FILE *input, BSOCK *UA_sock)
1088 {
1089    for (int i=1; i < argc; i++) {
1090       senditf("%s ", argk[i]);
1091    }
1092    sendit("\n");
1093    return 1;
1094 }
1095
1096 static int quitcmd(FILE *input, BSOCK *UA_sock)
1097 {
1098    return 0;
1099 }
1100
1101 static int helpcmd(FILE *input, BSOCK *UA_sock)
1102 {
1103    int i;
1104    for (i=0; i<comsize; i++) { 
1105       senditf("  %-10s %s\n", commands[i].key, commands[i].help);
1106    }
1107    return 1;   
1108 }
1109
1110
1111 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1112 {
1113    if (argc > 1) {
1114       sleep(atoi(argk[1]));
1115    }
1116    return 1;
1117 }
1118
1119
1120 static int timecmd(FILE *input, BSOCK *UA_sock)
1121 {
1122    char sdt[50];
1123    time_t ttime = time(NULL);
1124    struct tm tm;
1125    (void)localtime_r(&ttime, &tm);
1126    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1127    sendit("\n");
1128    return 1;
1129 }
1130
1131 /*
1132  * Send a line to the output file and or the terminal
1133  */
1134 void senditf(const char *fmt,...)
1135 {
1136    char buf[3000];
1137    va_list arg_ptr;
1138
1139    va_start(arg_ptr, fmt);
1140    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1141    va_end(arg_ptr);
1142    sendit(buf);
1143 }
1144
1145 void sendit(const char *buf)
1146 {
1147 #ifdef CONIO_FIX
1148    char obuf[3000];
1149    if (output == stdout || teeout) {
1150       const char *p, *q;
1151       /*
1152        * Here, we convert every \n into \r\n because the
1153        *  terminal is in raw mode when we are using
1154        *  conio.
1155        */
1156       for (p=q=buf; (p=strchr(q, '\n')); ) {
1157          int len = p - q;
1158          if (len > 0) {
1159             memcpy(obuf, q, len);
1160          }
1161          memcpy(obuf+len, "\r\n", 3);
1162          q = ++p;                    /* point after \n */
1163          fputs(obuf, output);
1164       }
1165       if (*q) {
1166          fputs(q, output);
1167       }
1168       fflush(output);
1169    }
1170    if (output != stdout) {
1171       fputs(buf, output);
1172    }
1173 #else
1174
1175    fputs(buf, output);
1176    fflush(output);
1177    if (teeout) {
1178       fputs(buf, stdout);
1179       fflush(stdout);
1180    }
1181 #endif
1182 }