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));
313 /*********************************************************************
315 * Main Bacula Console -- User Interface Program
318 int main(int argc, char *argv[])
321 bool no_signals = false;
322 bool test_config = false;
326 my_name_is(argc, argv, "bconsole");
327 textdomain("bacula");
328 init_msg(NULL, NULL);
329 working_directory = "/tmp";
330 args = get_pool_memory(PM_FNAME);
333 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
335 case 'c': /* configuration file */
336 if (configfile != NULL) {
339 configfile = bstrdup(optarg);
343 debug_level = atoi(optarg);
344 if (debug_level <= 0) {
349 case 's': /* turn off signals */
368 init_signals(terminate_console);
371 #if !defined(HAVE_WIN32)
372 /* Override Bacula default signals */
373 // signal(SIGCHLD, SIG_IGN);
374 signal(SIGQUIT, SIG_IGN);
375 signal(SIGTSTP, got_sigstop);
376 signal(SIGCONT, got_sigcontinue);
377 signal(SIGTTIN, got_sigtin);
378 signal(SIGTTOU, got_sigtout);
391 if (configfile == NULL) {
392 configfile = bstrdup(CONFIG_FILE);
395 parse_config(configfile);
397 if (init_tls() != 0) {
398 Emsg0(M_ERROR_TERM, 0, _("TLS library initialization failed.\n"));
401 if (!check_resources()) {
402 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
406 terminate_console(0);
410 memset(&jcr, 0, sizeof(jcr));
412 (void)WSA_Init(); /* Initialize Windows sockets */
415 struct sockaddr client_addr;
416 memset(&client_addr, 0, sizeof(client_addr));
417 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
419 sendit(_("Available Directors:\n"));
422 foreach_res(dir, R_DIRECTOR) {
423 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
427 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
428 (void)WSACleanup(); /* Cleanup Windows sockets */
431 item = atoi(UA_sock->msg);
432 if (item < 0 || item > numdir) {
433 senditf(_("You must enter a number between 1 and %d\n"), numdir);
438 for (i=0; i<item; i++) {
439 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
445 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
450 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
453 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
456 /* Initialize Console TLS context */
457 if (cons && (cons->tls_enable || cons->tls_require)) {
458 /* Generate passphrase prompt */
459 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
461 /* Initialize TLS context:
462 * Args: CA certfile, CA certdir, Certfile, Keyfile,
463 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
464 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
465 cons->tls_ca_certdir, cons->tls_certfile,
466 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
468 if (!cons->tls_ctx) {
469 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
471 terminate_console(0);
477 /* Initialize Director TLS context */
478 if (dir->tls_enable || dir->tls_require) {
479 /* Generate passphrase prompt */
480 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
482 /* Initialize TLS context:
483 * Args: CA certfile, CA certdir, Certfile, Keyfile,
484 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
485 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
486 dir->tls_ca_certdir, dir->tls_certfile,
487 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
490 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
492 terminate_console(0);
497 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
498 NULL, dir->DIRport, 0);
499 if (UA_sock == NULL) {
500 terminate_console(0);
503 jcr.dir_bsock = UA_sock;
505 /* If cons==NULL, default console will be used */
506 if (!authenticate_director(&jcr, dir, cons)) {
507 terminate_console(0);
511 Dmsg0(40, "Opened connection with Director daemon\n");
513 sendit(_("Enter a period to cancel a command.\n"));
515 /* Run commands in ~/.bconsolerc if any */
516 char *env = getenv("HOME");
519 pm_strcpy(&UA_sock->msg, env);
520 pm_strcat(&UA_sock->msg, "/.bconsolerc");
521 fd = fopen(UA_sock->msg, "r");
523 read_and_process_input(fd, UA_sock);
528 read_and_process_input(stdin, UA_sock);
531 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
535 terminate_console(0);
540 /* Cleanup and then exit */
541 static void terminate_console(int sig)
544 static bool already_here = false;
546 if (already_here) { /* avoid recursive temination problems */
551 free_pool_memory(args);
553 (void)WSACleanup(); /* Cleanup Windows sockets */
561 * Make a quick check to see that we have all the
564 static int check_resources()
572 foreach_res(director, R_DIRECTOR) {
575 /* tls_require implies tls_enable */
576 if (director->tls_require) {
578 director->tls_enable = true;
580 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
586 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
587 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
588 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
589 " At least one CA certificate store is required.\n"),
590 director->hdr.name, configfile);
596 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
597 "Without that I don't how to speak to the Director :-(\n"), configfile);
602 /* Loop over Consoles */
603 foreach_res(cons, R_CONSOLE) {
604 /* tls_require implies tls_enable */
605 if (cons->tls_require) {
607 cons->tls_enable = true;
609 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
615 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
616 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
617 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
618 cons->hdr.name, configfile);
630 #define READLINE_LIBRARY 1
632 #include "readline.h"
637 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
641 rl_catch_signals = 0; /* do it ourselves */
642 line = readline((char *)prompt); /* cast needed for old readlines */
647 strip_trailing_junk(line);
648 sock->msglen = pm_strcpy(&sock->msg, line);
650 add_history(sock->msg);
656 #else /* no readline, do it ourselves */
659 * Returns: 1 if data available
664 wait_for_data(int fd, int sec)
669 return 1; /* select doesn't seem to work on Win32 */
676 FD_SET((unsigned)fd, &fdset);
677 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
678 case 0: /* timeout */
681 if (errno == EINTR || errno == EAGAIN) {
684 return -1; /* error return */
692 * Get next input command from terminal.
694 * Returns: 1 if got input
699 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
703 if (output == stdout || tee) {
708 switch (wait_for_data(fileno(input), sec)) {
710 return 0; /* timeout */
712 return -1; /* error */
714 len = sizeof_pool_memory(sock->msg) - 1;
720 if (isatty(fileno(input))) {
721 input_line(sock->msg, len);
725 #ifdef HAVE_WIN32 /* use special console for input on win32 */
726 if (input == stdin) {
727 if (win32_cgets(sock->msg, len) == NULL) {
733 if (fgets(sock->msg, len, input) == NULL) {
742 strip_trailing_junk(sock->msg);
743 sock->msglen = strlen(sock->msg);
749 static int versioncmd(FILE *input, BSOCK *UA_sock)
751 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
752 HOST_OS, DISTNAME, DISTVER);
756 static int inputcmd(FILE *input, BSOCK *UA_sock)
761 sendit(_("Too many arguments on input command.\n"));
765 sendit(_("First argument to input command must be a filename.\n"));
768 fd = fopen(argk[1], "r");
770 senditf(_("Cannot open file %s for input. ERR=%s\n"),
771 argk[1], strerror(errno));
774 read_and_process_input(fd, UA_sock);
779 /* Send output to both termina and specified file */
780 static int teecmd(FILE *input, BSOCK *UA_sock)
783 return do_outputcmd(input, UA_sock);
786 /* Send output to specified "file" */
787 static int outputcmd(FILE *input, BSOCK *UA_sock)
790 return do_outputcmd(input, UA_sock);
794 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
797 const char *mode = "a+";
800 sendit(_("Too many arguments on output/tee command.\n"));
804 if (output != stdout) {
814 fd = fopen(argk[1], mode);
816 senditf(_("Cannot open file %s for output. ERR=%s\n"),
817 argk[1], strerror(errno));
824 static int quitcmd(FILE *input, BSOCK *UA_sock)
829 static int sleepcmd(FILE *input, BSOCK *UA_sock)
832 sleep(atoi(argk[1]));
838 static int timecmd(FILE *input, BSOCK *UA_sock)
841 time_t ttime = time(NULL);
843 localtime_r(&ttime, &tm);
844 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
850 * Send a line to the output file and or the terminal
852 void senditf(const char *fmt,...)
857 va_start(arg_ptr, fmt);
858 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
863 void sendit(const char *buf)
866 if (output == stdout || tee) {
869 * Here, we convert every \n into \r\n because the
870 * terminal is in raw mode when we are using
873 for (p=q=buf; (p=strchr(q, '\n')); ) {
878 q = ++p; /* point after \n */
884 if (output != stdout) {
894 if (output != stdout || tee) {