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