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();
41 #if defined(HAVE_WIN32)
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;
69 static DIRRES *dir = NULL;
70 static CONRES *cons = NULL;
71 static FILE *output = stdout;
72 static bool teeout = false; /* output to output and stdout */
73 static bool stop = false;
74 static bool no_conio = false;
79 static char *argk[MAX_CMD_ARGS];
80 static char *argv[MAX_CMD_ARGS];
83 /* Command prototypes */
84 static int versioncmd(FILE *input, BSOCK *UA_sock);
85 static int inputcmd(FILE *input, BSOCK *UA_sock);
86 static int outputcmd(FILE *input, BSOCK *UA_sock);
87 static int teecmd(FILE *input, BSOCK *UA_sock);
88 static int quitcmd(FILE *input, BSOCK *UA_sock);
89 static int timecmd(FILE *input, BSOCK *UA_sock);
90 static int sleepcmd(FILE *input, BSOCK *UA_sock);
93 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
98 "Copyright (C) 2000-%s Kern Sibbald\n"
99 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
100 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
101 " -c <file> set configuration file to file\n"
102 " -dnn set debug level to nn\n"
105 " -t test - read configuration and exit\n"
106 " -? print this message.\n"
107 "\n"), BYEAR, HOST_OS, DISTNAME, DISTVER);
112 void got_sigstop(int sig)
118 void got_sigcontinue(int sig)
124 void got_sigtout(int sig)
126 // printf("Got tout\n");
130 void got_sigtin(int sig)
132 // printf("Got tin\n");
136 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
143 * These are the @command
145 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
146 static struct cmdstruct commands[] = {
147 { N_("input"), inputcmd, _("input from file")},
148 { N_("output"), outputcmd, _("output to file")},
149 { N_("quit"), quitcmd, _("quit")},
150 { N_("tee"), teecmd, _("output to file and terminal")},
151 { N_("sleep"), sleepcmd, _("sleep specified time")},
152 { N_("time"), timecmd, _("print current time")},
153 { N_("version"), versioncmd, _("print Console's version")},
154 { N_("exit"), quitcmd, _("exit = quit")},
155 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
157 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
159 static int do_a_command(FILE *input, BSOCK *UA_sock)
170 Dmsg1(120, "Command: %s\n", UA_sock->msg);
176 if (*cmd == '#') { /* comment */
180 for (i=0; i<comsize; i++) { /* search for command */
181 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
182 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
188 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
189 UA_sock->msglen = strlen(UA_sock->msg);
190 sendit(UA_sock->msg);
196 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
198 const char *prompt = "*";
199 bool at_prompt = false;
200 int tty_input = isatty(fileno(input));
204 if (at_prompt) { /* don't prompt multiple times */
211 stat = get_cmd(input, prompt, UA_sock, 30);
219 /* Reading input from a file */
220 int len = sizeof_pool_memory(UA_sock->msg) - 1;
224 if (fgets(UA_sock->msg, len, input) == NULL) {
227 sendit(UA_sock->msg); /* echo to terminal */
228 strip_trailing_junk(UA_sock->msg);
229 UA_sock->msglen = strlen(UA_sock->msg);
234 break; /* error or interrupt */
235 } else if (stat == 0) { /* timeout */
236 if (strcmp(prompt, "*") == 0) {
237 bnet_fsend(UA_sock, ".messages");
243 /* @ => internal command for us */
244 if (UA_sock->msg[0] == '@') {
245 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
246 if (!do_a_command(input, UA_sock)) {
251 if (!bnet_send(UA_sock)) { /* send command */
255 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
258 while ((stat = bnet_recv(UA_sock)) >= 0) {
265 /* Suppress output if running in background or user hit ctl-c */
266 if (!stop && !usrbrk()) {
267 sendit(UA_sock->msg);
278 if (is_bnet_stop(UA_sock)) {
279 break; /* error or term */
280 } else if (stat == BNET_SIGNAL) {
281 if (UA_sock->msglen == BNET_PROMPT) {
284 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
290 * Call-back for reading a passphrase for an encrypted PEM file
291 * This function uses getpass(),
292 * which uses a static buffer and is NOT thread-safe.
294 static int tls_pem_callback(char *buf, int size, const void *userdata)
297 const char *prompt = (const char *)userdata;
298 # if defined(HAVE_WIN32)
300 if (win32_cgets(buf, size) == NULL) {
309 passwd = getpass(prompt);
310 bstrncpy(buf, passwd, size);
320 /*********************************************************************
322 * Main Bacula Console -- User Interface Program
325 int main(int argc, char *argv[])
328 bool no_signals = false;
329 bool test_config = false;
332 setlocale(LC_ALL, "");
333 bindtextdomain("bacula", LOCALEDIR);
334 textdomain("bacula");
337 my_name_is(argc, argv, "bconsole");
338 init_msg(NULL, NULL);
339 working_directory = "/tmp";
340 args = get_pool_memory(PM_FNAME);
342 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
344 case 'c': /* configuration file */
345 if (configfile != NULL) {
348 configfile = bstrdup(optarg);
352 debug_level = atoi(optarg);
353 if (debug_level <= 0) {
358 case 'n': /* no conio */
362 case 's': /* turn off signals */
380 init_signals(terminate_console);
384 #if !defined(HAVE_WIN32)
385 /* Override Bacula default signals */
386 signal(SIGQUIT, SIG_IGN);
387 signal(SIGTSTP, got_sigstop);
388 signal(SIGCONT, got_sigcontinue);
389 signal(SIGTTIN, got_sigtin);
390 signal(SIGTTOU, got_sigtout);
401 if (configfile == NULL) {
402 configfile = bstrdup(CONFIG_FILE);
405 parse_config(configfile);
407 if (init_crypto() != 0) {
408 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
411 if (!check_resources()) {
412 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
420 terminate_console(0);
424 memset(&jcr, 0, sizeof(jcr));
426 (void)WSA_Init(); /* Initialize Windows sockets */
430 foreach_res(dir, R_DIRECTOR) {
434 foreach_res(cons, R_CONSOLE) {
440 struct sockaddr client_addr;
441 memset(&client_addr, 0, sizeof(client_addr));
442 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
444 sendit(_("Available Directors:\n"));
447 foreach_res(dir, R_DIRECTOR) {
448 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
452 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
453 (void)WSACleanup(); /* Cleanup Windows sockets */
456 item = atoi(UA_sock->msg);
457 if (item < 0 || item > numdir) {
458 senditf(_("You must enter a number between 1 and %d\n"), numdir);
463 for (i=0; i<item; i++) {
464 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
466 /* Look for a console linked to this director */
467 for (i=0; i<numcon; i++) {
468 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
469 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
474 /* Look for the first non-linked console */
476 for (i=0; i<numcon; i++) {
477 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
478 if (cons->director == NULL)
485 /* If no director, take first one */
488 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
491 /* If no console, take first one */
494 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
498 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
501 /* Initialize Console TLS context */
502 if (cons && (cons->tls_enable || cons->tls_require)) {
503 /* Generate passphrase prompt */
504 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
506 /* Initialize TLS context:
507 * Args: CA certfile, CA certdir, Certfile, Keyfile,
508 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
509 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
510 cons->tls_ca_certdir, cons->tls_certfile,
511 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
513 if (!cons->tls_ctx) {
514 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
516 terminate_console(0);
521 /* Initialize Director TLS context */
522 if (dir->tls_enable || dir->tls_require) {
523 /* Generate passphrase prompt */
524 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
526 /* Initialize TLS context:
527 * Args: CA certfile, CA certdir, Certfile, Keyfile,
528 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
529 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
530 dir->tls_ca_certdir, dir->tls_certfile,
531 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
534 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
536 terminate_console(0);
541 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
542 NULL, dir->DIRport, 0);
543 if (UA_sock == NULL) {
544 terminate_console(0);
547 jcr.dir_bsock = UA_sock;
549 /* If cons==NULL, default console will be used */
550 if (!authenticate_director(&jcr, dir, cons)) {
551 terminate_console(0);
555 Dmsg0(40, "Opened connection with Director daemon\n");
557 sendit(_("Enter a period to cancel a command.\n"));
559 /* Run commands in ~/.bconsolerc if any */
560 char *env = getenv("HOME");
563 pm_strcpy(&UA_sock->msg, env);
564 pm_strcat(&UA_sock->msg, "/.bconsolerc");
565 fd = fopen(UA_sock->msg, "rb");
567 read_and_process_input(fd, UA_sock);
572 read_and_process_input(stdin, UA_sock);
575 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
579 terminate_console(0);
584 /* Cleanup and then exit */
585 static void terminate_console(int sig)
588 static bool already_here = false;
590 if (already_here) { /* avoid recursive temination problems */
595 free_pool_memory(args);
599 (void)WSACleanup(); /* Cleanup Windows sockets */
607 * Make a quick check to see that we have all the
610 static int check_resources()
618 foreach_res(director, R_DIRECTOR) {
621 /* tls_require implies tls_enable */
622 if (director->tls_require) {
624 director->tls_enable = true;
626 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
632 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
633 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
634 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
635 " At least one CA certificate store is required.\n"),
636 director->hdr.name, configfile);
642 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
643 "Without that I don't how to speak to the Director :-(\n"), configfile);
648 /* Loop over Consoles */
649 foreach_res(cons, R_CONSOLE) {
650 /* tls_require implies tls_enable */
651 if (cons->tls_require) {
653 cons->tls_enable = true;
655 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
661 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
662 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
663 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
664 cons->hdr.name, configfile);
676 #define READLINE_LIBRARY 1
678 #include "readline.h"
683 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
687 rl_catch_signals = 0; /* do it ourselves */
688 line = readline((char *)prompt); /* cast needed for old readlines */
693 strip_trailing_junk(line);
694 sock->msglen = pm_strcpy(&sock->msg, line);
696 add_history(sock->msg);
702 #else /* no readline, do it ourselves */
704 static bool bisatty(int fd)
713 * Returns: 1 if data available
718 wait_for_data(int fd, int sec)
720 #if defined(HAVE_WIN32)
730 FD_SET((unsigned)fd, &fdset);
731 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
732 case 0: /* timeout */
735 if (errno == EINTR || errno == EAGAIN) {
738 return -1; /* error return */
747 * Get next input command from terminal.
749 * Returns: 1 if got input
754 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
758 if (output == stdout || teeout) {
763 switch (wait_for_data(fileno(input), sec)) {
765 return 0; /* timeout */
767 return -1; /* error */
769 len = sizeof_pool_memory(sock->msg) - 1;
775 if (bisatty(fileno(input))) {
776 input_line(sock->msg, len);
780 #ifdef HAVE_WIN32 /* use special console for input on win32 */
781 if (input == stdin) {
782 if (win32_cgets(sock->msg, len) == NULL) {
788 if (fgets(sock->msg, len, input) == NULL) {
797 strip_trailing_junk(sock->msg);
798 sock->msglen = strlen(sock->msg);
802 #endif /* end non-readline code */
804 static int versioncmd(FILE *input, BSOCK *UA_sock)
806 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
807 HOST_OS, DISTNAME, DISTVER);
811 static int inputcmd(FILE *input, BSOCK *UA_sock)
816 sendit(_("Too many arguments on input command.\n"));
820 sendit(_("First argument to input command must be a filename.\n"));
823 fd = fopen(argk[1], "rb");
825 senditf(_("Cannot open file %s for input. ERR=%s\n"),
826 argk[1], strerror(errno));
829 read_and_process_input(fd, UA_sock);
834 /* Send output to both termina and specified file */
835 static int teecmd(FILE *input, BSOCK *UA_sock)
838 return do_outputcmd(input, UA_sock);
841 /* Send output to specified "file" */
842 static int outputcmd(FILE *input, BSOCK *UA_sock)
845 return do_outputcmd(input, UA_sock);
849 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
852 const char *mode = "a+b";
855 sendit(_("Too many arguments on output/tee command.\n"));
859 if (output != stdout) {
869 fd = fopen(argk[1], mode);
871 senditf(_("Cannot open file %s for output. ERR=%s\n"),
872 argk[1], strerror(errno));
879 static int quitcmd(FILE *input, BSOCK *UA_sock)
884 static int sleepcmd(FILE *input, BSOCK *UA_sock)
887 sleep(atoi(argk[1]));
893 static int timecmd(FILE *input, BSOCK *UA_sock)
896 time_t ttime = time(NULL);
898 (void)localtime_r(&ttime, &tm);
899 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
905 * Send a line to the output file and or the terminal
907 void senditf(const char *fmt,...)
912 va_start(arg_ptr, fmt);
913 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
918 void sendit(const char *buf)
922 if (output == stdout || teeout) {
925 * Here, we convert every \n into \r\n because the
926 * terminal is in raw mode when we are using
929 for (p=q=buf; (p=strchr(q, '\n')); ) {
932 memcpy(obuf, q, len);
934 memcpy(obuf+len, "\r\n", 3);
935 q = ++p; /* point after \n */
943 if (output != stdout) {