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();
40 #define isatty(fd) (fd==0)
43 /* Exported variables */
45 extern int rl_catch_signals;
47 /* Imported functions */
48 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
50 /* Forward referenced functions */
51 static void terminate_console(int sig);
52 static int check_resources();
53 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
54 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
55 void senditf(const char *fmt, ...);
56 void sendit(const char *buf);
58 extern "C" void got_sigstop(int sig);
59 extern "C" void got_sigcontinue(int sig);
60 extern "C" void got_sigtout(int sig);
61 extern "C" void got_sigtin(int sig);
64 /* Static variables */
65 static char *configfile = NULL;
66 static BSOCK *UA_sock = NULL;
68 static FILE *output = stdout;
69 static bool tee = false; /* output to output and stdout */
70 static bool stop = false;
74 static char *argk[MAX_CMD_ARGS];
75 static char *argv[MAX_CMD_ARGS];
78 /* Command prototypes */
79 static int versioncmd(FILE *input, BSOCK *UA_sock);
80 static int inputcmd(FILE *input, BSOCK *UA_sock);
81 static int outputcmd(FILE *input, BSOCK *UA_sock);
82 static int teecmd(FILE *input, BSOCK *UA_sock);
83 static int quitcmd(FILE *input, BSOCK *UA_sock);
84 static int timecmd(FILE *input, BSOCK *UA_sock);
85 static int sleepcmd(FILE *input, BSOCK *UA_sock);
88 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
93 "Copyright (C) 2000-2005 Kern Sibbald\n"
94 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
95 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
96 " -c <file> set configuration file to file\n"
97 " -dnn set debug level to nn\n"
99 " -t test - read configuration and exit\n"
100 " -? print this message.\n"
101 "\n"), HOST_OS, DISTNAME, DISTVER);
106 void got_sigstop(int sig)
112 void got_sigcontinue(int sig)
118 void got_sigtout(int sig)
120 // printf("Got tout\n");
124 void got_sigtin(int sig)
126 // printf("Got tin\n");
130 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
137 * These are the @command
139 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
140 static struct cmdstruct commands[] = {
141 { N_("input"), inputcmd, _("input from file")},
142 { N_("output"), outputcmd, _("output to file")},
143 { N_("quit"), quitcmd, _("quit")},
144 { N_("tee"), teecmd, _("output to file and terminal")},
145 { N_("sleep"), sleepcmd, _("sleep specified time")},
146 { N_("time"), timecmd, _("print current time")},
147 { N_("version"), versioncmd, _("print Console's version")},
148 { N_("exit"), quitcmd, _("exit = quit")},
149 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
151 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
153 static int do_a_command(FILE *input, BSOCK *UA_sock)
164 Dmsg1(120, "Command: %s\n", UA_sock->msg);
170 if (*cmd == '#') { /* comment */
174 for (i=0; i<comsize; i++) { /* search for command */
175 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
176 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
182 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
183 UA_sock->msglen = strlen(UA_sock->msg);
184 sendit(UA_sock->msg);
190 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
192 const char *prompt = "*";
193 bool at_prompt = false;
194 int tty_input = isatty(fileno(input));
198 if (at_prompt) { /* don't prompt multiple times */
205 stat = get_cmd(input, prompt, UA_sock, 30);
213 /* Reading input from a file */
214 int len = sizeof_pool_memory(UA_sock->msg) - 1;
218 if (fgets(UA_sock->msg, len, input) == NULL) {
221 sendit(UA_sock->msg); /* echo to terminal */
222 strip_trailing_junk(UA_sock->msg);
223 UA_sock->msglen = strlen(UA_sock->msg);
228 break; /* error or interrupt */
229 } else if (stat == 0) { /* timeout */
230 if (strcmp(prompt, "*") == 0) {
231 bnet_fsend(UA_sock, ".messages");
237 /* @ => internal command for us */
238 if (UA_sock->msg[0] == '@') {
239 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
240 if (!do_a_command(input, UA_sock)) {
245 if (!bnet_send(UA_sock)) { /* send command */
249 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
252 while ((stat = bnet_recv(UA_sock)) >= 0) {
259 /* Suppress output if running in background or user hit ctl-c */
260 if (!stop && !usrbrk()) {
261 sendit(UA_sock->msg);
272 if (is_bnet_stop(UA_sock)) {
273 break; /* error or term */
274 } else if (stat == BNET_SIGNAL) {
275 if (UA_sock->msglen == BNET_PROMPT) {
278 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
284 * Call-back for reading a passphrase for an encrypted PEM file
285 * This function uses getpass(),
286 * which uses a static buffer and is NOT thread-safe.
288 static int tls_pem_callback(char *buf, int size, const void *userdata)
292 const char *prompt = (const char *)userdata;
294 if (win32_cgets(buf, size) == NULL) {
301 const char *prompt = (const char *)userdata;
304 passwd = getpass(prompt);
305 bstrncpy(buf, passwd, size);
315 /*********************************************************************
317 * Main Bacula Console -- User Interface Program
320 int main(int argc, char *argv[])
323 bool no_signals = false;
324 bool test_config = false;
327 setlocale(LC_ALL, "");
328 bindtextdomain("bacula", LOCALEDIR);
329 textdomain("bacula");
332 my_name_is(argc, argv, "bconsole");
333 init_msg(NULL, NULL);
334 working_directory = "/tmp";
335 args = get_pool_memory(PM_FNAME);
338 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
340 case 'c': /* configuration file */
341 if (configfile != NULL) {
344 configfile = bstrdup(optarg);
348 debug_level = atoi(optarg);
349 if (debug_level <= 0) {
354 case 's': /* turn off signals */
373 init_signals(terminate_console);
377 #if !defined(HAVE_WIN32)
378 /* Override Bacula default signals */
379 signal(SIGQUIT, SIG_IGN);
380 signal(SIGTSTP, got_sigstop);
381 signal(SIGCONT, got_sigcontinue);
382 signal(SIGTTIN, got_sigtin);
383 signal(SIGTTOU, got_sigtout);
395 if (configfile == NULL) {
396 configfile = bstrdup(CONFIG_FILE);
399 parse_config(configfile);
401 if (init_crypto() != 0) {
402 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
405 if (!check_resources()) {
406 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
410 terminate_console(0);
414 memset(&jcr, 0, sizeof(jcr));
416 (void)WSA_Init(); /* Initialize Windows sockets */
419 struct sockaddr client_addr;
420 memset(&client_addr, 0, sizeof(client_addr));
421 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
423 sendit(_("Available Directors:\n"));
426 foreach_res(dir, R_DIRECTOR) {
427 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
431 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
432 (void)WSACleanup(); /* Cleanup Windows sockets */
435 item = atoi(UA_sock->msg);
436 if (item < 0 || item > numdir) {
437 senditf(_("You must enter a number between 1 and %d\n"), numdir);
442 for (i=0; i<item; i++) {
443 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
449 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
454 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
457 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
460 /* Initialize Console TLS context */
461 if (cons && (cons->tls_enable || cons->tls_require)) {
462 /* Generate passphrase prompt */
463 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
465 /* Initialize TLS context:
466 * Args: CA certfile, CA certdir, Certfile, Keyfile,
467 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
468 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
469 cons->tls_ca_certdir, cons->tls_certfile,
470 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
472 if (!cons->tls_ctx) {
473 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
475 terminate_console(0);
481 /* Initialize Director TLS context */
482 if (dir->tls_enable || dir->tls_require) {
483 /* Generate passphrase prompt */
484 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
486 /* Initialize TLS context:
487 * Args: CA certfile, CA certdir, Certfile, Keyfile,
488 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
489 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
490 dir->tls_ca_certdir, dir->tls_certfile,
491 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
494 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
496 terminate_console(0);
501 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
502 NULL, dir->DIRport, 0);
503 if (UA_sock == NULL) {
504 terminate_console(0);
507 jcr.dir_bsock = UA_sock;
509 /* If cons==NULL, default console will be used */
510 if (!authenticate_director(&jcr, dir, cons)) {
511 terminate_console(0);
515 Dmsg0(40, "Opened connection with Director daemon\n");
517 sendit(_("Enter a period to cancel a command.\n"));
519 /* Run commands in ~/.bconsolerc if any */
520 char *env = getenv("HOME");
523 pm_strcpy(&UA_sock->msg, env);
524 pm_strcat(&UA_sock->msg, "/.bconsolerc");
525 fd = fopen(UA_sock->msg, "rb");
527 read_and_process_input(fd, UA_sock);
532 read_and_process_input(stdin, UA_sock);
535 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
539 terminate_console(0);
544 /* Cleanup and then exit */
545 static void terminate_console(int sig)
548 static bool already_here = false;
550 if (already_here) { /* avoid recursive temination problems */
555 free_pool_memory(args);
557 (void)WSACleanup(); /* Cleanup Windows sockets */
565 * Make a quick check to see that we have all the
568 static int check_resources()
576 foreach_res(director, R_DIRECTOR) {
579 /* tls_require implies tls_enable */
580 if (director->tls_require) {
582 director->tls_enable = true;
584 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
590 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
591 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
592 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
593 " At least one CA certificate store is required.\n"),
594 director->hdr.name, configfile);
600 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
601 "Without that I don't how to speak to the Director :-(\n"), configfile);
606 /* Loop over Consoles */
607 foreach_res(cons, R_CONSOLE) {
608 /* tls_require implies tls_enable */
609 if (cons->tls_require) {
611 cons->tls_enable = true;
613 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
619 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
620 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
621 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
622 cons->hdr.name, configfile);
634 #define READLINE_LIBRARY 1
636 #include "readline.h"
641 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
645 rl_catch_signals = 0; /* do it ourselves */
646 line = readline((char *)prompt); /* cast needed for old readlines */
651 strip_trailing_junk(line);
652 sock->msglen = pm_strcpy(&sock->msg, line);
654 add_history(sock->msg);
660 #else /* no readline, do it ourselves */
663 * Returns: 1 if data available
668 wait_for_data(int fd, int sec)
673 return 1; /* select doesn't seem to work on Win32 */
680 FD_SET((unsigned)fd, &fdset);
681 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
682 case 0: /* timeout */
685 if (errno == EINTR || errno == EAGAIN) {
688 return -1; /* error return */
696 * Get next input command from terminal.
698 * Returns: 1 if got input
703 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
707 if (output == stdout || tee) {
712 switch (wait_for_data(fileno(input), sec)) {
714 return 0; /* timeout */
716 return -1; /* error */
718 len = sizeof_pool_memory(sock->msg) - 1;
724 if (isatty(fileno(input))) {
725 input_line(sock->msg, len);
729 #ifdef HAVE_WIN32 /* use special console for input on win32 */
730 if (input == stdin) {
731 if (win32_cgets(sock->msg, len) == NULL) {
737 if (fgets(sock->msg, len, input) == NULL) {
746 strip_trailing_junk(sock->msg);
747 sock->msglen = strlen(sock->msg);
753 static int versioncmd(FILE *input, BSOCK *UA_sock)
755 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
756 HOST_OS, DISTNAME, DISTVER);
760 static int inputcmd(FILE *input, BSOCK *UA_sock)
765 sendit(_("Too many arguments on input command.\n"));
769 sendit(_("First argument to input command must be a filename.\n"));
772 fd = fopen(argk[1], "rb");
774 senditf(_("Cannot open file %s for input. ERR=%s\n"),
775 argk[1], strerror(errno));
778 read_and_process_input(fd, UA_sock);
783 /* Send output to both termina and specified file */
784 static int teecmd(FILE *input, BSOCK *UA_sock)
787 return do_outputcmd(input, UA_sock);
790 /* Send output to specified "file" */
791 static int outputcmd(FILE *input, BSOCK *UA_sock)
794 return do_outputcmd(input, UA_sock);
798 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
801 const char *mode = "a+b";
804 sendit(_("Too many arguments on output/tee command.\n"));
808 if (output != stdout) {
818 fd = fopen(argk[1], mode);
820 senditf(_("Cannot open file %s for output. ERR=%s\n"),
821 argk[1], strerror(errno));
828 static int quitcmd(FILE *input, BSOCK *UA_sock)
833 static int sleepcmd(FILE *input, BSOCK *UA_sock)
836 sleep(atoi(argk[1]));
842 static int timecmd(FILE *input, BSOCK *UA_sock)
845 time_t ttime = time(NULL);
847 (void)localtime_r(&ttime, &tm);
848 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
854 * Send a line to the output file and or the terminal
856 void senditf(const char *fmt,...)
861 va_start(arg_ptr, fmt);
862 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
867 void sendit(const char *buf)
870 if (output == stdout || tee) {
873 * Here, we convert every \n into \r\n because the
874 * terminal is in raw mode when we are using
877 for (p=q=buf; (p=strchr(q, '\n')); ) {
882 q = ++p; /* point after \n */
888 if (output != stdout) {
898 if (output != stdout || tee) {