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