]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
kes Added the following to provide solutions to the plugin/Accurate
[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 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 #include "readline.h"
348 #include "history.h"
349
350 static char eol = '\0';
351 static int eolcmd(FILE *input, BSOCK *UA_sock)
352 {
353    if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
354       eol = argk[1][0];
355    } else if (argc == 1) {
356       eol = '\0';
357    } else {
358       sendit(_("Illegal separator character.\n"));
359    }
360    return 1;
361 }
362
363 int
364 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
365 {
366    static char *line = NULL;
367    static char *next = NULL;
368    static int do_history = 0;
369    char *command;
370
371    if (line == NULL) {
372       do_history = 0;
373       rl_catch_signals = 0;              /* do it ourselves */
374       /* Here, readline does ***real*** malloc
375        * so, be we have to use the real free
376        */
377       line = readline((char *)prompt);   /* cast needed for old readlines */
378       if (!line) {
379          exit(1);
380       }
381       strip_trailing_junk(line);
382       command = line;
383    } else if (next) {
384       command = next + 1;
385    } else {
386      sendit(_("Command logic problem\n"));
387      sock->msglen = 0;
388      sock->msg[0] = 0;
389      return 0;
390    }
391
392    /*
393     * Split "line" into multiple commands separated by the eol character.
394     *   Each part is pointed to by "next" until finally it becomes null.
395     */
396    if (eol == '\0') {
397       next = NULL;
398    } else {
399       next = strchr(command, eol);
400       if (next) {
401          *next = '\0';
402       }
403    }
404    if (command != line && isatty(fileno(input))) {
405       senditf("%s%s\n", prompt, command);
406    }
407
408    sock->msglen = pm_strcpy(&sock->msg, command);
409    if (sock->msglen) {
410       do_history++;
411    }
412
413    if (!next) {
414       if (do_history) {
415         add_history(line);
416       }
417       actuallyfree(line);       /* allocated by readline() malloc */
418       line = NULL;
419    }
420    return 1;
421 }
422
423 #else /* no readline, do it ourselves */
424
425 #ifdef HAVE_CONIO
426 static bool bisatty(int fd)
427 {
428    if (no_conio) {
429       return false;
430    }
431    return isatty(fd);
432 }
433 #endif
434
435 /*
436  *   Returns: 1 if data available
437  *            0 if timeout
438  *           -1 if error
439  */
440 static int
441 wait_for_data(int fd, int sec)
442 {
443 #if defined(HAVE_WIN32)
444    return 1;
445 #else
446    fd_set fdset;
447    struct timeval tv;
448
449    tv.tv_sec = sec;
450    tv.tv_usec = 0;
451    for ( ;; ) {
452       FD_ZERO(&fdset);
453       FD_SET((unsigned)fd, &fdset);
454       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
455       case 0:                         /* timeout */
456          return 0;
457       case -1:
458          if (errno == EINTR || errno == EAGAIN) {
459             continue;
460          }
461          return -1;                  /* error return */
462       default:
463          return 1;
464       }
465    }
466 #endif
467 }
468
469 /*
470  * Get next input command from terminal.
471  *
472  *   Returns: 1 if got input
473  *            0 if timeout
474  *           -1 if EOF or error
475  */
476 int
477 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
478 {
479    int len;
480    if (!stop) {
481       if (output == stdout || teeout) {
482          sendit(prompt);
483       }
484    }
485 again:
486    switch (wait_for_data(fileno(input), sec)) {
487    case 0:
488       return 0;                    /* timeout */
489    case -1:
490       return -1;                   /* error */
491    default:
492       len = sizeof_pool_memory(sock->msg) - 1;
493       if (stop) {
494          sleep(1);
495          goto again;
496       }
497 #ifdef HAVE_CONIO
498       if (bisatty(fileno(input))) {
499          input_line(sock->msg, len);
500          break;
501       }
502 #endif
503 #ifdef HAVE_WIN32 /* use special console for input on win32 */
504       if (input == stdin) {
505          if (win32_cgets(sock->msg, len) == NULL) {
506             return -1;
507          }
508       }
509       else
510 #endif
511       if (fgets(sock->msg, len, input) == NULL) {
512          return -1;
513
514       }
515       break;
516    }
517    if (usrbrk()) {
518       clrbrk();
519    }
520    strip_trailing_junk(sock->msg);
521    sock->msglen = strlen(sock->msg);
522    return 1;
523 }
524
525 #endif /* ! HAVE_READLINE */
526
527
528 static int console_update_history(const char *histfile)
529 {
530    int ret=0;
531
532 #ifdef HAVE_READLINE
533 /* first, try to truncate the history file, and if it
534  * fail, the file is probably not present, and we
535  * can use write_history to create it
536  */
537
538    if (history_truncate_file(histfile, 100) == 0) {
539       ret = append_history(history_length, histfile);
540    } else {
541       ret = write_history(histfile);
542    }
543
544 #endif
545
546    return ret;
547 }
548
549 static int console_init_history(const char *histfile)
550 {
551    int ret=0;
552
553 #ifdef HAVE_READLINE
554
555    using_history();
556    ret = read_history(histfile);
557
558 #endif
559
560    return ret;
561 }
562
563 /*********************************************************************
564  *
565  *         Main Bacula Console -- User Interface Program
566  *
567  */
568 int main(int argc, char *argv[])
569 {
570    int ch, i, item;
571    bool no_signals = false;
572    bool test_config = false;
573    JCR jcr;
574    utime_t heart_beat;
575
576    setlocale(LC_ALL, "");
577    bindtextdomain("bacula", LOCALEDIR);
578    textdomain("bacula");
579
580    init_stack_dump();
581    my_name_is(argc, argv, "bconsole");
582    init_msg(NULL, NULL);
583    working_directory = "/tmp";
584    args = get_pool_memory(PM_FNAME);
585
586    while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
587       switch (ch) {
588       case 'c':                    /* configuration file */
589          if (configfile != NULL) {
590             free(configfile);
591          }
592          configfile = bstrdup(optarg);
593          break;
594
595       case 'd':
596          if (*optarg == 't') {
597             dbg_timestamp = true;
598          } else {
599             debug_level = atoi(optarg);
600             if (debug_level <= 0) {
601                debug_level = 1;
602             }
603          }
604          break;
605
606       case 'n':                    /* no conio */
607          no_conio = true;
608          break;
609
610       case 's':                    /* turn off signals */
611          no_signals = true;
612          break;
613
614       case 't':
615          test_config = true;
616          break;
617
618       case '?':
619       default:
620          usage();
621          exit(1);
622       }
623    }
624    argc -= optind;
625    argv += optind;
626
627    if (!no_signals) {
628       init_signals(terminate_console);
629    }
630
631
632 #if !defined(HAVE_WIN32)
633    /* Override Bacula default signals */
634    signal(SIGQUIT, SIG_IGN);
635    signal(SIGTSTP, got_sigstop);
636    signal(SIGCONT, got_sigcontinue);
637    signal(SIGTTIN, got_sigtin);
638    signal(SIGTTOU, got_sigtout);
639    trapctlc();
640 #endif
641
642    OSDependentInit();
643
644    if (argc) {
645       usage();
646       exit(1);
647    }
648
649    if (configfile == NULL) {
650       configfile = bstrdup(CONFIG_FILE);
651    }
652
653    config = new_config_parser();
654    parse_cons_config(config, configfile, M_ERROR_TERM);
655
656    if (init_crypto() != 0) {
657       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
658    }
659
660    if (!check_resources()) {
661       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
662    }
663
664    if (!no_conio) {
665       con_init(stdin);
666    }
667
668    if (test_config) {
669       terminate_console(0);
670       exit(0);
671    }
672
673    memset(&jcr, 0, sizeof(jcr));
674
675    (void)WSA_Init();                        /* Initialize Windows sockets */
676
677    LockRes();
678    numdir = 0;
679    foreach_res(dir, R_DIRECTOR) {
680       numdir++;
681    }
682    numcon = 0;
683    foreach_res(cons, R_CONSOLE) {
684       numcon++;
685    }
686    UnlockRes();
687
688    if (numdir > 1) {
689       struct sockaddr client_addr;
690       memset(&client_addr, 0, sizeof(client_addr));
691       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
692 try_again:
693       sendit(_("Available Directors:\n"));
694       LockRes();
695       numdir = 0;
696       foreach_res(dir, R_DIRECTOR) {
697          senditf( _("%2d:  %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
698             dir->DIRport);
699       }
700       UnlockRes();
701       if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
702          (void)WSACleanup();               /* Cleanup Windows sockets */
703          return 1;
704       }
705       if (!is_a_number(UA_sock->msg)) {
706          senditf(_("%s is not a number. You must enter a number between 1 and %d\n"), 
707                  UA_sock->msg, numdir);
708          goto try_again;
709       }
710       item = atoi(UA_sock->msg);
711       if (item < 0 || item > numdir) {
712          senditf(_("You must enter a number between 1 and %d\n"), numdir);
713          goto try_again;
714       }
715       term_bsock(UA_sock);
716       LockRes();
717       for (i=0; i<item; i++) {
718          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
719       }
720       /* Look for a console linked to this director */
721       for (i=0; i<numcon; i++) {
722          cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
723          if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
724             break;
725          }
726          cons = NULL;
727       }
728       /* Look for the first non-linked console */
729       if (cons == NULL) {
730          for (i=0; i<numcon; i++) {
731             cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
732             if (cons->director == NULL)
733                break;
734             cons = NULL;
735         }
736       }
737       UnlockRes();
738    }
739    /* If no director, take first one */
740    if (!dir) {
741       LockRes();
742       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
743       UnlockRes();
744    }
745    /* If no console, take first one */
746    if (!cons) {
747       LockRes();
748       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
749       UnlockRes();
750    }
751
752    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
753
754    char buf[1024];
755    /* Initialize Console TLS context */
756    if (cons && (cons->tls_enable || cons->tls_require)) {
757       /* Generate passphrase prompt */
758       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
759
760       /* Initialize TLS context:
761        * Args: CA certfile, CA certdir, Certfile, Keyfile,
762        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer   
763        */
764       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
765          cons->tls_ca_certdir, cons->tls_certfile,
766          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
767
768       if (!cons->tls_ctx) {
769          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
770             dir->hdr.name);
771          terminate_console(0);
772          return 1;
773       }
774    }
775
776    /* Initialize Director TLS context */
777    if (dir->tls_enable || dir->tls_require) {
778       /* Generate passphrase prompt */
779       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
780
781       /* Initialize TLS context:
782        * Args: CA certfile, CA certdir, Certfile, Keyfile,
783        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
784       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
785          dir->tls_ca_certdir, dir->tls_certfile,
786          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
787
788       if (!dir->tls_ctx) {
789          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
790             dir->hdr.name);
791          terminate_console(0);
792          return 1;
793       }
794    }
795
796    if (dir->heartbeat_interval) {
797       heart_beat = dir->heartbeat_interval;
798    } else if (cons) {
799       heart_beat = cons->heartbeat_interval;
800    } else {
801       heart_beat = 0;
802    }
803    UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
804                           NULL, dir->DIRport, 0);
805    if (UA_sock == NULL) {
806       terminate_console(0);
807       return 1;
808    }
809    jcr.dir_bsock = UA_sock;
810
811    /* If cons==NULL, default console will be used */
812    if (!authenticate_director(&jcr, dir, cons)) {
813       terminate_console(0);
814       return 1;
815    }
816
817    Dmsg0(40, "Opened connection with Director daemon\n");
818
819    sendit(_("Enter a period to cancel a command.\n"));
820
821    /* Read/Update history file if HOME exists */
822    POOL_MEM history_file;
823
824    /* Run commands in ~/.bconsolerc if any */
825    char *env = getenv("HOME");
826    if (env) {
827       FILE *fd;
828       pm_strcpy(&UA_sock->msg, env);
829       pm_strcat(&UA_sock->msg, "/.bconsolerc");
830       fd = fopen(UA_sock->msg, "rb");
831       if (fd) {
832          read_and_process_input(fd, UA_sock);
833          fclose(fd);
834       }
835
836       pm_strcpy(history_file, env);
837       pm_strcat(history_file, "/.bconsole_history");
838       console_init_history(history_file.c_str());
839    }
840
841    read_and_process_input(stdin, UA_sock);
842
843    if (UA_sock) {
844       UA_sock->signal(BNET_TERMINATE); /* send EOF */
845       UA_sock->close();
846    }
847
848    if (env) {
849       console_update_history(history_file.c_str());
850    }
851
852    terminate_console(0);
853    return 0;
854 }
855
856 /* Cleanup and then exit */
857 static void terminate_console(int sig)
858 {
859
860    static bool already_here = false;
861
862    if (already_here) {                /* avoid recursive temination problems */
863       exit(1);
864    }
865    already_here = true;
866    config->free_resources();
867    free(config);
868    config = NULL;
869    cleanup_crypto();
870    free_pool_memory(args);
871    if (!no_conio) {
872       con_term();
873    }
874    (void)WSACleanup();               /* Cleanup Windows sockets */
875    lmgr_cleanup_main();
876
877    if (sig != 0) {
878       exit(1);
879    }
880    return;
881 }
882
883 /*
884  * Make a quick check to see that we have all the
885  * resources needed.
886  */
887 static int check_resources()
888 {
889    bool OK = true;
890    DIRRES *director;
891    bool tls_needed;
892
893    LockRes();
894
895    numdir = 0;
896    foreach_res(director, R_DIRECTOR) {
897
898       numdir++;
899       /* tls_require implies tls_enable */
900       if (director->tls_require) {
901          if (have_tls) {
902             director->tls_enable = true;
903          } else {
904             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
905             OK = false;
906             continue;
907          }
908       }
909       tls_needed = director->tls_enable || director->tls_authenticate;
910
911       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
912          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
913                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
914                              " At least one CA certificate store is required.\n"),
915                              director->hdr.name, configfile);
916          OK = false;
917       }
918    }
919    
920    if (numdir == 0) {
921       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
922                           "Without that I don't how to speak to the Director :-(\n"), configfile);
923       OK = false;
924    }
925
926    CONRES *cons;
927    /* Loop over Consoles */
928    foreach_res(cons, R_CONSOLE) {
929       /* tls_require implies tls_enable */
930       if (cons->tls_require) {
931          if (have_tls) {
932             cons->tls_enable = true;
933          } else {
934             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
935             OK = false;
936             continue;
937          }
938       }
939       tls_needed = cons->tls_enable || cons->tls_authenticate;
940       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
941          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
942                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
943                              cons->hdr.name, configfile);
944          OK = false;
945       }
946    }
947
948    UnlockRes();
949
950    return OK;
951 }
952
953 static int versioncmd(FILE *input, BSOCK *UA_sock)
954 {
955    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
956       HOST_OS, DISTNAME, DISTVER);
957    return 1;
958 }
959
960 static int inputcmd(FILE *input, BSOCK *UA_sock)
961 {
962    FILE *fd;
963
964    if (argc > 2) {
965       sendit(_("Too many arguments on input command.\n"));
966       return 1;
967    }
968    if (argc == 1) {
969       sendit(_("First argument to input command must be a filename.\n"));
970       return 1;
971    }
972    fd = fopen(argk[1], "rb");
973    if (!fd) {
974       berrno be;
975       senditf(_("Cannot open file %s for input. ERR=%s\n"),
976          argk[1], be.bstrerror());
977       return 1;
978    }
979    read_and_process_input(fd, UA_sock);
980    fclose(fd);
981    return 1;
982 }
983
984 /* Send output to both termina and specified file */
985 static int teecmd(FILE *input, BSOCK *UA_sock)
986 {
987    teeout = true;
988    return do_outputcmd(input, UA_sock);
989 }
990
991 /* Send output to specified "file" */
992 static int outputcmd(FILE *input, BSOCK *UA_sock)
993 {
994    teeout = false;
995    return do_outputcmd(input, UA_sock);
996 }
997
998
999 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1000 {
1001    FILE *fd;
1002    const char *mode = "a+b";
1003
1004    if (argc > 3) {
1005       sendit(_("Too many arguments on output/tee command.\n"));
1006       return 1;
1007    }
1008    if (argc == 1) {
1009       if (output != stdout) {
1010          fclose(output);
1011          output = stdout;
1012          teeout = false;
1013       }
1014       return 1;
1015    }
1016    if (argc == 3) {
1017       mode = argk[2];
1018    }
1019    fd = fopen(argk[1], mode);
1020    if (!fd) {
1021       berrno be;
1022       senditf(_("Cannot open file %s for output. ERR=%s\n"),
1023          argk[1], be.bstrerror(errno));
1024       return 1;
1025    }
1026    output = fd;
1027    return 1;
1028 }
1029
1030 /*
1031  * exec "some-command" [wait-seconds]
1032 */
1033 static int execcmd(FILE *input, BSOCK *UA_sock)
1034 {
1035    BPIPE *bpipe;
1036    char line[5000];
1037    int stat;
1038    int wait = 0;
1039
1040    if (argc > 3) {
1041       sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1042       return 1;
1043    }
1044    if (argc == 3) {
1045       wait = atoi(argk[2]);
1046    }
1047    bpipe = open_bpipe(argk[1], wait, "r");
1048    if (!bpipe) {
1049       berrno be;
1050       senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1051          argk[1], be.bstrerror(errno));
1052       return 1;
1053    }
1054   
1055    while (fgets(line, sizeof(line), bpipe->rfd)) {
1056       senditf("%s", line);
1057    }
1058    stat = close_bpipe(bpipe);
1059    if (stat != 0) {
1060       berrno be;
1061       be.set_errno(stat);
1062      senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1063    }
1064    return 1;
1065 }
1066
1067
1068 static int echocmd(FILE *input, BSOCK *UA_sock)
1069 {
1070    for (int i=1; i < argc; i++) {
1071       senditf("%s", argk[i]);
1072       sendit(" ");
1073    }
1074    sendit("\n");
1075    return 1;
1076 }
1077
1078 static int quitcmd(FILE *input, BSOCK *UA_sock)
1079 {
1080    return 0;
1081 }
1082
1083 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1084 {
1085    if (argc > 1) {
1086       sleep(atoi(argk[1]));
1087    }
1088    return 1;
1089 }
1090
1091
1092 static int timecmd(FILE *input, BSOCK *UA_sock)
1093 {
1094    char sdt[50];
1095    time_t ttime = time(NULL);
1096    struct tm tm;
1097    (void)localtime_r(&ttime, &tm);
1098    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1099    sendit("\n");
1100    return 1;
1101 }
1102
1103 /*
1104  * Send a line to the output file and or the terminal
1105  */
1106 void senditf(const char *fmt,...)
1107 {
1108    char buf[3000];
1109    va_list arg_ptr;
1110
1111    va_start(arg_ptr, fmt);
1112    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1113    va_end(arg_ptr);
1114    sendit(buf);
1115 }
1116
1117 void sendit(const char *buf)
1118 {
1119 #ifdef CONIO_FIX
1120    char obuf[3000];
1121    if (output == stdout || teeout) {
1122       const char *p, *q;
1123       /*
1124        * Here, we convert every \n into \r\n because the
1125        *  terminal is in raw mode when we are using
1126        *  conio.
1127        */
1128       for (p=q=buf; (p=strchr(q, '\n')); ) {
1129          int len = p - q;
1130          if (len > 0) {
1131             memcpy(obuf, q, len);
1132          }
1133          memcpy(obuf+len, "\r\n", 3);
1134          q = ++p;                    /* point after \n */
1135          fputs(obuf, output);
1136       }
1137       if (*q) {
1138          fputs(q, output);
1139       }
1140       fflush(output);
1141    }
1142    if (output != stdout) {
1143       fputs(buf, output);
1144    }
1145 #else
1146
1147    fputs(buf, output);
1148    fflush(output);
1149    if (teeout) {
1150       fputs(buf, stdout);
1151       fflush(stdout);
1152    }
1153 #endif
1154 }