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"
35 #define con_set_zed_keys();
41 #if defined(HAVE_WIN32)
42 #define isatty(fd) (fd==0)
45 /* Exported variables */
47 extern int rl_catch_signals;
49 /* Imported functions */
50 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
52 /* Forward referenced functions */
53 static void terminate_console(int sig);
54 static int check_resources();
55 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
56 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
57 void senditf(const char *fmt, ...);
58 void sendit(const char *buf);
60 extern "C" void got_sigstop(int sig);
61 extern "C" void got_sigcontinue(int sig);
62 extern "C" void got_sigtout(int sig);
63 extern "C" void got_sigtin(int sig);
66 /* Static variables */
67 static char *configfile = NULL;
68 static BSOCK *UA_sock = NULL;
70 static FILE *output = stdout;
71 static bool teeout = false; /* output to output and stdout */
72 static bool stop = false;
73 static bool no_conio = false;
77 static char *argk[MAX_CMD_ARGS];
78 static char *argv[MAX_CMD_ARGS];
81 /* Command prototypes */
82 static int versioncmd(FILE *input, BSOCK *UA_sock);
83 static int inputcmd(FILE *input, BSOCK *UA_sock);
84 static int outputcmd(FILE *input, BSOCK *UA_sock);
85 static int teecmd(FILE *input, BSOCK *UA_sock);
86 static int quitcmd(FILE *input, BSOCK *UA_sock);
87 static int timecmd(FILE *input, BSOCK *UA_sock);
88 static int sleepcmd(FILE *input, BSOCK *UA_sock);
91 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
96 "Copyright (C) 2000-%s Kern Sibbald\n"
97 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
98 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
99 " -c <file> set configuration file to file\n"
100 " -dnn set debug level to nn\n"
103 " -t test - read configuration and exit\n"
104 " -? print this message.\n"
105 "\n"), BYEAR, 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)
295 const char *prompt = (const char *)userdata;
296 # if defined(HAVE_WIN32)
298 if (win32_cgets(buf, size) == NULL) {
307 passwd = getpass(prompt);
308 bstrncpy(buf, passwd, size);
318 /*********************************************************************
320 * Main Bacula Console -- User Interface Program
323 int main(int argc, char *argv[])
326 bool no_signals = false;
327 bool test_config = false;
330 setlocale(LC_ALL, "");
331 bindtextdomain("bacula", LOCALEDIR);
332 textdomain("bacula");
335 my_name_is(argc, argv, "bconsole");
336 init_msg(NULL, NULL);
337 working_directory = "/tmp";
338 args = get_pool_memory(PM_FNAME);
340 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
342 case 'c': /* configuration file */
343 if (configfile != NULL) {
346 configfile = bstrdup(optarg);
350 debug_level = atoi(optarg);
351 if (debug_level <= 0) {
356 case 'n': /* no conio */
360 case 's': /* turn off signals */
378 init_signals(terminate_console);
382 #if !defined(HAVE_WIN32)
383 /* Override Bacula default signals */
384 signal(SIGQUIT, SIG_IGN);
385 signal(SIGTSTP, got_sigstop);
386 signal(SIGCONT, got_sigcontinue);
387 signal(SIGTTIN, got_sigtin);
388 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);
418 terminate_console(0);
422 memset(&jcr, 0, sizeof(jcr));
424 (void)WSA_Init(); /* Initialize Windows sockets */
427 struct sockaddr client_addr;
428 memset(&client_addr, 0, sizeof(client_addr));
429 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
431 sendit(_("Available Directors:\n"));
434 foreach_res(dir, R_DIRECTOR) {
435 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
439 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
440 (void)WSACleanup(); /* Cleanup Windows sockets */
443 item = atoi(UA_sock->msg);
444 if (item < 0 || item > numdir) {
445 senditf(_("You must enter a number between 1 and %d\n"), numdir);
450 for (i=0; i<item; i++) {
451 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
457 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
462 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
465 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
468 /* Initialize Console TLS context */
469 if (cons && (cons->tls_enable || cons->tls_require)) {
470 /* Generate passphrase prompt */
471 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
473 /* Initialize TLS context:
474 * Args: CA certfile, CA certdir, Certfile, Keyfile,
475 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
476 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
477 cons->tls_ca_certdir, cons->tls_certfile,
478 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
480 if (!cons->tls_ctx) {
481 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
483 terminate_console(0);
489 /* Initialize Director TLS context */
490 if (dir->tls_enable || dir->tls_require) {
491 /* Generate passphrase prompt */
492 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
494 /* Initialize TLS context:
495 * Args: CA certfile, CA certdir, Certfile, Keyfile,
496 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
497 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
498 dir->tls_ca_certdir, dir->tls_certfile,
499 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
502 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
504 terminate_console(0);
509 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
510 NULL, dir->DIRport, 0);
511 if (UA_sock == NULL) {
512 terminate_console(0);
515 jcr.dir_bsock = UA_sock;
517 /* If cons==NULL, default console will be used */
518 if (!authenticate_director(&jcr, dir, cons)) {
519 terminate_console(0);
523 Dmsg0(40, "Opened connection with Director daemon\n");
525 sendit(_("Enter a period to cancel a command.\n"));
527 /* Run commands in ~/.bconsolerc if any */
528 char *env = getenv("HOME");
531 pm_strcpy(&UA_sock->msg, env);
532 pm_strcat(&UA_sock->msg, "/.bconsolerc");
533 fd = fopen(UA_sock->msg, "rb");
535 read_and_process_input(fd, UA_sock);
540 read_and_process_input(stdin, UA_sock);
543 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
547 terminate_console(0);
552 /* Cleanup and then exit */
553 static void terminate_console(int sig)
556 static bool already_here = false;
558 if (already_here) { /* avoid recursive temination problems */
563 free_pool_memory(args);
567 (void)WSACleanup(); /* Cleanup Windows sockets */
575 * Make a quick check to see that we have all the
578 static int check_resources()
586 foreach_res(director, R_DIRECTOR) {
589 /* tls_require implies tls_enable */
590 if (director->tls_require) {
592 director->tls_enable = true;
594 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
600 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
601 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
602 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
603 " At least one CA certificate store is required.\n"),
604 director->hdr.name, configfile);
610 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
611 "Without that I don't how to speak to the Director :-(\n"), configfile);
616 /* Loop over Consoles */
617 foreach_res(cons, R_CONSOLE) {
618 /* tls_require implies tls_enable */
619 if (cons->tls_require) {
621 cons->tls_enable = true;
623 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
629 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
630 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
631 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
632 cons->hdr.name, configfile);
644 #define READLINE_LIBRARY 1
646 #include "readline.h"
651 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
655 rl_catch_signals = 0; /* do it ourselves */
656 line = readline((char *)prompt); /* cast needed for old readlines */
661 strip_trailing_junk(line);
662 sock->msglen = pm_strcpy(&sock->msg, line);
664 add_history(sock->msg);
670 #else /* no readline, do it ourselves */
672 static bool bisatty(int fd)
681 * Returns: 1 if data available
686 wait_for_data(int fd, int sec)
688 #if defined(HAVE_WIN32)
698 FD_SET((unsigned)fd, &fdset);
699 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
700 case 0: /* timeout */
703 if (errno == EINTR || errno == EAGAIN) {
706 return -1; /* error return */
715 * Get next input command from terminal.
717 * Returns: 1 if got input
722 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
726 if (output == stdout || teeout) {
731 switch (wait_for_data(fileno(input), sec)) {
733 return 0; /* timeout */
735 return -1; /* error */
737 len = sizeof_pool_memory(sock->msg) - 1;
743 if (bisatty(fileno(input))) {
744 input_line(sock->msg, len);
748 #ifdef HAVE_WIN32 /* use special console for input on win32 */
749 if (input == stdin) {
750 if (win32_cgets(sock->msg, len) == NULL) {
756 if (fgets(sock->msg, len, input) == NULL) {
765 strip_trailing_junk(sock->msg);
766 sock->msglen = strlen(sock->msg);
770 #endif /* end non-readline code */
772 static int versioncmd(FILE *input, BSOCK *UA_sock)
774 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
775 HOST_OS, DISTNAME, DISTVER);
779 static int inputcmd(FILE *input, BSOCK *UA_sock)
784 sendit(_("Too many arguments on input command.\n"));
788 sendit(_("First argument to input command must be a filename.\n"));
791 fd = fopen(argk[1], "rb");
793 senditf(_("Cannot open file %s for input. ERR=%s\n"),
794 argk[1], strerror(errno));
797 read_and_process_input(fd, UA_sock);
802 /* Send output to both termina and specified file */
803 static int teecmd(FILE *input, BSOCK *UA_sock)
806 return do_outputcmd(input, UA_sock);
809 /* Send output to specified "file" */
810 static int outputcmd(FILE *input, BSOCK *UA_sock)
813 return do_outputcmd(input, UA_sock);
817 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
820 const char *mode = "a+b";
823 sendit(_("Too many arguments on output/tee command.\n"));
827 if (output != stdout) {
837 fd = fopen(argk[1], mode);
839 senditf(_("Cannot open file %s for output. ERR=%s\n"),
840 argk[1], strerror(errno));
847 static int quitcmd(FILE *input, BSOCK *UA_sock)
852 static int sleepcmd(FILE *input, BSOCK *UA_sock)
855 sleep(atoi(argk[1]));
861 static int timecmd(FILE *input, BSOCK *UA_sock)
864 time_t ttime = time(NULL);
866 (void)localtime_r(&ttime, &tm);
867 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
873 * Send a line to the output file and or the terminal
875 void senditf(const char *fmt,...)
880 va_start(arg_ptr, fmt);
881 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
886 void sendit(const char *buf)
890 if (output == stdout || teeout) {
893 * Here, we convert every \n into \r\n because the
894 * terminal is in raw mode when we are using
897 for (p=q=buf; (p=strchr(q, '\n')); ) {
900 memcpy(obuf, q, len);
902 memcpy(obuf+len, "\r\n", 3);
903 q = ++p; /* point after \n */
911 if (output != stdout) {