3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
10 Copyright (C) 2000-2005 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);
57 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
59 /* Forward referenced functions */
60 static void terminate_console(int sig);
61 static int check_resources();
62 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
63 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
64 void senditf(const char *fmt, ...);
65 void sendit(const char *buf);
67 extern "C" void got_sigstop(int sig);
68 extern "C" void got_sigcontinue(int sig);
69 extern "C" void got_sigtout(int sig);
70 extern "C" void got_sigtin(int sig);
73 /* Static variables */
74 static char *configfile = NULL;
75 static BSOCK *UA_sock = NULL;
77 static FILE *output = stdout;
78 static bool tee = false; /* output to output and stdout */
79 static bool stop = false;
83 static char *argk[MAX_CMD_ARGS];
84 static char *argv[MAX_CMD_ARGS];
87 /* Command prototypes */
88 static int versioncmd(FILE *input, BSOCK *UA_sock);
89 static int inputcmd(FILE *input, BSOCK *UA_sock);
90 static int outputcmd(FILE *input, BSOCK *UA_sock);
91 static int teecmd(FILE *input, BSOCK *UA_sock);
92 static int quitcmd(FILE *input, BSOCK *UA_sock);
93 static int timecmd(FILE *input, BSOCK *UA_sock);
94 static int sleepcmd(FILE *input, BSOCK *UA_sock);
97 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
102 "Copyright (C) 2000-2005 Kern Sibbald\n"
103 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
104 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
105 " -c <file> set configuration file to file\n"
106 " -dnn set debug level to nn\n"
108 " -t test - read configuration and exit\n"
109 " -? print this message.\n"
110 "\n"), HOST_OS, DISTNAME, DISTVER);
115 void got_sigstop(int sig)
121 void got_sigcontinue(int sig)
127 void got_sigtout(int sig)
129 // printf("Got tout\n");
133 void got_sigtin(int sig)
135 // printf("Got tin\n");
139 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
146 * These are the @command
148 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
149 static struct cmdstruct commands[] = {
150 { N_("input"), inputcmd, _("input from file")},
151 { N_("output"), outputcmd, _("output to file")},
152 { N_("quit"), quitcmd, _("quit")},
153 { N_("tee"), teecmd, _("output to file and terminal")},
154 { N_("sleep"), sleepcmd, _("sleep specified time")},
155 { N_("time"), timecmd, _("print current time")},
156 { N_("version"), versioncmd, _("print Console's version")},
157 { N_("exit"), quitcmd, _("exit = quit")},
158 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
160 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
162 static int do_a_command(FILE *input, BSOCK *UA_sock)
173 Dmsg1(120, "Command: %s\n", UA_sock->msg);
179 if (*cmd == '#') { /* comment */
183 for (i=0; i<comsize; i++) { /* search for command */
184 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
185 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
191 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
192 UA_sock->msglen = strlen(UA_sock->msg);
193 sendit(UA_sock->msg);
199 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
201 const char *prompt = "*";
202 bool at_prompt = false;
203 int tty_input = isatty(fileno(input));
207 if (at_prompt) { /* don't prompt multiple times */
214 stat = get_cmd(input, prompt, UA_sock, 30);
222 /* Reading input from a file */
223 int len = sizeof_pool_memory(UA_sock->msg) - 1;
227 if (fgets(UA_sock->msg, len, input) == NULL) {
230 sendit(UA_sock->msg); /* echo to terminal */
231 strip_trailing_junk(UA_sock->msg);
232 UA_sock->msglen = strlen(UA_sock->msg);
237 break; /* error or interrupt */
238 } else if (stat == 0) { /* timeout */
239 if (strcmp(prompt, "*") == 0) {
240 bnet_fsend(UA_sock, ".messages");
246 /* @ => internal command for us */
247 if (UA_sock->msg[0] == '@') {
248 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
249 if (!do_a_command(input, UA_sock)) {
254 if (!bnet_send(UA_sock)) { /* send command */
258 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
261 while ((stat = bnet_recv(UA_sock)) >= 0) {
268 /* Suppress output if running in background or user hit ctl-c */
269 if (!stop && !usrbrk()) {
270 sendit(UA_sock->msg);
281 if (is_bnet_stop(UA_sock)) {
282 break; /* error or term */
283 } else if (stat == BNET_SIGNAL) {
284 if (UA_sock->msglen == BNET_PROMPT) {
287 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
293 * Call-back for reading a passphrase for an encrypted PEM file
294 * This function uses getpass(), which uses a static buffer and is NOT thread-safe.
296 static int tls_pem_callback(char *buf, int size, const void *userdata)
299 const char *prompt = (const char *) userdata;
302 passwd = getpass(prompt);
303 bstrncpy(buf, passwd, size);
304 return (strlen(buf));
312 /*********************************************************************
314 * Main Bacula Console -- User Interface Program
317 int main(int argc, char *argv[])
320 bool no_signals = false;
321 bool test_config = false;
324 setlocale(LC_ALL, "");
325 bindtextdomain("bacula", LOCALEDIR);
326 textdomain("bacula");
329 my_name_is(argc, argv, "bconsole");
330 init_msg(NULL, NULL);
331 working_directory = "/tmp";
332 args = get_pool_memory(PM_FNAME);
335 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
337 case 'c': /* configuration file */
338 if (configfile != NULL) {
341 configfile = bstrdup(optarg);
345 debug_level = atoi(optarg);
346 if (debug_level <= 0) {
351 case 's': /* turn off signals */
370 init_signals(terminate_console);
373 #if !defined(HAVE_WIN32)
374 /* Override Bacula default signals */
375 // signal(SIGCHLD, SIG_IGN);
376 signal(SIGQUIT, SIG_IGN);
377 signal(SIGTSTP, got_sigstop);
378 signal(SIGCONT, got_sigcontinue);
379 signal(SIGTTIN, got_sigtin);
380 signal(SIGTTOU, got_sigtout);
393 if (configfile == NULL) {
394 configfile = bstrdup(CONFIG_FILE);
397 parse_config(configfile);
399 if (init_tls() != 0) {
400 Emsg0(M_ERROR_TERM, 0, _("TLS library initialization failed.\n"));
403 if (!check_resources()) {
404 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
408 terminate_console(0);
412 memset(&jcr, 0, sizeof(jcr));
414 (void)WSA_Init(); /* Initialize Windows sockets */
417 struct sockaddr client_addr;
418 memset(&client_addr, 0, sizeof(client_addr));
419 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
421 sendit(_("Available Directors:\n"));
424 foreach_res(dir, R_DIRECTOR) {
425 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
429 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
430 (void)WSACleanup(); /* Cleanup Windows sockets */
433 item = atoi(UA_sock->msg);
434 if (item < 0 || item > numdir) {
435 senditf(_("You must enter a number between 1 and %d\n"), numdir);
440 for (i=0; i<item; i++) {
441 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
447 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
452 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
455 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
458 /* Initialize Console TLS context */
459 if (cons && (cons->tls_enable || cons->tls_require)) {
460 /* Generate passphrase prompt */
461 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
463 /* Initialize TLS context:
464 * Args: CA certfile, CA certdir, Certfile, Keyfile,
465 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
466 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
467 cons->tls_ca_certdir, cons->tls_certfile,
468 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
470 if (!cons->tls_ctx) {
471 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
473 terminate_console(0);
479 /* Initialize Director TLS context */
480 if (dir->tls_enable || dir->tls_require) {
481 /* Generate passphrase prompt */
482 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
484 /* Initialize TLS context:
485 * Args: CA certfile, CA certdir, Certfile, Keyfile,
486 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
487 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
488 dir->tls_ca_certdir, dir->tls_certfile,
489 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
492 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
494 terminate_console(0);
499 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
500 NULL, dir->DIRport, 0);
501 if (UA_sock == NULL) {
502 terminate_console(0);
505 jcr.dir_bsock = UA_sock;
507 /* If cons==NULL, default console will be used */
508 if (!authenticate_director(&jcr, dir, cons)) {
509 terminate_console(0);
513 Dmsg0(40, "Opened connection with Director daemon\n");
515 sendit(_("Enter a period to cancel a command.\n"));
517 /* Run commands in ~/.bconsolerc if any */
518 char *env = getenv("HOME");
521 pm_strcpy(&UA_sock->msg, env);
522 pm_strcat(&UA_sock->msg, "/.bconsolerc");
523 fd = fopen(UA_sock->msg, "r");
525 read_and_process_input(fd, UA_sock);
530 read_and_process_input(stdin, UA_sock);
533 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
537 terminate_console(0);
542 /* Cleanup and then exit */
543 static void terminate_console(int sig)
546 static bool already_here = false;
548 if (already_here) { /* avoid recursive temination problems */
553 free_pool_memory(args);
555 (void)WSACleanup(); /* Cleanup Windows sockets */
563 * Make a quick check to see that we have all the
566 static int check_resources()
574 foreach_res(director, R_DIRECTOR) {
577 /* tls_require implies tls_enable */
578 if (director->tls_require) {
580 director->tls_enable = true;
582 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
588 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
589 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
590 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
591 " At least one CA certificate store is required.\n"),
592 director->hdr.name, configfile);
598 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
599 "Without that I don't how to speak to the Director :-(\n"), configfile);
604 /* Loop over Consoles */
605 foreach_res(cons, R_CONSOLE) {
606 /* tls_require implies tls_enable */
607 if (cons->tls_require) {
609 cons->tls_enable = true;
611 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
617 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
618 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
619 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
620 cons->hdr.name, configfile);
632 #define READLINE_LIBRARY 1
634 #include "readline.h"
639 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
643 rl_catch_signals = 0; /* do it ourselves */
644 line = readline((char *)prompt); /* cast needed for old readlines */
649 strip_trailing_junk(line);
650 sock->msglen = pm_strcpy(&sock->msg, line);
652 add_history(sock->msg);
658 #else /* no readline, do it ourselves */
661 * Returns: 1 if data available
666 wait_for_data(int fd, int sec)
671 return 1; /* select doesn't seem to work on Win32 */
678 FD_SET((unsigned)fd, &fdset);
679 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
680 case 0: /* timeout */
683 if (errno == EINTR || errno == EAGAIN) {
686 return -1; /* error return */
694 * Get next input command from terminal.
696 * Returns: 1 if got input
701 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
705 if (output == stdout || tee) {
710 switch (wait_for_data(fileno(input), sec)) {
712 return 0; /* timeout */
714 return -1; /* error */
716 len = sizeof_pool_memory(sock->msg) - 1;
722 if (isatty(fileno(input))) {
723 input_line(sock->msg, len);
727 #ifdef HAVE_WIN32 /* use special console for input on win32 */
728 if (input == stdin) {
729 if (win32_cgets(sock->msg, len) == NULL) {
735 if (fgets(sock->msg, len, input) == NULL) {
744 strip_trailing_junk(sock->msg);
745 sock->msglen = strlen(sock->msg);
751 static int versioncmd(FILE *input, BSOCK *UA_sock)
753 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
754 HOST_OS, DISTNAME, DISTVER);
758 static int inputcmd(FILE *input, BSOCK *UA_sock)
763 sendit(_("Too many arguments on input command.\n"));
767 sendit(_("First argument to input command must be a filename.\n"));
770 fd = fopen(argk[1], "r");
772 senditf(_("Cannot open file %s for input. ERR=%s\n"),
773 argk[1], strerror(errno));
776 read_and_process_input(fd, UA_sock);
781 /* Send output to both termina and specified file */
782 static int teecmd(FILE *input, BSOCK *UA_sock)
785 return do_outputcmd(input, UA_sock);
788 /* Send output to specified "file" */
789 static int outputcmd(FILE *input, BSOCK *UA_sock)
792 return do_outputcmd(input, UA_sock);
796 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
799 const char *mode = "a+";
802 sendit(_("Too many arguments on output/tee command.\n"));
806 if (output != stdout) {
816 fd = fopen(argk[1], mode);
818 senditf(_("Cannot open file %s for output. ERR=%s\n"),
819 argk[1], strerror(errno));
826 static int quitcmd(FILE *input, BSOCK *UA_sock)
831 static int sleepcmd(FILE *input, BSOCK *UA_sock)
834 sleep(atoi(argk[1]));
840 static int timecmd(FILE *input, BSOCK *UA_sock)
843 time_t ttime = time(NULL);
845 localtime_r(&ttime, &tm);
846 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
852 * Send a line to the output file and or the terminal
854 void senditf(const char *fmt,...)
859 va_start(arg_ptr, fmt);
860 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
865 void sendit(const char *buf)
868 if (output == stdout || tee) {
871 * Here, we convert every \n into \r\n because the
872 * terminal is in raw mode when we are using
875 for (p=q=buf; (p=strchr(q, '\n')); ) {
880 q = ++p; /* point after \n */
886 if (output != stdout) {
896 if (output != stdout || tee) {