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