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 /* Remove when we have real lib in src/lib */
57 /* Imported functions */
58 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
60 /* Forward referenced functions */
61 static void terminate_console(int sig);
62 static int check_resources();
63 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
64 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
65 void senditf(const char *fmt, ...);
66 void sendit(const char *buf);
68 extern "C" void got_sigstop(int sig);
69 extern "C" void got_sigcontinue(int sig);
70 extern "C" void got_sigtout(int sig);
71 extern "C" void got_sigtin(int sig);
74 /* Static variables */
75 static char *configfile = NULL;
76 static BSOCK *UA_sock = NULL;
78 static FILE *output = stdout;
79 static bool tee = false; /* output to output and stdout */
80 static bool stop = false;
84 static char *argk[MAX_CMD_ARGS];
85 static char *argv[MAX_CMD_ARGS];
88 /* Command prototypes */
89 static int versioncmd(FILE *input, BSOCK *UA_sock);
90 static int inputcmd(FILE *input, BSOCK *UA_sock);
91 static int outputcmd(FILE *input, BSOCK *UA_sock);
92 static int teecmd(FILE *input, BSOCK *UA_sock);
93 static int quitcmd(FILE *input, BSOCK *UA_sock);
94 static int timecmd(FILE *input, BSOCK *UA_sock);
95 static int sleepcmd(FILE *input, BSOCK *UA_sock);
98 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
103 "Copyright (C) 2000-2005 Kern Sibbald\n"
104 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
105 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
106 " -c <file> set configuration file to file\n"
107 " -dnn set debug level to nn\n"
109 " -t test - read configuration and exit\n"
110 " -? print this message.\n"
111 "\n"), HOST_OS, DISTNAME, DISTVER);
116 void got_sigstop(int sig)
122 void got_sigcontinue(int sig)
128 void got_sigtout(int sig)
130 // printf("Got tout\n");
134 void got_sigtin(int sig)
136 // printf("Got tin\n");
140 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
147 * These are the @command
149 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
150 static struct cmdstruct commands[] = {
151 { N_("input"), inputcmd, _("input from file")},
152 { N_("output"), outputcmd, _("output to file")},
153 { N_("quit"), quitcmd, _("quit")},
154 { N_("tee"), teecmd, _("output to file and terminal")},
155 { N_("sleep"), sleepcmd, _("sleep specified time")},
156 { N_("time"), timecmd, _("print current time")},
157 { N_("version"), versioncmd, _("print Console's version")},
158 { N_("exit"), quitcmd, _("exit = quit")},
159 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
161 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
163 static int do_a_command(FILE *input, BSOCK *UA_sock)
174 Dmsg1(120, "Command: %s\n", UA_sock->msg);
180 if (*cmd == '#') { /* comment */
184 for (i=0; i<comsize; i++) { /* search for command */
185 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
186 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
192 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
193 UA_sock->msglen = strlen(UA_sock->msg);
194 sendit(UA_sock->msg);
200 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
202 const char *prompt = "*";
203 bool at_prompt = false;
204 int tty_input = isatty(fileno(input));
208 if (at_prompt) { /* don't prompt multiple times */
215 stat = get_cmd(input, prompt, UA_sock, 30);
223 /* Reading input from a file */
224 int len = sizeof_pool_memory(UA_sock->msg) - 1;
228 if (fgets(UA_sock->msg, len, input) == NULL) {
231 sendit(UA_sock->msg); /* echo to terminal */
232 strip_trailing_junk(UA_sock->msg);
233 UA_sock->msglen = strlen(UA_sock->msg);
238 break; /* error or interrupt */
239 } else if (stat == 0) { /* timeout */
240 if (strcmp(prompt, "*") == 0) {
241 bnet_fsend(UA_sock, ".messages");
247 /* @ => internal command for us */
248 if (UA_sock->msg[0] == '@') {
249 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
250 if (!do_a_command(input, UA_sock)) {
255 if (!bnet_send(UA_sock)) { /* send command */
259 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
262 while ((stat = bnet_recv(UA_sock)) >= 0) {
269 /* Suppress output if running in background or user hit ctl-c */
270 if (!stop && !usrbrk()) {
271 sendit(UA_sock->msg);
282 if (is_bnet_stop(UA_sock)) {
283 break; /* error or term */
284 } else if (stat == BNET_SIGNAL) {
285 if (UA_sock->msglen == BNET_PROMPT) {
288 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
294 * Call-back for reading a passphrase for an encrypted PEM file
295 * This function uses getpass(),
296 * which uses a static buffer and is NOT thread-safe.
298 static int tls_pem_callback(char *buf, int size, const void *userdata)
302 const char *prompt = (const char *)userdata;
304 if (win32_cgets(buf, size) == NULL) {
311 const char *prompt = (const char *)userdata;
314 passwd = getpass(prompt);
315 bstrncpy(buf, passwd, size);
325 /*********************************************************************
327 * Main Bacula Console -- User Interface Program
330 int main(int argc, char *argv[])
333 bool no_signals = false;
334 bool test_config = false;
337 setlocale(LC_ALL, "");
338 bindtextdomain("bacula", LOCALEDIR);
339 textdomain("bacula");
342 my_name_is(argc, argv, "bconsole");
343 init_msg(NULL, NULL);
344 working_directory = "/tmp";
345 args = get_pool_memory(PM_FNAME);
348 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
350 case 'c': /* configuration file */
351 if (configfile != NULL) {
354 configfile = bstrdup(optarg);
358 debug_level = atoi(optarg);
359 if (debug_level <= 0) {
364 case 's': /* turn off signals */
383 init_signals(terminate_console);
386 #if !defined(HAVE_WIN32)
387 /* Override Bacula default signals */
388 // signal(SIGCHLD, SIG_IGN);
389 signal(SIGQUIT, SIG_IGN);
390 signal(SIGTSTP, got_sigstop);
391 signal(SIGCONT, got_sigcontinue);
392 signal(SIGTTIN, got_sigtin);
393 signal(SIGTTOU, got_sigtout);
406 if (configfile == NULL) {
407 configfile = bstrdup(CONFIG_FILE);
410 parse_config(configfile);
412 if (init_crypto() != 0) {
413 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
416 if (!check_resources()) {
417 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
421 terminate_console(0);
425 memset(&jcr, 0, sizeof(jcr));
427 (void)WSA_Init(); /* Initialize Windows sockets */
430 struct sockaddr client_addr;
431 memset(&client_addr, 0, sizeof(client_addr));
432 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
434 sendit(_("Available Directors:\n"));
437 foreach_res(dir, R_DIRECTOR) {
438 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
442 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
443 (void)WSACleanup(); /* Cleanup Windows sockets */
446 item = atoi(UA_sock->msg);
447 if (item < 0 || item > numdir) {
448 senditf(_("You must enter a number between 1 and %d\n"), numdir);
453 for (i=0; i<item; i++) {
454 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
460 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
465 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
468 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
471 /* Initialize Console TLS context */
472 if (cons && (cons->tls_enable || cons->tls_require)) {
473 /* Generate passphrase prompt */
474 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
476 /* Initialize TLS context:
477 * Args: CA certfile, CA certdir, Certfile, Keyfile,
478 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
479 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
480 cons->tls_ca_certdir, cons->tls_certfile,
481 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
483 if (!cons->tls_ctx) {
484 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
486 terminate_console(0);
492 /* Initialize Director TLS context */
493 if (dir->tls_enable || dir->tls_require) {
494 /* Generate passphrase prompt */
495 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
497 /* Initialize TLS context:
498 * Args: CA certfile, CA certdir, Certfile, Keyfile,
499 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
500 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
501 dir->tls_ca_certdir, dir->tls_certfile,
502 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
505 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
507 terminate_console(0);
512 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
513 NULL, dir->DIRport, 0);
514 if (UA_sock == NULL) {
515 terminate_console(0);
518 jcr.dir_bsock = UA_sock;
520 /* If cons==NULL, default console will be used */
521 if (!authenticate_director(&jcr, dir, cons)) {
522 terminate_console(0);
526 Dmsg0(40, "Opened connection with Director daemon\n");
528 sendit(_("Enter a period to cancel a command.\n"));
530 /* Run commands in ~/.bconsolerc if any */
531 char *env = getenv("HOME");
534 pm_strcpy(&UA_sock->msg, env);
535 pm_strcat(&UA_sock->msg, "/.bconsolerc");
536 fd = fopen(UA_sock->msg, "r");
538 read_and_process_input(fd, UA_sock);
543 read_and_process_input(stdin, UA_sock);
546 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
550 terminate_console(0);
555 /* Cleanup and then exit */
556 static void terminate_console(int sig)
559 static bool already_here = false;
561 if (already_here) { /* avoid recursive temination problems */
566 free_pool_memory(args);
568 (void)WSACleanup(); /* Cleanup Windows sockets */
576 * Make a quick check to see that we have all the
579 static int check_resources()
587 foreach_res(director, R_DIRECTOR) {
590 /* tls_require implies tls_enable */
591 if (director->tls_require) {
593 director->tls_enable = true;
595 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
601 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
602 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
603 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
604 " At least one CA certificate store is required.\n"),
605 director->hdr.name, configfile);
611 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
612 "Without that I don't how to speak to the Director :-(\n"), configfile);
617 /* Loop over Consoles */
618 foreach_res(cons, R_CONSOLE) {
619 /* tls_require implies tls_enable */
620 if (cons->tls_require) {
622 cons->tls_enable = true;
624 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
630 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
631 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
632 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
633 cons->hdr.name, configfile);
645 #define READLINE_LIBRARY 1
647 #include "readline.h"
652 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
656 rl_catch_signals = 0; /* do it ourselves */
657 line = readline((char *)prompt); /* cast needed for old readlines */
662 strip_trailing_junk(line);
663 sock->msglen = pm_strcpy(&sock->msg, line);
665 add_history(sock->msg);
671 #else /* no readline, do it ourselves */
674 * Returns: 1 if data available
679 wait_for_data(int fd, int sec)
684 return 1; /* select doesn't seem to work on Win32 */
691 FD_SET((unsigned)fd, &fdset);
692 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
693 case 0: /* timeout */
696 if (errno == EINTR || errno == EAGAIN) {
699 return -1; /* error return */
707 * Get next input command from terminal.
709 * Returns: 1 if got input
714 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
718 if (output == stdout || tee) {
723 switch (wait_for_data(fileno(input), sec)) {
725 return 0; /* timeout */
727 return -1; /* error */
729 len = sizeof_pool_memory(sock->msg) - 1;
735 if (isatty(fileno(input))) {
736 input_line(sock->msg, len);
740 #ifdef HAVE_WIN32 /* use special console for input on win32 */
741 if (input == stdin) {
742 if (win32_cgets(sock->msg, len) == NULL) {
748 if (fgets(sock->msg, len, input) == NULL) {
757 strip_trailing_junk(sock->msg);
758 sock->msglen = strlen(sock->msg);
764 static int versioncmd(FILE *input, BSOCK *UA_sock)
766 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
767 HOST_OS, DISTNAME, DISTVER);
771 static int inputcmd(FILE *input, BSOCK *UA_sock)
776 sendit(_("Too many arguments on input command.\n"));
780 sendit(_("First argument to input command must be a filename.\n"));
783 fd = fopen(argk[1], "r");
785 senditf(_("Cannot open file %s for input. ERR=%s\n"),
786 argk[1], strerror(errno));
789 read_and_process_input(fd, UA_sock);
794 /* Send output to both termina and specified file */
795 static int teecmd(FILE *input, BSOCK *UA_sock)
798 return do_outputcmd(input, UA_sock);
801 /* Send output to specified "file" */
802 static int outputcmd(FILE *input, BSOCK *UA_sock)
805 return do_outputcmd(input, UA_sock);
809 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
812 const char *mode = "a+";
815 sendit(_("Too many arguments on output/tee command.\n"));
819 if (output != stdout) {
829 fd = fopen(argk[1], mode);
831 senditf(_("Cannot open file %s for output. ERR=%s\n"),
832 argk[1], strerror(errno));
839 static int quitcmd(FILE *input, BSOCK *UA_sock)
844 static int sleepcmd(FILE *input, BSOCK *UA_sock)
847 sleep(atoi(argk[1]));
853 static int timecmd(FILE *input, BSOCK *UA_sock)
856 time_t ttime = time(NULL);
858 localtime_r(&ttime, &tm);
859 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
865 * Send a line to the output file and or the terminal
867 void senditf(const char *fmt,...)
872 va_start(arg_ptr, fmt);
873 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
878 void sendit(const char *buf)
881 if (output == stdout || tee) {
884 * Here, we convert every \n into \r\n because the
885 * terminal is in raw mode when we are using
888 for (p=q=buf; (p=strchr(q, '\n')); ) {
893 q = ++p; /* point after \n */
899 if (output != stdout) {
909 if (output != stdout || tee) {