]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
kes Add additional info to some SD messages.
[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    if (sig != 0) {
874       exit(1);
875    }
876    return;
877 }
878
879 /*
880  * Make a quick check to see that we have all the
881  * resources needed.
882  */
883 static int check_resources()
884 {
885    bool OK = true;
886    DIRRES *director;
887    bool tls_needed;
888
889    LockRes();
890
891    numdir = 0;
892    foreach_res(director, R_DIRECTOR) {
893
894       numdir++;
895       /* tls_require implies tls_enable */
896       if (director->tls_require) {
897          if (have_tls) {
898             director->tls_enable = true;
899          } else {
900             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
901             OK = false;
902             continue;
903          }
904       }
905       tls_needed = director->tls_enable || director->tls_authenticate;
906
907       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
908          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
909                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
910                              " At least one CA certificate store is required.\n"),
911                              director->hdr.name, configfile);
912          OK = false;
913       }
914    }
915    
916    if (numdir == 0) {
917       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
918                           "Without that I don't how to speak to the Director :-(\n"), configfile);
919       OK = false;
920    }
921
922    CONRES *cons;
923    /* Loop over Consoles */
924    foreach_res(cons, R_CONSOLE) {
925       /* tls_require implies tls_enable */
926       if (cons->tls_require) {
927          if (have_tls) {
928             cons->tls_enable = true;
929          } else {
930             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
931             OK = false;
932             continue;
933          }
934       }
935       tls_needed = cons->tls_enable || cons->tls_authenticate;
936       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
937          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
938                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
939                              cons->hdr.name, configfile);
940          OK = false;
941       }
942    }
943
944    UnlockRes();
945
946    return OK;
947 }
948
949 static int versioncmd(FILE *input, BSOCK *UA_sock)
950 {
951    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
952       HOST_OS, DISTNAME, DISTVER);
953    return 1;
954 }
955
956 static int inputcmd(FILE *input, BSOCK *UA_sock)
957 {
958    FILE *fd;
959
960    if (argc > 2) {
961       sendit(_("Too many arguments on input command.\n"));
962       return 1;
963    }
964    if (argc == 1) {
965       sendit(_("First argument to input command must be a filename.\n"));
966       return 1;
967    }
968    fd = fopen(argk[1], "rb");
969    if (!fd) {
970       berrno be;
971       senditf(_("Cannot open file %s for input. ERR=%s\n"),
972          argk[1], be.bstrerror());
973       return 1;
974    }
975    read_and_process_input(fd, UA_sock);
976    fclose(fd);
977    return 1;
978 }
979
980 /* Send output to both termina and specified file */
981 static int teecmd(FILE *input, BSOCK *UA_sock)
982 {
983    teeout = true;
984    return do_outputcmd(input, UA_sock);
985 }
986
987 /* Send output to specified "file" */
988 static int outputcmd(FILE *input, BSOCK *UA_sock)
989 {
990    teeout = false;
991    return do_outputcmd(input, UA_sock);
992 }
993
994
995 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
996 {
997    FILE *fd;
998    const char *mode = "a+b";
999
1000    if (argc > 3) {
1001       sendit(_("Too many arguments on output/tee command.\n"));
1002       return 1;
1003    }
1004    if (argc == 1) {
1005       if (output != stdout) {
1006          fclose(output);
1007          output = stdout;
1008          teeout = false;
1009       }
1010       return 1;
1011    }
1012    if (argc == 3) {
1013       mode = argk[2];
1014    }
1015    fd = fopen(argk[1], mode);
1016    if (!fd) {
1017       berrno be;
1018       senditf(_("Cannot open file %s for output. ERR=%s\n"),
1019          argk[1], be.bstrerror(errno));
1020       return 1;
1021    }
1022    output = fd;
1023    return 1;
1024 }
1025
1026 /*
1027  * exec "some-command" [wait-seconds]
1028 */
1029 static int execcmd(FILE *input, BSOCK *UA_sock)
1030 {
1031    BPIPE *bpipe;
1032    char line[5000];
1033    int stat;
1034    int wait = 0;
1035
1036    if (argc > 3) {
1037       sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1038       return 1;
1039    }
1040    if (argc == 3) {
1041       wait = atoi(argk[2]);
1042    }
1043    bpipe = open_bpipe(argk[1], wait, "r");
1044    if (!bpipe) {
1045       berrno be;
1046       senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1047          argk[1], be.bstrerror(errno));
1048       return 1;
1049    }
1050   
1051    while (fgets(line, sizeof(line), bpipe->rfd)) {
1052       senditf("%s", line);
1053    }
1054    stat = close_bpipe(bpipe);
1055    if (stat != 0) {
1056       berrno be;
1057       be.set_errno(stat);
1058      senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1059    }
1060    return 1;
1061 }
1062
1063
1064 static int echocmd(FILE *input, BSOCK *UA_sock)
1065 {
1066    for (int i=1; i < argc; i++) {
1067       senditf("%s", argk[i]);
1068       sendit(" ");
1069    }
1070    sendit("\n");
1071    return 1;
1072 }
1073
1074 static int quitcmd(FILE *input, BSOCK *UA_sock)
1075 {
1076    return 0;
1077 }
1078
1079 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1080 {
1081    if (argc > 1) {
1082       sleep(atoi(argk[1]));
1083    }
1084    return 1;
1085 }
1086
1087
1088 static int timecmd(FILE *input, BSOCK *UA_sock)
1089 {
1090    char sdt[50];
1091    time_t ttime = time(NULL);
1092    struct tm tm;
1093    (void)localtime_r(&ttime, &tm);
1094    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1095    sendit("\n");
1096    return 1;
1097 }
1098
1099 /*
1100  * Send a line to the output file and or the terminal
1101  */
1102 void senditf(const char *fmt,...)
1103 {
1104    char buf[3000];
1105    va_list arg_ptr;
1106
1107    va_start(arg_ptr, fmt);
1108    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1109    va_end(arg_ptr);
1110    sendit(buf);
1111 }
1112
1113 void sendit(const char *buf)
1114 {
1115 #ifdef CONIO_FIX
1116    char obuf[3000];
1117    if (output == stdout || teeout) {
1118       const char *p, *q;
1119       /*
1120        * Here, we convert every \n into \r\n because the
1121        *  terminal is in raw mode when we are using
1122        *  conio.
1123        */
1124       for (p=q=buf; (p=strchr(q, '\n')); ) {
1125          int len = p - q;
1126          if (len > 0) {
1127             memcpy(obuf, q, len);
1128          }
1129          memcpy(obuf+len, "\r\n", 3);
1130          q = ++p;                    /* point after \n */
1131          fputs(obuf, output);
1132       }
1133       if (*q) {
1134          fputs(q, output);
1135       }
1136       fflush(output);
1137    }
1138    if (output != stdout) {
1139       fputs(buf, output);
1140    }
1141 #else
1142
1143    fputs(buf, output);
1144    fflush(output);
1145    if (teeout) {
1146       fputs(buf, stdout);
1147       fflush(stdout);
1148    }
1149 #endif
1150 }