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