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 ammended 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)
298 const char *prompt = (const char *) userdata;
301 passwd = getpass(prompt);
302 bstrncpy(buf, passwd, size);
304 return (strlen(buf));
308 /*********************************************************************
310 * Main Bacula Console -- User Interface Program
313 int main(int argc, char *argv[])
316 bool no_signals = false;
317 bool test_config = false;
321 my_name_is(argc, argv, "bconsole");
322 textdomain("bacula");
323 init_msg(NULL, NULL);
324 working_directory = "/tmp";
325 args = get_pool_memory(PM_FNAME);
328 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
330 case 'c': /* configuration file */
331 if (configfile != NULL) {
334 configfile = bstrdup(optarg);
338 debug_level = atoi(optarg);
339 if (debug_level <= 0) {
344 case 's': /* turn off signals */
363 init_signals(terminate_console);
366 #if !defined(HAVE_WIN32)
367 /* Override Bacula default signals */
368 // signal(SIGCHLD, SIG_IGN);
369 signal(SIGQUIT, SIG_IGN);
370 signal(SIGTSTP, got_sigstop);
371 signal(SIGCONT, got_sigcontinue);
372 signal(SIGTTIN, got_sigtin);
373 signal(SIGTTOU, got_sigtout);
386 if (configfile == NULL) {
387 configfile = bstrdup(CONFIG_FILE);
390 parse_config(configfile);
392 if (init_tls() != 0) {
393 Emsg0(M_ERROR_TERM, 0, _("TLS library initialization failed.\n"));
396 if (!check_resources()) {
397 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
401 terminate_console(0);
405 memset(&jcr, 0, sizeof(jcr));
407 (void)WSA_Init(); /* Initialize Windows sockets */
410 struct sockaddr client_addr;
411 memset(&client_addr, 0, sizeof(client_addr));
412 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
414 sendit(_("Available Directors:\n"));
417 foreach_res(dir, R_DIRECTOR) {
418 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
422 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
423 (void)WSACleanup(); /* Cleanup Windows sockets */
426 item = atoi(UA_sock->msg);
427 if (item < 0 || item > numdir) {
428 senditf(_("You must enter a number between 1 and %d\n"), numdir);
433 for (i=0; i<item; i++) {
434 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
440 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
445 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
448 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
451 /* Initialize Console TLS context */
452 if (cons && (cons->tls_enable || cons->tls_require)) {
453 /* Generate passphrase prompt */
454 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
456 /* Initialize TLS context:
457 * Args: CA certfile, CA certdir, Certfile, Keyfile,
458 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
459 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
460 cons->tls_ca_certdir, cons->tls_certfile,
461 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
463 if (!cons->tls_ctx) {
464 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
466 terminate_console(0);
472 /* Initialize Director TLS context */
473 if (dir->tls_enable || dir->tls_require) {
474 /* Generate passphrase prompt */
475 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
477 /* Initialize TLS context:
478 * Args: CA certfile, CA certdir, Certfile, Keyfile,
479 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
480 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
481 dir->tls_ca_certdir, dir->tls_certfile,
482 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
485 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
487 terminate_console(0);
492 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
493 NULL, dir->DIRport, 0);
494 if (UA_sock == NULL) {
495 terminate_console(0);
498 jcr.dir_bsock = UA_sock;
500 /* If cons==NULL, default console will be used */
501 if (!authenticate_director(&jcr, dir, cons)) {
502 terminate_console(0);
506 Dmsg0(40, "Opened connection with Director daemon\n");
508 sendit(_("Enter a period to cancel a command.\n"));
510 /* Run commands in ~/.bconsolerc if any */
511 char *env = getenv("HOME");
514 pm_strcpy(&UA_sock->msg, env);
515 pm_strcat(&UA_sock->msg, "/.bconsolerc");
516 fd = fopen(UA_sock->msg, "r");
518 read_and_process_input(fd, UA_sock);
523 read_and_process_input(stdin, UA_sock);
526 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
530 terminate_console(0);
535 /* Cleanup and then exit */
536 static void terminate_console(int sig)
539 static bool already_here = false;
541 if (already_here) { /* avoid recursive temination problems */
546 free_pool_memory(args);
548 (void)WSACleanup(); /* Cleanup Windows sockets */
556 * Make a quick check to see that we have all the
559 static int check_resources()
567 foreach_res(director, R_DIRECTOR) {
570 /* tls_require implies tls_enable */
571 if (director->tls_require) {
573 director->tls_enable = true;
575 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
581 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
582 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
583 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
584 " At least one CA certificate store is required.\n"),
585 director->hdr.name, configfile);
591 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
592 "Without that I don't how to speak to the Director :-(\n"), configfile);
597 /* Loop over Consoles */
598 foreach_res(cons, R_CONSOLE) {
599 /* tls_require implies tls_enable */
600 if (cons->tls_require) {
602 cons->tls_enable = true;
604 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
610 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
611 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
612 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
613 cons->hdr.name, configfile);
625 #define READLINE_LIBRARY 1
627 #include "readline.h"
632 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
636 rl_catch_signals = 0; /* do it ourselves */
637 line = readline((char *)prompt); /* cast needed for old readlines */
642 strip_trailing_junk(line);
643 sock->msglen = pm_strcpy(&sock->msg, line);
645 add_history(sock->msg);
651 #else /* no readline, do it ourselves */
654 * Returns: 1 if data available
659 wait_for_data(int fd, int sec)
664 return 1; /* select doesn't seem to work on Win32 */
671 FD_SET((unsigned)fd, &fdset);
672 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
673 case 0: /* timeout */
676 if (errno == EINTR || errno == EAGAIN) {
679 return -1; /* error return */
687 * Get next input command from terminal.
689 * Returns: 1 if got input
694 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
698 if (output == stdout || tee) {
703 switch (wait_for_data(fileno(input), sec)) {
705 return 0; /* timeout */
707 return -1; /* error */
709 len = sizeof_pool_memory(sock->msg) - 1;
715 if (isatty(fileno(input))) {
716 input_line(sock->msg, len);
720 #ifdef HAVE_WIN32 /* use special console for input on win32 */
721 if (input == stdin) {
722 if (win32_cgets(sock->msg, len) == NULL) {
728 if (fgets(sock->msg, len, input) == NULL) {
737 strip_trailing_junk(sock->msg);
738 sock->msglen = strlen(sock->msg);
744 static int versioncmd(FILE *input, BSOCK *UA_sock)
746 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
747 HOST_OS, DISTNAME, DISTVER);
751 static int inputcmd(FILE *input, BSOCK *UA_sock)
756 sendit(_("Too many arguments on input command.\n"));
760 sendit(_("First argument to input command must be a filename.\n"));
763 fd = fopen(argk[1], "r");
765 senditf(_("Cannot open file %s for input. ERR=%s\n"),
766 argk[1], strerror(errno));
769 read_and_process_input(fd, UA_sock);
774 /* Send output to both termina and specified file */
775 static int teecmd(FILE *input, BSOCK *UA_sock)
778 return do_outputcmd(input, UA_sock);
781 /* Send output to specified "file" */
782 static int outputcmd(FILE *input, BSOCK *UA_sock)
785 return do_outputcmd(input, UA_sock);
789 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
792 const char *mode = "a+";
795 sendit(_("Too many arguments on output/tee command.\n"));
799 if (output != stdout) {
809 fd = fopen(argk[1], mode);
811 senditf(_("Cannot open file %s for output. ERR=%s\n"),
812 argk[1], strerror(errno));
819 static int quitcmd(FILE *input, BSOCK *UA_sock)
824 static int sleepcmd(FILE *input, BSOCK *UA_sock)
827 sleep(atoi(argk[1]));
833 static int timecmd(FILE *input, BSOCK *UA_sock)
836 time_t ttime = time(NULL);
838 localtime_r(&ttime, &tm);
839 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
845 * Send a line to the output file and or the terminal
847 void senditf(const char *fmt,...)
852 va_start(arg_ptr, fmt);
853 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
858 void sendit(const char *buf)
861 if (output == stdout || tee) {
864 * Here, we convert every \n into \r\n because the
865 * terminal is in raw mode when we are using
868 for (p=q=buf; (p=strchr(q, '\n')); ) {
873 q = ++p; /* point after \n */
879 if (output != stdout) {
889 if (output != stdout || tee) {