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 /* Remove when we have real lib in src/lib */
51 /* Imported functions */
52 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
54 /* Forward referenced functions */
55 static void terminate_console(int sig);
56 static int check_resources();
57 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
58 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
59 void senditf(const char *fmt, ...);
60 void sendit(const char *buf);
62 extern "C" void got_sigstop(int sig);
63 extern "C" void got_sigcontinue(int sig);
64 extern "C" void got_sigtout(int sig);
65 extern "C" void got_sigtin(int sig);
68 /* Static variables */
69 static char *configfile = NULL;
70 static BSOCK *UA_sock = NULL;
72 static FILE *output = stdout;
73 static bool tee = false; /* output to output and stdout */
74 static bool stop = false;
78 static char *argk[MAX_CMD_ARGS];
79 static char *argv[MAX_CMD_ARGS];
82 /* Command prototypes */
83 static int versioncmd(FILE *input, BSOCK *UA_sock);
84 static int inputcmd(FILE *input, BSOCK *UA_sock);
85 static int outputcmd(FILE *input, BSOCK *UA_sock);
86 static int teecmd(FILE *input, BSOCK *UA_sock);
87 static int quitcmd(FILE *input, BSOCK *UA_sock);
88 static int timecmd(FILE *input, BSOCK *UA_sock);
89 static int sleepcmd(FILE *input, BSOCK *UA_sock);
92 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
97 "Copyright (C) 2000-2005 Kern Sibbald\n"
98 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
99 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
100 " -c <file> set configuration file to file\n"
101 " -dnn set debug level to nn\n"
103 " -t test - read configuration and exit\n"
104 " -? print this message.\n"
105 "\n"), HOST_OS, DISTNAME, DISTVER);
110 void got_sigstop(int sig)
116 void got_sigcontinue(int sig)
122 void got_sigtout(int sig)
124 // printf("Got tout\n");
128 void got_sigtin(int sig)
130 // printf("Got tin\n");
134 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
141 * These are the @command
143 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
144 static struct cmdstruct commands[] = {
145 { N_("input"), inputcmd, _("input from file")},
146 { N_("output"), outputcmd, _("output to file")},
147 { N_("quit"), quitcmd, _("quit")},
148 { N_("tee"), teecmd, _("output to file and terminal")},
149 { N_("sleep"), sleepcmd, _("sleep specified time")},
150 { N_("time"), timecmd, _("print current time")},
151 { N_("version"), versioncmd, _("print Console's version")},
152 { N_("exit"), quitcmd, _("exit = quit")},
153 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
155 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
157 static int do_a_command(FILE *input, BSOCK *UA_sock)
168 Dmsg1(120, "Command: %s\n", UA_sock->msg);
174 if (*cmd == '#') { /* comment */
178 for (i=0; i<comsize; i++) { /* search for command */
179 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
180 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
186 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
187 UA_sock->msglen = strlen(UA_sock->msg);
188 sendit(UA_sock->msg);
194 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
196 const char *prompt = "*";
197 bool at_prompt = false;
198 int tty_input = isatty(fileno(input));
202 if (at_prompt) { /* don't prompt multiple times */
209 stat = get_cmd(input, prompt, UA_sock, 30);
217 /* Reading input from a file */
218 int len = sizeof_pool_memory(UA_sock->msg) - 1;
222 if (fgets(UA_sock->msg, len, input) == NULL) {
225 sendit(UA_sock->msg); /* echo to terminal */
226 strip_trailing_junk(UA_sock->msg);
227 UA_sock->msglen = strlen(UA_sock->msg);
232 break; /* error or interrupt */
233 } else if (stat == 0) { /* timeout */
234 if (strcmp(prompt, "*") == 0) {
235 bnet_fsend(UA_sock, ".messages");
241 /* @ => internal command for us */
242 if (UA_sock->msg[0] == '@') {
243 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
244 if (!do_a_command(input, UA_sock)) {
249 if (!bnet_send(UA_sock)) { /* send command */
253 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
256 while ((stat = bnet_recv(UA_sock)) >= 0) {
263 /* Suppress output if running in background or user hit ctl-c */
264 if (!stop && !usrbrk()) {
265 sendit(UA_sock->msg);
276 if (is_bnet_stop(UA_sock)) {
277 break; /* error or term */
278 } else if (stat == BNET_SIGNAL) {
279 if (UA_sock->msglen == BNET_PROMPT) {
282 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
288 * Call-back for reading a passphrase for an encrypted PEM file
289 * This function uses getpass(),
290 * which uses a static buffer and is NOT thread-safe.
292 static int tls_pem_callback(char *buf, int size, const void *userdata)
296 const char *prompt = (const char *)userdata;
298 if (win32_cgets(buf, size) == NULL) {
305 const char *prompt = (const char *)userdata;
308 passwd = getpass(prompt);
309 bstrncpy(buf, passwd, size);
319 /*********************************************************************
321 * Main Bacula Console -- User Interface Program
324 int main(int argc, char *argv[])
327 bool no_signals = false;
328 bool test_config = false;
331 setlocale(LC_ALL, "");
332 bindtextdomain("bacula", LOCALEDIR);
333 textdomain("bacula");
336 my_name_is(argc, argv, "bconsole");
337 init_msg(NULL, NULL);
338 working_directory = "/tmp";
339 args = get_pool_memory(PM_FNAME);
342 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
344 case 'c': /* configuration file */
345 if (configfile != NULL) {
348 configfile = bstrdup(optarg);
352 debug_level = atoi(optarg);
353 if (debug_level <= 0) {
358 case 's': /* turn off signals */
377 init_signals(terminate_console);
381 #if !defined(HAVE_WIN32)
382 /* Override Bacula default signals */
383 signal(SIGQUIT, SIG_IGN);
384 signal(SIGTSTP, got_sigstop);
385 signal(SIGCONT, got_sigcontinue);
386 signal(SIGTTIN, got_sigtin);
387 signal(SIGTTOU, got_sigtout);
399 if (configfile == NULL) {
400 configfile = bstrdup(CONFIG_FILE);
403 parse_config(configfile);
405 if (init_crypto() != 0) {
406 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
409 if (!check_resources()) {
410 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
414 terminate_console(0);
418 memset(&jcr, 0, sizeof(jcr));
420 (void)WSA_Init(); /* Initialize Windows sockets */
423 struct sockaddr client_addr;
424 memset(&client_addr, 0, sizeof(client_addr));
425 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
427 sendit(_("Available Directors:\n"));
430 foreach_res(dir, R_DIRECTOR) {
431 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
435 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
436 (void)WSACleanup(); /* Cleanup Windows sockets */
439 item = atoi(UA_sock->msg);
440 if (item < 0 || item > numdir) {
441 senditf(_("You must enter a number between 1 and %d\n"), numdir);
446 for (i=0; i<item; i++) {
447 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
453 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
458 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
461 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
464 /* Initialize Console TLS context */
465 if (cons && (cons->tls_enable || cons->tls_require)) {
466 /* Generate passphrase prompt */
467 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
469 /* Initialize TLS context:
470 * Args: CA certfile, CA certdir, Certfile, Keyfile,
471 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
472 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
473 cons->tls_ca_certdir, cons->tls_certfile,
474 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
476 if (!cons->tls_ctx) {
477 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
479 terminate_console(0);
485 /* Initialize Director TLS context */
486 if (dir->tls_enable || dir->tls_require) {
487 /* Generate passphrase prompt */
488 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
490 /* Initialize TLS context:
491 * Args: CA certfile, CA certdir, Certfile, Keyfile,
492 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
493 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
494 dir->tls_ca_certdir, dir->tls_certfile,
495 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
498 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
500 terminate_console(0);
505 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
506 NULL, dir->DIRport, 0);
507 if (UA_sock == NULL) {
508 terminate_console(0);
511 jcr.dir_bsock = UA_sock;
513 /* If cons==NULL, default console will be used */
514 if (!authenticate_director(&jcr, dir, cons)) {
515 terminate_console(0);
519 Dmsg0(40, "Opened connection with Director daemon\n");
521 sendit(_("Enter a period to cancel a command.\n"));
523 /* Run commands in ~/.bconsolerc if any */
524 char *env = getenv("HOME");
527 pm_strcpy(&UA_sock->msg, env);
528 pm_strcat(&UA_sock->msg, "/.bconsolerc");
529 fd = fopen(UA_sock->msg, "rb");
531 read_and_process_input(fd, UA_sock);
536 read_and_process_input(stdin, UA_sock);
539 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
543 terminate_console(0);
548 /* Cleanup and then exit */
549 static void terminate_console(int sig)
552 static bool already_here = false;
554 if (already_here) { /* avoid recursive temination problems */
559 free_pool_memory(args);
561 (void)WSACleanup(); /* Cleanup Windows sockets */
569 * Make a quick check to see that we have all the
572 static int check_resources()
580 foreach_res(director, R_DIRECTOR) {
583 /* tls_require implies tls_enable */
584 if (director->tls_require) {
586 director->tls_enable = true;
588 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
594 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
595 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
596 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
597 " At least one CA certificate store is required.\n"),
598 director->hdr.name, configfile);
604 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
605 "Without that I don't how to speak to the Director :-(\n"), configfile);
610 /* Loop over Consoles */
611 foreach_res(cons, R_CONSOLE) {
612 /* tls_require implies tls_enable */
613 if (cons->tls_require) {
615 cons->tls_enable = true;
617 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
623 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
624 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
625 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
626 cons->hdr.name, configfile);
638 #define READLINE_LIBRARY 1
640 #include "readline.h"
645 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
649 rl_catch_signals = 0; /* do it ourselves */
650 line = readline((char *)prompt); /* cast needed for old readlines */
655 strip_trailing_junk(line);
656 sock->msglen = pm_strcpy(&sock->msg, line);
658 add_history(sock->msg);
664 #else /* no readline, do it ourselves */
667 * Returns: 1 if data available
672 wait_for_data(int fd, int sec)
677 return 1; /* select doesn't seem to work on Win32 */
684 FD_SET((unsigned)fd, &fdset);
685 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
686 case 0: /* timeout */
689 if (errno == EINTR || errno == EAGAIN) {
692 return -1; /* error return */
700 * Get next input command from terminal.
702 * Returns: 1 if got input
707 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
711 if (output == stdout || tee) {
716 switch (wait_for_data(fileno(input), sec)) {
718 return 0; /* timeout */
720 return -1; /* error */
722 len = sizeof_pool_memory(sock->msg) - 1;
728 if (isatty(fileno(input))) {
729 input_line(sock->msg, len);
733 #ifdef HAVE_WIN32 /* use special console for input on win32 */
734 if (input == stdin) {
735 if (win32_cgets(sock->msg, len) == NULL) {
741 if (fgets(sock->msg, len, input) == NULL) {
750 strip_trailing_junk(sock->msg);
751 sock->msglen = strlen(sock->msg);
757 static int versioncmd(FILE *input, BSOCK *UA_sock)
759 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
760 HOST_OS, DISTNAME, DISTVER);
764 static int inputcmd(FILE *input, BSOCK *UA_sock)
769 sendit(_("Too many arguments on input command.\n"));
773 sendit(_("First argument to input command must be a filename.\n"));
776 fd = fopen(argk[1], "rb");
778 senditf(_("Cannot open file %s for input. ERR=%s\n"),
779 argk[1], strerror(errno));
782 read_and_process_input(fd, UA_sock);
787 /* Send output to both termina and specified file */
788 static int teecmd(FILE *input, BSOCK *UA_sock)
791 return do_outputcmd(input, UA_sock);
794 /* Send output to specified "file" */
795 static int outputcmd(FILE *input, BSOCK *UA_sock)
798 return do_outputcmd(input, UA_sock);
802 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
805 const char *mode = "a+b";
808 sendit(_("Too many arguments on output/tee command.\n"));
812 if (output != stdout) {
822 fd = fopen(argk[1], mode);
824 senditf(_("Cannot open file %s for output. ERR=%s\n"),
825 argk[1], strerror(errno));
832 static int quitcmd(FILE *input, BSOCK *UA_sock)
837 static int sleepcmd(FILE *input, BSOCK *UA_sock)
840 sleep(atoi(argk[1]));
846 static int timecmd(FILE *input, BSOCK *UA_sock)
849 time_t ttime = time(NULL);
851 (void)localtime_r(&ttime, &tm);
852 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
858 * Send a line to the output file and or the terminal
860 void senditf(const char *fmt,...)
865 va_start(arg_ptr, fmt);
866 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
871 void sendit(const char *buf)
874 if (output == stdout || tee) {
877 * Here, we convert every \n into \r\n because the
878 * terminal is in raw mode when we are using
881 for (p=q=buf; (p=strchr(q, '\n')); ) {
886 q = ++p; /* point after \n */
892 if (output != stdout) {
902 if (output != stdout || tee) {