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"
33 #define con_set_zed_keys();
41 #include "../lib/winapi.h"
42 #define isatty(fd) (fd==0)
45 /* Exported variables */
50 extern int rl_catch_signals;
53 /* Imported functions */
54 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
56 /* Forward referenced functions */
57 static void terminate_console(int sig);
58 static int check_resources();
59 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
60 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
61 void senditf(const char *fmt, ...);
62 void sendit(const char *buf);
64 extern "C" void got_sigstop(int sig);
65 extern "C" void got_sigcontinue(int sig);
66 extern "C" void got_sigtout(int sig);
67 extern "C" void got_sigtin(int sig);
70 /* Static variables */
71 static char *configfile = NULL;
72 static BSOCK *UA_sock = NULL;
74 static FILE *output = stdout;
75 static bool tee = false; /* output to output and stdout */
76 static bool stop = false;
80 static char *argk[MAX_CMD_ARGS];
81 static char *argv[MAX_CMD_ARGS];
84 /* Command prototypes */
85 static int versioncmd(FILE *input, BSOCK *UA_sock);
86 static int inputcmd(FILE *input, BSOCK *UA_sock);
87 static int outputcmd(FILE *input, BSOCK *UA_sock);
88 static int teecmd(FILE *input, BSOCK *UA_sock);
89 static int quitcmd(FILE *input, BSOCK *UA_sock);
90 static int timecmd(FILE *input, BSOCK *UA_sock);
91 static int sleepcmd(FILE *input, BSOCK *UA_sock);
94 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
99 "Copyright (C) 2000-2005 Kern Sibbald\n"
100 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
101 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
102 " -c <file> set configuration file to file\n"
103 " -dnn set debug level to nn\n"
105 " -t test - read configuration and exit\n"
106 " -? print this message.\n"
107 "\n"), 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_keyst"), 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 illegal 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)
298 const char *prompt = (const char *)userdata;
300 if (win32_cgets(buf, size) == NULL) {
307 const char *prompt = (const char *)userdata;
310 passwd = getpass(prompt);
311 bstrncpy(buf, passwd, size);
321 /*********************************************************************
323 * Main Bacula Console -- User Interface Program
326 int main(int argc, char *argv[])
329 bool no_signals = false;
330 bool test_config = false;
333 setlocale(LC_ALL, "");
334 bindtextdomain("bacula", LOCALEDIR);
335 textdomain("bacula");
338 my_name_is(argc, argv, "bconsole");
339 init_msg(NULL, NULL);
340 working_directory = "/tmp";
341 args = get_pool_memory(PM_FNAME);
344 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
346 case 'c': /* configuration file */
347 if (configfile != NULL) {
350 configfile = bstrdup(optarg);
354 debug_level = atoi(optarg);
355 if (debug_level <= 0) {
360 case 's': /* turn off signals */
379 init_signals(terminate_console);
382 #if !defined(HAVE_WIN32)
383 /* Override Bacula default signals */
384 // signal(SIGCHLD, SIG_IGN);
385 signal(SIGQUIT, SIG_IGN);
386 signal(SIGTSTP, got_sigstop);
387 signal(SIGCONT, got_sigcontinue);
388 signal(SIGTTIN, got_sigtin);
389 signal(SIGTTOU, got_sigtout);
402 if (configfile == NULL) {
403 configfile = bstrdup(CONFIG_FILE);
406 parse_config(configfile);
408 if (init_crypto() != 0) {
409 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
412 if (!check_resources()) {
413 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
417 terminate_console(0);
421 memset(&jcr, 0, sizeof(jcr));
423 (void)WSA_Init(); /* Initialize Windows sockets */
426 struct sockaddr client_addr;
427 memset(&client_addr, 0, sizeof(client_addr));
428 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
430 sendit(_("Available Directors:\n"));
433 foreach_res(dir, R_DIRECTOR) {
434 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
438 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
439 (void)WSACleanup(); /* Cleanup Windows sockets */
442 item = atoi(UA_sock->msg);
443 if (item < 0 || item > numdir) {
444 senditf(_("You must enter a number between 1 and %d\n"), numdir);
449 for (i=0; i<item; i++) {
450 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
456 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
461 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
464 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
467 /* Initialize Console TLS context */
468 if (cons && (cons->tls_enable || cons->tls_require)) {
469 /* Generate passphrase prompt */
470 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
472 /* Initialize TLS context:
473 * Args: CA certfile, CA certdir, Certfile, Keyfile,
474 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
475 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
476 cons->tls_ca_certdir, cons->tls_certfile,
477 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
479 if (!cons->tls_ctx) {
480 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
482 terminate_console(0);
488 /* Initialize Director TLS context */
489 if (dir->tls_enable || dir->tls_require) {
490 /* Generate passphrase prompt */
491 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
493 /* Initialize TLS context:
494 * Args: CA certfile, CA certdir, Certfile, Keyfile,
495 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
496 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
497 dir->tls_ca_certdir, dir->tls_certfile,
498 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
501 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
503 terminate_console(0);
508 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
509 NULL, dir->DIRport, 0);
510 if (UA_sock == NULL) {
511 terminate_console(0);
514 jcr.dir_bsock = UA_sock;
516 /* If cons==NULL, default console will be used */
517 if (!authenticate_director(&jcr, dir, cons)) {
518 terminate_console(0);
522 Dmsg0(40, "Opened connection with Director daemon\n");
524 sendit(_("Enter a period to cancel a command.\n"));
526 /* Run commands in ~/.bconsolerc if any */
527 char *env = getenv("HOME");
530 pm_strcpy(&UA_sock->msg, env);
531 pm_strcat(&UA_sock->msg, "/.bconsolerc");
532 fd = fopen(UA_sock->msg, "r");
534 read_and_process_input(fd, UA_sock);
539 read_and_process_input(stdin, UA_sock);
542 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
546 terminate_console(0);
551 /* Cleanup and then exit */
552 static void terminate_console(int sig)
555 static bool already_here = false;
557 if (already_here) { /* avoid recursive temination problems */
562 free_pool_memory(args);
564 (void)WSACleanup(); /* Cleanup Windows sockets */
572 * Make a quick check to see that we have all the
575 static int check_resources()
583 foreach_res(director, R_DIRECTOR) {
586 /* tls_require implies tls_enable */
587 if (director->tls_require) {
589 director->tls_enable = true;
591 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
597 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
598 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
599 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
600 " At least one CA certificate store is required.\n"),
601 director->hdr.name, configfile);
607 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
608 "Without that I don't how to speak to the Director :-(\n"), configfile);
613 /* Loop over Consoles */
614 foreach_res(cons, R_CONSOLE) {
615 /* tls_require implies tls_enable */
616 if (cons->tls_require) {
618 cons->tls_enable = true;
620 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
626 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
627 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
628 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
629 cons->hdr.name, configfile);
641 #define READLINE_LIBRARY 1
643 #include "readline.h"
648 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
652 rl_catch_signals = 0; /* do it ourselves */
653 line = readline((char *)prompt); /* cast needed for old readlines */
658 strip_trailing_junk(line);
659 sock->msglen = pm_strcpy(&sock->msg, line);
661 add_history(sock->msg);
667 #else /* no readline, do it ourselves */
670 * Returns: 1 if data available
675 wait_for_data(int fd, int sec)
680 return 1; /* select doesn't seem to work on Win32 */
687 FD_SET((unsigned)fd, &fdset);
688 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
689 case 0: /* timeout */
692 if (errno == EINTR || errno == EAGAIN) {
695 return -1; /* error return */
703 * Get next input command from terminal.
705 * Returns: 1 if got input
710 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
714 if (output == stdout || tee) {
719 switch (wait_for_data(fileno(input), sec)) {
721 return 0; /* timeout */
723 return -1; /* error */
725 len = sizeof_pool_memory(sock->msg) - 1;
731 if (isatty(fileno(input))) {
732 input_line(sock->msg, len);
736 #ifdef HAVE_WIN32 /* use special console for input on win32 */
737 if (input == stdin) {
738 if (win32_cgets(sock->msg, len) == NULL) {
744 if (fgets(sock->msg, len, input) == NULL) {
753 strip_trailing_junk(sock->msg);
754 sock->msglen = strlen(sock->msg);
760 static int versioncmd(FILE *input, BSOCK *UA_sock)
762 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
763 HOST_OS, DISTNAME, DISTVER);
767 static int inputcmd(FILE *input, BSOCK *UA_sock)
772 sendit(_("Too many arguments on input command.\n"));
776 sendit(_("First argument to input command must be a filename.\n"));
779 fd = fopen(argk[1], "r");
781 senditf(_("Cannot open file %s for input. ERR=%s\n"),
782 argk[1], strerror(errno));
785 read_and_process_input(fd, UA_sock);
790 /* Send output to both termina and specified file */
791 static int teecmd(FILE *input, BSOCK *UA_sock)
794 return do_outputcmd(input, UA_sock);
797 /* Send output to specified "file" */
798 static int outputcmd(FILE *input, BSOCK *UA_sock)
801 return do_outputcmd(input, UA_sock);
805 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
808 const char *mode = "a+";
811 sendit(_("Too many arguments on output/tee command.\n"));
815 if (output != stdout) {
825 fd = fopen(argk[1], mode);
827 senditf(_("Cannot open file %s for output. ERR=%s\n"),
828 argk[1], strerror(errno));
835 static int quitcmd(FILE *input, BSOCK *UA_sock)
840 static int sleepcmd(FILE *input, BSOCK *UA_sock)
843 sleep(atoi(argk[1]));
849 static int timecmd(FILE *input, BSOCK *UA_sock)
852 time_t ttime = time(NULL);
854 localtime_r(&ttime, &tm);
855 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
861 * Send a line to the output file and or the terminal
863 void senditf(const char *fmt,...)
868 va_start(arg_ptr, fmt);
869 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
874 void sendit(const char *buf)
877 if (output == stdout || tee) {
880 * Here, we convert every \n into \r\n because the
881 * terminal is in raw mode when we are using
884 for (p=q=buf; (p=strchr(q, '\n')); ) {
889 q = ++p; /* point after \n */
895 if (output != stdout) {
905 if (output != stdout || tee) {