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