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