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(), which uses a static buffer and is NOT thread-safe.
293 static int tls_pem_callback(char *buf, int size, const void *userdata)
296 const char *prompt = (const char *) userdata;
299 passwd = getpass(prompt);
300 bstrncpy(buf, passwd, size);
301 return (strlen(buf));
309 /*********************************************************************
311 * Main Bacula Console -- User Interface Program
314 int main(int argc, char *argv[])
317 bool no_signals = false;
318 bool test_config = false;
321 setlocale(LC_ALL, "");
322 bindtextdomain("bacula", LOCALEDIR);
323 textdomain("bacula");
326 my_name_is(argc, argv, "bconsole");
327 init_msg(NULL, NULL);
328 working_directory = "/tmp";
329 args = get_pool_memory(PM_FNAME);
332 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
334 case 'c': /* configuration file */
335 if (configfile != NULL) {
338 configfile = bstrdup(optarg);
342 debug_level = atoi(optarg);
343 if (debug_level <= 0) {
348 case 's': /* turn off signals */
367 init_signals(terminate_console);
370 #if !defined(HAVE_WIN32)
371 /* Override Bacula default signals */
372 // signal(SIGCHLD, SIG_IGN);
373 signal(SIGQUIT, SIG_IGN);
374 signal(SIGTSTP, got_sigstop);
375 signal(SIGCONT, got_sigcontinue);
376 signal(SIGTTIN, got_sigtin);
377 signal(SIGTTOU, got_sigtout);
390 if (configfile == NULL) {
391 configfile = bstrdup(CONFIG_FILE);
394 parse_config(configfile);
396 if (init_crypto() != 0) {
397 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
400 if (!check_resources()) {
401 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
405 terminate_console(0);
409 memset(&jcr, 0, sizeof(jcr));
411 (void)WSA_Init(); /* Initialize Windows sockets */
414 struct sockaddr client_addr;
415 memset(&client_addr, 0, sizeof(client_addr));
416 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
418 sendit(_("Available Directors:\n"));
421 foreach_res(dir, R_DIRECTOR) {
422 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
426 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
427 (void)WSACleanup(); /* Cleanup Windows sockets */
430 item = atoi(UA_sock->msg);
431 if (item < 0 || item > numdir) {
432 senditf(_("You must enter a number between 1 and %d\n"), numdir);
437 for (i=0; i<item; i++) {
438 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
444 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
449 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
452 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
455 /* Initialize Console TLS context */
456 if (cons && (cons->tls_enable || cons->tls_require)) {
457 /* Generate passphrase prompt */
458 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
460 /* Initialize TLS context:
461 * Args: CA certfile, CA certdir, Certfile, Keyfile,
462 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
463 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
464 cons->tls_ca_certdir, cons->tls_certfile,
465 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
467 if (!cons->tls_ctx) {
468 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
470 terminate_console(0);
476 /* Initialize Director TLS context */
477 if (dir->tls_enable || dir->tls_require) {
478 /* Generate passphrase prompt */
479 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
481 /* Initialize TLS context:
482 * Args: CA certfile, CA certdir, Certfile, Keyfile,
483 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
484 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
485 dir->tls_ca_certdir, dir->tls_certfile,
486 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
489 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
491 terminate_console(0);
496 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
497 NULL, dir->DIRport, 0);
498 if (UA_sock == NULL) {
499 terminate_console(0);
502 jcr.dir_bsock = UA_sock;
504 /* If cons==NULL, default console will be used */
505 if (!authenticate_director(&jcr, dir, cons)) {
506 terminate_console(0);
510 Dmsg0(40, "Opened connection with Director daemon\n");
512 sendit(_("Enter a period to cancel a command.\n"));
514 /* Run commands in ~/.bconsolerc if any */
515 char *env = getenv("HOME");
518 pm_strcpy(&UA_sock->msg, env);
519 pm_strcat(&UA_sock->msg, "/.bconsolerc");
520 fd = fopen(UA_sock->msg, "r");
522 read_and_process_input(fd, UA_sock);
527 read_and_process_input(stdin, UA_sock);
530 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
534 terminate_console(0);
539 /* Cleanup and then exit */
540 static void terminate_console(int sig)
543 static bool already_here = false;
545 if (already_here) { /* avoid recursive temination problems */
550 free_pool_memory(args);
552 (void)WSACleanup(); /* Cleanup Windows sockets */
560 * Make a quick check to see that we have all the
563 static int check_resources()
571 foreach_res(director, R_DIRECTOR) {
574 /* tls_require implies tls_enable */
575 if (director->tls_require) {
577 director->tls_enable = true;
579 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
585 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
586 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
587 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
588 " At least one CA certificate store is required.\n"),
589 director->hdr.name, configfile);
595 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
596 "Without that I don't how to speak to the Director :-(\n"), configfile);
601 /* Loop over Consoles */
602 foreach_res(cons, R_CONSOLE) {
603 /* tls_require implies tls_enable */
604 if (cons->tls_require) {
606 cons->tls_enable = true;
608 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
614 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
615 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
616 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
617 cons->hdr.name, configfile);
629 #define READLINE_LIBRARY 1
631 #include "readline.h"
636 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
640 rl_catch_signals = 0; /* do it ourselves */
641 line = readline((char *)prompt); /* cast needed for old readlines */
646 strip_trailing_junk(line);
647 sock->msglen = pm_strcpy(&sock->msg, line);
649 add_history(sock->msg);
655 #else /* no readline, do it ourselves */
658 * Returns: 1 if data available
663 wait_for_data(int fd, int sec)
668 return 1; /* select doesn't seem to work on Win32 */
675 FD_SET((unsigned)fd, &fdset);
676 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
677 case 0: /* timeout */
680 if (errno == EINTR || errno == EAGAIN) {
683 return -1; /* error return */
691 * Get next input command from terminal.
693 * Returns: 1 if got input
698 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
702 if (output == stdout || tee) {
707 switch (wait_for_data(fileno(input), sec)) {
709 return 0; /* timeout */
711 return -1; /* error */
713 len = sizeof_pool_memory(sock->msg) - 1;
719 if (isatty(fileno(input))) {
720 input_line(sock->msg, len);
724 #ifdef HAVE_WIN32 /* use special console for input on win32 */
725 if (input == stdin) {
726 if (win32_cgets(sock->msg, len) == NULL) {
732 if (fgets(sock->msg, len, input) == NULL) {
741 strip_trailing_junk(sock->msg);
742 sock->msglen = strlen(sock->msg);
748 static int versioncmd(FILE *input, BSOCK *UA_sock)
750 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
751 HOST_OS, DISTNAME, DISTVER);
755 static int inputcmd(FILE *input, BSOCK *UA_sock)
760 sendit(_("Too many arguments on input command.\n"));
764 sendit(_("First argument to input command must be a filename.\n"));
767 fd = fopen(argk[1], "r");
769 senditf(_("Cannot open file %s for input. ERR=%s\n"),
770 argk[1], strerror(errno));
773 read_and_process_input(fd, UA_sock);
778 /* Send output to both termina and specified file */
779 static int teecmd(FILE *input, BSOCK *UA_sock)
782 return do_outputcmd(input, UA_sock);
785 /* Send output to specified "file" */
786 static int outputcmd(FILE *input, BSOCK *UA_sock)
789 return do_outputcmd(input, UA_sock);
793 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
796 const char *mode = "a+";
799 sendit(_("Too many arguments on output/tee command.\n"));
803 if (output != stdout) {
813 fd = fopen(argk[1], mode);
815 senditf(_("Cannot open file %s for output. ERR=%s\n"),
816 argk[1], strerror(errno));
823 static int quitcmd(FILE *input, BSOCK *UA_sock)
828 static int sleepcmd(FILE *input, BSOCK *UA_sock)
831 sleep(atoi(argk[1]));
837 static int timecmd(FILE *input, BSOCK *UA_sock)
840 time_t ttime = time(NULL);
842 localtime_r(&ttime, &tm);
843 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
849 * Send a line to the output file and or the terminal
851 void senditf(const char *fmt,...)
856 va_start(arg_ptr, fmt);
857 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
862 void sendit(const char *buf)
865 if (output == stdout || tee) {
868 * Here, we convert every \n into \r\n because the
869 * terminal is in raw mode when we are using
872 for (p=q=buf; (p=strchr(q, '\n')); ) {
877 q = ++p; /* point after \n */
883 if (output != stdout) {
893 if (output != stdout || tee) {