3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
38 #include "console_conf.h"
48 #define con_set_zed_keys();
54 #if defined(HAVE_WIN32)
55 #define isatty(fd) (fd==0)
58 /* Exported variables */
60 extern int rl_catch_signals;
62 /* Imported functions */
63 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
65 /* Forward referenced functions */
66 static void terminate_console(int sig);
67 static int check_resources();
68 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
69 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
70 void senditf(const char *fmt, ...);
71 void sendit(const char *buf);
73 extern "C" void got_sigstop(int sig);
74 extern "C" void got_sigcontinue(int sig);
75 extern "C" void got_sigtout(int sig);
76 extern "C" void got_sigtin(int sig);
79 /* Static variables */
80 static char *configfile = NULL;
81 static BSOCK *UA_sock = NULL;
82 static DIRRES *dir = NULL;
83 static CONRES *cons = NULL;
84 static FILE *output = stdout;
85 static bool teeout = false; /* output to output and stdout */
86 static bool stop = false;
87 static bool no_conio = false;
92 static char *argk[MAX_CMD_ARGS];
93 static char *argv[MAX_CMD_ARGS];
96 /* Command prototypes */
97 static int versioncmd(FILE *input, BSOCK *UA_sock);
98 static int inputcmd(FILE *input, BSOCK *UA_sock);
99 static int outputcmd(FILE *input, BSOCK *UA_sock);
100 static int teecmd(FILE *input, BSOCK *UA_sock);
101 static int quitcmd(FILE *input, BSOCK *UA_sock);
102 static int timecmd(FILE *input, BSOCK *UA_sock);
103 static int sleepcmd(FILE *input, BSOCK *UA_sock);
106 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
111 "Copyright (C) 2000-%s Kern Sibbald\n"
112 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
113 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
114 " -c <file> set configuration file to file\n"
115 " -dnn set debug level to nn\n"
118 " -t test - read configuration and exit\n"
119 " -? print this message.\n"
120 "\n"), BYEAR, HOST_OS, DISTNAME, DISTVER);
125 void got_sigstop(int sig)
131 void got_sigcontinue(int sig)
137 void got_sigtout(int sig)
139 // printf("Got tout\n");
143 void got_sigtin(int sig)
145 // printf("Got tin\n");
149 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
156 * These are the @command
158 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
159 static struct cmdstruct commands[] = {
160 { N_("input"), inputcmd, _("input from file")},
161 { N_("output"), outputcmd, _("output to file")},
162 { N_("quit"), quitcmd, _("quit")},
163 { N_("tee"), teecmd, _("output to file and terminal")},
164 { N_("sleep"), sleepcmd, _("sleep specified time")},
165 { N_("time"), timecmd, _("print current time")},
166 { N_("version"), versioncmd, _("print Console's version")},
167 { N_("exit"), quitcmd, _("exit = quit")},
168 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
170 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
172 static int do_a_command(FILE *input, BSOCK *UA_sock)
183 Dmsg1(120, "Command: %s\n", UA_sock->msg);
189 if (*cmd == '#') { /* comment */
193 for (i=0; i<comsize; i++) { /* search for command */
194 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
195 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
201 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
202 UA_sock->msglen = strlen(UA_sock->msg);
203 sendit(UA_sock->msg);
209 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
211 const char *prompt = "*";
212 bool at_prompt = false;
213 int tty_input = isatty(fileno(input));
217 if (at_prompt) { /* don't prompt multiple times */
224 stat = get_cmd(input, prompt, UA_sock, 30);
232 /* Reading input from a file */
233 int len = sizeof_pool_memory(UA_sock->msg) - 1;
237 if (fgets(UA_sock->msg, len, input) == NULL) {
240 sendit(UA_sock->msg); /* echo to terminal */
241 strip_trailing_junk(UA_sock->msg);
242 UA_sock->msglen = strlen(UA_sock->msg);
247 break; /* error or interrupt */
248 } else if (stat == 0) { /* timeout */
249 if (strcmp(prompt, "*") == 0) {
250 bnet_fsend(UA_sock, ".messages");
256 /* @ => internal command for us */
257 if (UA_sock->msg[0] == '@') {
258 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
259 if (!do_a_command(input, UA_sock)) {
264 if (!bnet_send(UA_sock)) { /* send command */
268 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
271 while ((stat = bnet_recv(UA_sock)) >= 0) {
278 /* Suppress output if running in background or user hit ctl-c */
279 if (!stop && !usrbrk()) {
280 sendit(UA_sock->msg);
291 if (is_bnet_stop(UA_sock)) {
292 break; /* error or term */
293 } else if (stat == BNET_SIGNAL) {
294 if (UA_sock->msglen == BNET_PROMPT) {
297 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
303 * Call-back for reading a passphrase for an encrypted PEM file
304 * This function uses getpass(),
305 * which uses a static buffer and is NOT thread-safe.
307 static int tls_pem_callback(char *buf, int size, const void *userdata)
310 const char *prompt = (const char *)userdata;
311 # if defined(HAVE_WIN32)
313 if (win32_cgets(buf, size) == NULL) {
322 passwd = getpass(prompt);
323 bstrncpy(buf, passwd, size);
333 /*********************************************************************
335 * Main Bacula Console -- User Interface Program
338 int main(int argc, char *argv[])
341 bool no_signals = false;
342 bool test_config = false;
345 setlocale(LC_ALL, "");
346 bindtextdomain("bacula", LOCALEDIR);
347 textdomain("bacula");
350 my_name_is(argc, argv, "bconsole");
351 init_msg(NULL, NULL);
352 working_directory = "/tmp";
353 args = get_pool_memory(PM_FNAME);
355 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
357 case 'c': /* configuration file */
358 if (configfile != NULL) {
361 configfile = bstrdup(optarg);
365 debug_level = atoi(optarg);
366 if (debug_level <= 0) {
371 case 'n': /* no conio */
375 case 's': /* turn off signals */
393 init_signals(terminate_console);
397 #if !defined(HAVE_WIN32)
398 /* Override Bacula default signals */
399 signal(SIGQUIT, SIG_IGN);
400 signal(SIGTSTP, got_sigstop);
401 signal(SIGCONT, got_sigcontinue);
402 signal(SIGTTIN, got_sigtin);
403 signal(SIGTTOU, got_sigtout);
414 if (configfile == NULL) {
415 configfile = bstrdup(CONFIG_FILE);
418 parse_config(configfile);
420 if (init_crypto() != 0) {
421 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
424 if (!check_resources()) {
425 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
433 terminate_console(0);
437 memset(&jcr, 0, sizeof(jcr));
439 (void)WSA_Init(); /* Initialize Windows sockets */
443 foreach_res(dir, R_DIRECTOR) {
447 foreach_res(cons, R_CONSOLE) {
453 struct sockaddr client_addr;
454 memset(&client_addr, 0, sizeof(client_addr));
455 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
457 sendit(_("Available Directors:\n"));
460 foreach_res(dir, R_DIRECTOR) {
461 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
465 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
466 (void)WSACleanup(); /* Cleanup Windows sockets */
469 item = atoi(UA_sock->msg);
470 if (item < 0 || item > numdir) {
471 senditf(_("You must enter a number between 1 and %d\n"), numdir);
476 for (i=0; i<item; i++) {
477 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
479 /* Look for a console linked to this director */
480 for (i=0; i<numcon; i++) {
481 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
482 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
487 /* Look for the first non-linked console */
489 for (i=0; i<numcon; i++) {
490 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
491 if (cons->director == NULL)
498 /* If no director, take first one */
501 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
504 /* If no console, take first one */
507 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
511 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
514 /* Initialize Console TLS context */
515 if (cons && (cons->tls_enable || cons->tls_require)) {
516 /* Generate passphrase prompt */
517 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
519 /* Initialize TLS context:
520 * Args: CA certfile, CA certdir, Certfile, Keyfile,
521 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
522 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
523 cons->tls_ca_certdir, cons->tls_certfile,
524 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
526 if (!cons->tls_ctx) {
527 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
529 terminate_console(0);
534 /* Initialize Director TLS context */
535 if (dir->tls_enable || dir->tls_require) {
536 /* Generate passphrase prompt */
537 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
539 /* Initialize TLS context:
540 * Args: CA certfile, CA certdir, Certfile, Keyfile,
541 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
542 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
543 dir->tls_ca_certdir, dir->tls_certfile,
544 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
547 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
549 terminate_console(0);
554 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
555 NULL, dir->DIRport, 0);
556 if (UA_sock == NULL) {
557 terminate_console(0);
560 jcr.dir_bsock = UA_sock;
562 /* If cons==NULL, default console will be used */
563 if (!authenticate_director(&jcr, dir, cons)) {
564 terminate_console(0);
568 Dmsg0(40, "Opened connection with Director daemon\n");
570 sendit(_("Enter a period to cancel a command.\n"));
572 /* Run commands in ~/.bconsolerc if any */
573 char *env = getenv("HOME");
576 pm_strcpy(&UA_sock->msg, env);
577 pm_strcat(&UA_sock->msg, "/.bconsolerc");
578 fd = fopen(UA_sock->msg, "rb");
580 read_and_process_input(fd, UA_sock);
585 read_and_process_input(stdin, UA_sock);
588 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
592 terminate_console(0);
597 /* Cleanup and then exit */
598 static void terminate_console(int sig)
601 static bool already_here = false;
603 if (already_here) { /* avoid recursive temination problems */
608 free_pool_memory(args);
612 (void)WSACleanup(); /* Cleanup Windows sockets */
620 * Make a quick check to see that we have all the
623 static int check_resources()
631 foreach_res(director, R_DIRECTOR) {
634 /* tls_require implies tls_enable */
635 if (director->tls_require) {
637 director->tls_enable = true;
639 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
645 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
646 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
647 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
648 " At least one CA certificate store is required.\n"),
649 director->hdr.name, configfile);
655 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
656 "Without that I don't how to speak to the Director :-(\n"), configfile);
661 /* Loop over Consoles */
662 foreach_res(cons, R_CONSOLE) {
663 /* tls_require implies tls_enable */
664 if (cons->tls_require) {
666 cons->tls_enable = true;
668 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
674 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
675 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
676 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
677 cons->hdr.name, configfile);
689 #define READLINE_LIBRARY 1
691 #include "readline.h"
696 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
700 rl_catch_signals = 0; /* do it ourselves */
701 line = readline((char *)prompt); /* cast needed for old readlines */
706 strip_trailing_junk(line);
707 sock->msglen = pm_strcpy(&sock->msg, line);
709 add_history(sock->msg);
715 #else /* no readline, do it ourselves */
717 #if !defined(HAVE_WIN32)
718 static bool bisatty(int fd)
728 * Returns: 1 if data available
733 wait_for_data(int fd, int sec)
735 #if defined(HAVE_WIN32)
745 FD_SET((unsigned)fd, &fdset);
746 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
747 case 0: /* timeout */
750 if (errno == EINTR || errno == EAGAIN) {
753 return -1; /* error return */
762 * Get next input command from terminal.
764 * Returns: 1 if got input
769 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
773 if (output == stdout || teeout) {
778 switch (wait_for_data(fileno(input), sec)) {
780 return 0; /* timeout */
782 return -1; /* error */
784 len = sizeof_pool_memory(sock->msg) - 1;
790 if (bisatty(fileno(input))) {
791 input_line(sock->msg, len);
795 #ifdef HAVE_WIN32 /* use special console for input on win32 */
796 if (input == stdin) {
797 if (win32_cgets(sock->msg, len) == NULL) {
803 if (fgets(sock->msg, len, input) == NULL) {
812 strip_trailing_junk(sock->msg);
813 sock->msglen = strlen(sock->msg);
817 #endif /* end non-readline code */
819 static int versioncmd(FILE *input, BSOCK *UA_sock)
821 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
822 HOST_OS, DISTNAME, DISTVER);
826 static int inputcmd(FILE *input, BSOCK *UA_sock)
831 sendit(_("Too many arguments on input command.\n"));
835 sendit(_("First argument to input command must be a filename.\n"));
838 fd = fopen(argk[1], "rb");
840 senditf(_("Cannot open file %s for input. ERR=%s\n"),
841 argk[1], strerror(errno));
844 read_and_process_input(fd, UA_sock);
849 /* Send output to both termina and specified file */
850 static int teecmd(FILE *input, BSOCK *UA_sock)
853 return do_outputcmd(input, UA_sock);
856 /* Send output to specified "file" */
857 static int outputcmd(FILE *input, BSOCK *UA_sock)
860 return do_outputcmd(input, UA_sock);
864 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
867 const char *mode = "a+b";
870 sendit(_("Too many arguments on output/tee command.\n"));
874 if (output != stdout) {
884 fd = fopen(argk[1], mode);
886 senditf(_("Cannot open file %s for output. ERR=%s\n"),
887 argk[1], strerror(errno));
894 static int quitcmd(FILE *input, BSOCK *UA_sock)
899 static int sleepcmd(FILE *input, BSOCK *UA_sock)
902 sleep(atoi(argk[1]));
908 static int timecmd(FILE *input, BSOCK *UA_sock)
911 time_t ttime = time(NULL);
913 (void)localtime_r(&ttime, &tm);
914 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
920 * Send a line to the output file and or the terminal
922 void senditf(const char *fmt,...)
927 va_start(arg_ptr, fmt);
928 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
933 void sendit(const char *buf)
937 if (output == stdout || teeout) {
940 * Here, we convert every \n into \r\n because the
941 * terminal is in raw mode when we are using
944 for (p=q=buf; (p=strchr(q, '\n')); ) {
947 memcpy(obuf, q, len);
949 memcpy(obuf+len, "\r\n", 3);
950 q = ++p; /* point after \n */
958 if (output != stdout) {