3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
10 Copyright (C) 2000-2006 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
25 #include "console_conf.h"
35 #define con_set_zed_keys();
42 #define isatty(fd) (fd==0)
45 /* Exported variables */
47 extern int rl_catch_signals;
49 /* Imported functions */
50 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
52 /* Forward referenced functions */
53 static void terminate_console(int sig);
54 static int check_resources();
55 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
56 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
57 void senditf(const char *fmt, ...);
58 void sendit(const char *buf);
60 extern "C" void got_sigstop(int sig);
61 extern "C" void got_sigcontinue(int sig);
62 extern "C" void got_sigtout(int sig);
63 extern "C" void got_sigtin(int sig);
66 /* Static variables */
67 static char *configfile = NULL;
68 static BSOCK *UA_sock = NULL;
70 static FILE *output = stdout;
71 static bool teeout = false; /* output to output and stdout */
72 static bool stop = false;
76 static char *argk[MAX_CMD_ARGS];
77 static char *argv[MAX_CMD_ARGS];
80 /* Command prototypes */
81 static int versioncmd(FILE *input, BSOCK *UA_sock);
82 static int inputcmd(FILE *input, BSOCK *UA_sock);
83 static int outputcmd(FILE *input, BSOCK *UA_sock);
84 static int teecmd(FILE *input, BSOCK *UA_sock);
85 static int quitcmd(FILE *input, BSOCK *UA_sock);
86 static int timecmd(FILE *input, BSOCK *UA_sock);
87 static int sleepcmd(FILE *input, BSOCK *UA_sock);
90 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
95 "Copyright (C) 2000-2005 Kern Sibbald\n"
96 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
97 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
98 " -c <file> set configuration file to file\n"
99 " -dnn set debug level to nn\n"
101 " -t test - read configuration and exit\n"
102 " -? print this message.\n"
103 "\n"), HOST_OS, DISTNAME, DISTVER);
108 void got_sigstop(int sig)
114 void got_sigcontinue(int sig)
120 void got_sigtout(int sig)
122 // printf("Got tout\n");
126 void got_sigtin(int sig)
128 // printf("Got tin\n");
132 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
139 * These are the @command
141 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
142 static struct cmdstruct commands[] = {
143 { N_("input"), inputcmd, _("input from file")},
144 { N_("output"), outputcmd, _("output to file")},
145 { N_("quit"), quitcmd, _("quit")},
146 { N_("tee"), teecmd, _("output to file and terminal")},
147 { N_("sleep"), sleepcmd, _("sleep specified time")},
148 { N_("time"), timecmd, _("print current time")},
149 { N_("version"), versioncmd, _("print Console's version")},
150 { N_("exit"), quitcmd, _("exit = quit")},
151 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
153 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
155 static int do_a_command(FILE *input, BSOCK *UA_sock)
166 Dmsg1(120, "Command: %s\n", UA_sock->msg);
172 if (*cmd == '#') { /* comment */
176 for (i=0; i<comsize; i++) { /* search for command */
177 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
178 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
184 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
185 UA_sock->msglen = strlen(UA_sock->msg);
186 sendit(UA_sock->msg);
192 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
194 const char *prompt = "*";
195 bool at_prompt = false;
196 int tty_input = isatty(fileno(input));
200 if (at_prompt) { /* don't prompt multiple times */
207 stat = get_cmd(input, prompt, UA_sock, 30);
215 /* Reading input from a file */
216 int len = sizeof_pool_memory(UA_sock->msg) - 1;
220 if (fgets(UA_sock->msg, len, input) == NULL) {
223 sendit(UA_sock->msg); /* echo to terminal */
224 strip_trailing_junk(UA_sock->msg);
225 UA_sock->msglen = strlen(UA_sock->msg);
230 break; /* error or interrupt */
231 } else if (stat == 0) { /* timeout */
232 if (strcmp(prompt, "*") == 0) {
233 bnet_fsend(UA_sock, ".messages");
239 /* @ => internal command for us */
240 if (UA_sock->msg[0] == '@') {
241 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
242 if (!do_a_command(input, UA_sock)) {
247 if (!bnet_send(UA_sock)) { /* send command */
251 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
254 while ((stat = bnet_recv(UA_sock)) >= 0) {
261 /* Suppress output if running in background or user hit ctl-c */
262 if (!stop && !usrbrk()) {
263 sendit(UA_sock->msg);
274 if (is_bnet_stop(UA_sock)) {
275 break; /* error or term */
276 } else if (stat == BNET_SIGNAL) {
277 if (UA_sock->msglen == BNET_PROMPT) {
280 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
286 * Call-back for reading a passphrase for an encrypted PEM file
287 * This function uses getpass(),
288 * which uses a static buffer and is NOT thread-safe.
290 static int tls_pem_callback(char *buf, int size, const void *userdata)
294 const char *prompt = (const char *)userdata;
296 if (win32_cgets(buf, size) == NULL) {
303 const char *prompt = (const char *)userdata;
306 passwd = getpass(prompt);
307 bstrncpy(buf, passwd, size);
317 /*********************************************************************
319 * Main Bacula Console -- User Interface Program
322 int main(int argc, char *argv[])
325 bool no_signals = false;
326 bool test_config = false;
329 setlocale(LC_ALL, "");
330 bindtextdomain("bacula", LOCALEDIR);
331 textdomain("bacula");
334 my_name_is(argc, argv, "bconsole");
335 init_msg(NULL, NULL);
336 working_directory = "/tmp";
337 args = get_pool_memory(PM_FNAME);
340 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
342 case 'c': /* configuration file */
343 if (configfile != NULL) {
346 configfile = bstrdup(optarg);
350 debug_level = atoi(optarg);
351 if (debug_level <= 0) {
356 case 's': /* turn off signals */
375 init_signals(terminate_console);
379 #if !defined(HAVE_WIN32)
380 /* Override Bacula default signals */
381 signal(SIGQUIT, SIG_IGN);
382 signal(SIGTSTP, got_sigstop);
383 signal(SIGCONT, got_sigcontinue);
384 signal(SIGTTIN, got_sigtin);
385 signal(SIGTTOU, got_sigtout);
397 if (configfile == NULL) {
398 configfile = bstrdup(CONFIG_FILE);
401 parse_config(configfile);
403 if (init_crypto() != 0) {
404 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
407 if (!check_resources()) {
408 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
412 terminate_console(0);
416 memset(&jcr, 0, sizeof(jcr));
418 (void)WSA_Init(); /* Initialize Windows sockets */
421 struct sockaddr client_addr;
422 memset(&client_addr, 0, sizeof(client_addr));
423 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
425 sendit(_("Available Directors:\n"));
428 foreach_res(dir, R_DIRECTOR) {
429 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
433 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
434 (void)WSACleanup(); /* Cleanup Windows sockets */
437 item = atoi(UA_sock->msg);
438 if (item < 0 || item > numdir) {
439 senditf(_("You must enter a number between 1 and %d\n"), numdir);
444 for (i=0; i<item; i++) {
445 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
451 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
456 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
459 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
462 /* Initialize Console TLS context */
463 if (cons && (cons->tls_enable || cons->tls_require)) {
464 /* Generate passphrase prompt */
465 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
467 /* Initialize TLS context:
468 * Args: CA certfile, CA certdir, Certfile, Keyfile,
469 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
470 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
471 cons->tls_ca_certdir, cons->tls_certfile,
472 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
474 if (!cons->tls_ctx) {
475 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
477 terminate_console(0);
483 /* Initialize Director TLS context */
484 if (dir->tls_enable || dir->tls_require) {
485 /* Generate passphrase prompt */
486 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
488 /* Initialize TLS context:
489 * Args: CA certfile, CA certdir, Certfile, Keyfile,
490 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
491 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
492 dir->tls_ca_certdir, dir->tls_certfile,
493 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
496 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
498 terminate_console(0);
503 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
504 NULL, dir->DIRport, 0);
505 if (UA_sock == NULL) {
506 terminate_console(0);
509 jcr.dir_bsock = UA_sock;
511 /* If cons==NULL, default console will be used */
512 if (!authenticate_director(&jcr, dir, cons)) {
513 terminate_console(0);
517 Dmsg0(40, "Opened connection with Director daemon\n");
519 sendit(_("Enter a period to cancel a command.\n"));
521 /* Run commands in ~/.bconsolerc if any */
522 char *env = getenv("HOME");
525 pm_strcpy(&UA_sock->msg, env);
526 pm_strcat(&UA_sock->msg, "/.bconsolerc");
527 fd = fopen(UA_sock->msg, "rb");
529 read_and_process_input(fd, UA_sock);
534 read_and_process_input(stdin, UA_sock);
537 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
541 terminate_console(0);
546 /* Cleanup and then exit */
547 static void terminate_console(int sig)
550 static bool already_here = false;
552 if (already_here) { /* avoid recursive temination problems */
557 free_pool_memory(args);
559 (void)WSACleanup(); /* Cleanup Windows sockets */
567 * Make a quick check to see that we have all the
570 static int check_resources()
578 foreach_res(director, R_DIRECTOR) {
581 /* tls_require implies tls_enable */
582 if (director->tls_require) {
584 director->tls_enable = true;
586 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
592 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
593 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
594 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
595 " At least one CA certificate store is required.\n"),
596 director->hdr.name, configfile);
602 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
603 "Without that I don't how to speak to the Director :-(\n"), configfile);
608 /* Loop over Consoles */
609 foreach_res(cons, R_CONSOLE) {
610 /* tls_require implies tls_enable */
611 if (cons->tls_require) {
613 cons->tls_enable = true;
615 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
621 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
622 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
623 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
624 cons->hdr.name, configfile);
636 #define READLINE_LIBRARY 1
638 #include "readline.h"
643 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
647 rl_catch_signals = 0; /* do it ourselves */
648 line = readline((char *)prompt); /* cast needed for old readlines */
653 strip_trailing_junk(line);
654 sock->msglen = pm_strcpy(&sock->msg, line);
656 add_history(sock->msg);
662 #else /* no readline, do it ourselves */
665 * Returns: 1 if data available
670 wait_for_data(int fd, int sec)
675 return 1; /* select doesn't seem to work on Win32 */
682 FD_SET((unsigned)fd, &fdset);
683 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
684 case 0: /* timeout */
687 if (errno == EINTR || errno == EAGAIN) {
690 return -1; /* error return */
698 * Get next input command from terminal.
700 * Returns: 1 if got input
705 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
709 if (output == stdout || teeout) {
714 switch (wait_for_data(fileno(input), sec)) {
716 return 0; /* timeout */
718 return -1; /* error */
720 len = sizeof_pool_memory(sock->msg) - 1;
726 if (isatty(fileno(input))) {
727 input_line(sock->msg, len);
731 #ifdef HAVE_WIN32 /* use special console for input on win32 */
732 if (input == stdin) {
733 if (win32_cgets(sock->msg, len) == NULL) {
739 if (fgets(sock->msg, len, input) == NULL) {
748 strip_trailing_junk(sock->msg);
749 sock->msglen = strlen(sock->msg);
755 static int versioncmd(FILE *input, BSOCK *UA_sock)
757 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
758 HOST_OS, DISTNAME, DISTVER);
762 static int inputcmd(FILE *input, BSOCK *UA_sock)
767 sendit(_("Too many arguments on input command.\n"));
771 sendit(_("First argument to input command must be a filename.\n"));
774 fd = fopen(argk[1], "rb");
776 senditf(_("Cannot open file %s for input. ERR=%s\n"),
777 argk[1], strerror(errno));
780 read_and_process_input(fd, UA_sock);
785 /* Send output to both termina and specified file */
786 static int teecmd(FILE *input, BSOCK *UA_sock)
789 return do_outputcmd(input, UA_sock);
792 /* Send output to specified "file" */
793 static int outputcmd(FILE *input, BSOCK *UA_sock)
796 return do_outputcmd(input, UA_sock);
800 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
803 const char *mode = "a+b";
806 sendit(_("Too many arguments on output/tee command.\n"));
810 if (output != stdout) {
820 fd = fopen(argk[1], mode);
822 senditf(_("Cannot open file %s for output. ERR=%s\n"),
823 argk[1], strerror(errno));
830 static int quitcmd(FILE *input, BSOCK *UA_sock)
835 static int sleepcmd(FILE *input, BSOCK *UA_sock)
838 sleep(atoi(argk[1]));
844 static int timecmd(FILE *input, BSOCK *UA_sock)
847 time_t ttime = time(NULL);
849 (void)localtime_r(&ttime, &tm);
850 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
856 * Send a line to the output file and or the terminal
858 void senditf(const char *fmt,...)
863 va_start(arg_ptr, fmt);
864 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
869 void sendit(const char *buf)
873 if (output == stdout || teeout) {
876 * Here, we convert every \n into \r\n because the
877 * terminal is in raw mode when we are using
880 for (p=q=buf; (p=strchr(q, '\n')); ) {
883 memcpy(obuf, q, len);
885 memcpy(obuf+len, "\r\n", 3);
886 q = ++p; /* point after \n */
894 if (output != stdout) {