2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Console interface to the Director
32 * Kern Sibbald, September MM
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);
64 extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
66 /* Forward referenced functions */
67 static void terminate_console(int sig);
68 static int check_resources();
69 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
70 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
71 void senditf(const char *fmt, ...);
72 void sendit(const char *buf);
74 extern "C" void got_sigstop(int sig);
75 extern "C" void got_sigcontinue(int sig);
76 extern "C" void got_sigtout(int sig);
77 extern "C" void got_sigtin(int sig);
80 /* Static variables */
81 static char *configfile = NULL;
82 static BSOCK *UA_sock = NULL;
83 static DIRRES *dir = NULL;
84 static CONRES *cons = NULL;
85 static FILE *output = stdout;
86 static bool teeout = false; /* output to output and stdout */
87 static bool stop = false;
88 static bool no_conio = false;
89 static int timeout = 0;
94 static char *argk[MAX_CMD_ARGS];
95 static char *argv[MAX_CMD_ARGS];
96 static CONFIG *config;
99 /* Command prototypes */
100 static int versioncmd(FILE *input, BSOCK *UA_sock);
101 static int inputcmd(FILE *input, BSOCK *UA_sock);
102 static int outputcmd(FILE *input, BSOCK *UA_sock);
103 static int teecmd(FILE *input, BSOCK *UA_sock);
104 static int quitcmd(FILE *input, BSOCK *UA_sock);
105 static int helpcmd(FILE *input, BSOCK *UA_sock);
106 static int echocmd(FILE *input, BSOCK *UA_sock);
107 static int timecmd(FILE *input, BSOCK *UA_sock);
108 static int sleepcmd(FILE *input, BSOCK *UA_sock);
109 static int execcmd(FILE *input, BSOCK *UA_sock);
111 static int eolcmd(FILE *input, BSOCK *UA_sock);
115 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
121 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
122 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
123 " -c <file> set configuration file to file\n"
124 " -d <nn> set debug level to <nn>\n"
125 " -dt print timestamp in debug output\n"
128 " -u <nn> set command execution timeout to <nn> seconds\n"
129 " -t test - read configuration and exit\n"
130 " -? print this message.\n"
131 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
136 void got_sigstop(int sig)
142 void got_sigcontinue(int sig)
148 void got_sigtout(int sig)
150 // printf("Got tout\n");
154 void got_sigtin(int sig)
156 // printf("Got tin\n");
160 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
167 * These are the @command
169 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
170 static struct cmdstruct commands[] = {
171 { N_("input"), inputcmd, _("input from file")},
172 { N_("output"), outputcmd, _("output to file")},
173 { N_("quit"), quitcmd, _("quit")},
174 { N_("tee"), teecmd, _("output to file and terminal")},
175 { N_("sleep"), sleepcmd, _("sleep specified time")},
176 { N_("time"), timecmd, _("print current time")},
177 { N_("version"), versioncmd, _("print Console's version")},
178 { N_("echo"), echocmd, _("echo command string")},
179 { N_("exec"), execcmd, _("execute an external command")},
180 { N_("exit"), quitcmd, _("exit = quit")},
181 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
182 { N_("help"), helpcmd, _("help listing")},
184 { N_("separator"), eolcmd, _("set command separator")},
187 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
189 static int do_a_command(FILE *input, BSOCK *UA_sock)
200 Dmsg1(120, "Command: %s\n", UA_sock->msg);
206 if (*cmd == '#') { /* comment */
210 for (i=0; i<comsize; i++) { /* search for command */
211 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
212 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
218 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
219 UA_sock->msglen = strlen(UA_sock->msg);
220 sendit(UA_sock->msg);
226 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
228 const char *prompt = "*";
229 bool at_prompt = false;
230 int tty_input = isatty(fileno(input));
235 if (at_prompt) { /* don't prompt multiple times */
242 stat = get_cmd(input, prompt, UA_sock, 30);
250 /* Reading input from a file */
251 int len = sizeof_pool_memory(UA_sock->msg) - 1;
255 if (fgets(UA_sock->msg, len, input) == NULL) {
258 sendit(UA_sock->msg); /* echo to terminal */
259 strip_trailing_junk(UA_sock->msg);
260 UA_sock->msglen = strlen(UA_sock->msg);
265 break; /* error or interrupt */
266 } else if (stat == 0) { /* timeout */
267 if (strcmp(prompt, "*") == 0) {
268 tid = start_bsock_timer(UA_sock, timeout);
269 bnet_fsend(UA_sock, ".messages");
270 stop_bsock_timer(tid);
276 /* @ => internal command for us */
277 if (UA_sock->msg[0] == '@') {
278 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
279 if (!do_a_command(input, UA_sock)) {
284 tid = start_bsock_timer(UA_sock, timeout);
285 if (!bnet_send(UA_sock)) { /* send command */
286 stop_bsock_timer(tid);
289 stop_bsock_timer(tid);
291 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
294 tid = start_bsock_timer(UA_sock, timeout);
295 while ((stat = bnet_recv(UA_sock)) >= 0) {
302 /* Suppress output if running in background or user hit ctl-c */
303 if (!stop && !usrbrk()) {
304 sendit(UA_sock->msg);
307 stop_bsock_timer(tid);
316 if (is_bnet_stop(UA_sock)) {
317 break; /* error or term */
318 } else if (stat == BNET_SIGNAL) {
319 if (UA_sock->msglen == BNET_PROMPT) {
322 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
328 * Call-back for reading a passphrase for an encrypted PEM file
329 * This function uses getpass(),
330 * which uses a static buffer and is NOT thread-safe.
332 static int tls_pem_callback(char *buf, int size, const void *userdata)
335 const char *prompt = (const char *)userdata;
336 # if defined(HAVE_WIN32)
338 if (win32_cgets(buf, size) == NULL) {
347 passwd = getpass(prompt);
348 bstrncpy(buf, passwd, size);
358 #define READLINE_LIBRARY 1
359 #include "readline.h"
362 static char eol = '\0';
363 static int eolcmd(FILE *input, BSOCK *UA_sock)
365 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
367 } else if (argc == 1) {
370 sendit(_("Illegal separator character.\n"));
376 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
378 static char *line = NULL;
379 static char *next = NULL;
380 static int do_history = 0;
385 rl_catch_signals = 0; /* do it ourselves */
386 /* Here, readline does ***real*** malloc
387 * so, be we have to use the real free
389 line = readline((char *)prompt); /* cast needed for old readlines */
393 strip_trailing_junk(line);
398 sendit(_("Command logic problem\n"));
405 * Split "line" into multiple commands separated by the eol character.
406 * Each part is pointed to by "next" until finally it becomes null.
411 next = strchr(command, eol);
416 if (command != line && isatty(fileno(input))) {
417 senditf("%s%s\n", prompt, command);
420 sock->msglen = pm_strcpy(&sock->msg, command);
429 actuallyfree(line); /* allocated by readline() malloc */
435 #else /* no readline, do it ourselves */
438 static bool bisatty(int fd)
448 * Returns: 1 if data available
453 wait_for_data(int fd, int sec)
455 #if defined(HAVE_WIN32)
465 FD_SET((unsigned)fd, &fdset);
466 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
467 case 0: /* timeout */
470 if (errno == EINTR || errno == EAGAIN) {
473 return -1; /* error return */
482 * Get next input command from terminal.
484 * Returns: 1 if got input
489 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
493 if (output == stdout || teeout) {
498 switch (wait_for_data(fileno(input), sec)) {
500 return 0; /* timeout */
502 return -1; /* error */
504 len = sizeof_pool_memory(sock->msg) - 1;
510 if (bisatty(fileno(input))) {
511 input_line(sock->msg, len);
515 #ifdef HAVE_WIN32 /* use special console for input on win32 */
516 if (input == stdin) {
517 if (win32_cgets(sock->msg, len) == NULL) {
523 if (fgets(sock->msg, len, input) == NULL) {
532 strip_trailing_junk(sock->msg);
533 sock->msglen = strlen(sock->msg);
537 #endif /* ! HAVE_READLINE */
540 static int console_update_history(const char *histfile)
545 /* first, try to truncate the history file, and if it
546 * fail, the file is probably not present, and we
547 * can use write_history to create it
550 if (history_truncate_file(histfile, 100) == 0) {
551 ret = append_history(history_length, histfile);
553 ret = write_history(histfile);
561 static int console_init_history(const char *histfile)
568 ret = read_history(histfile);
575 /*********************************************************************
577 * Main Bacula Console -- User Interface Program
580 int main(int argc, char *argv[])
583 bool no_signals = false;
584 bool test_config = false;
588 setlocale(LC_ALL, "");
589 bindtextdomain("bacula", LOCALEDIR);
590 textdomain("bacula");
594 my_name_is(argc, argv, "bconsole");
595 init_msg(NULL, NULL);
596 working_directory = "/tmp";
597 args = get_pool_memory(PM_FNAME);
599 while ((ch = getopt(argc, argv, "bc:d:nstu:?")) != -1) {
601 case 'c': /* configuration file */
602 if (configfile != NULL) {
605 configfile = bstrdup(optarg);
609 if (*optarg == 't') {
610 dbg_timestamp = true;
612 debug_level = atoi(optarg);
613 if (debug_level <= 0) {
619 case 'n': /* no conio */
623 case 's': /* turn off signals */
632 timeout = atoi(optarg);
645 init_signals(terminate_console);
649 #if !defined(HAVE_WIN32)
650 /* Override Bacula default signals */
651 signal(SIGQUIT, SIG_IGN);
652 signal(SIGTSTP, got_sigstop);
653 signal(SIGCONT, got_sigcontinue);
654 signal(SIGTTIN, got_sigtin);
655 signal(SIGTTOU, got_sigtout);
666 if (configfile == NULL) {
667 configfile = bstrdup(CONFIG_FILE);
670 config = new_config_parser();
671 parse_cons_config(config, configfile, M_ERROR_TERM);
673 if (init_crypto() != 0) {
674 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
677 if (!check_resources()) {
678 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
686 terminate_console(0);
690 memset(&jcr, 0, sizeof(jcr));
692 (void)WSA_Init(); /* Initialize Windows sockets */
694 start_watchdog(); /* Start socket watchdog */
698 foreach_res(dir, R_DIRECTOR) {
702 foreach_res(cons, R_CONSOLE) {
708 struct sockaddr client_addr;
709 memset(&client_addr, 0, sizeof(client_addr));
710 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
712 sendit(_("Available Directors:\n"));
715 foreach_res(dir, R_DIRECTOR) {
716 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
720 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
721 (void)WSACleanup(); /* Cleanup Windows sockets */
724 if (!is_a_number(UA_sock->msg)) {
725 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
726 UA_sock->msg, numdir);
729 item = atoi(UA_sock->msg);
730 if (item < 0 || item > numdir) {
731 senditf(_("You must enter a number between 1 and %d\n"), numdir);
736 for (i=0; i<item; i++) {
737 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
739 /* Look for a console linked to this director */
740 for (i=0; i<numcon; i++) {
741 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
742 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
747 /* Look for the first non-linked console */
749 for (i=0; i<numcon; i++) {
750 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
751 if (cons->director == NULL)
758 /* If no director, take first one */
761 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
764 /* If no console, take first one */
767 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
771 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
774 /* Initialize Console TLS context */
775 if (cons && (cons->tls_enable || cons->tls_require)) {
776 /* Generate passphrase prompt */
777 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
779 /* Initialize TLS context:
780 * Args: CA certfile, CA certdir, Certfile, Keyfile,
781 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
783 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
784 cons->tls_ca_certdir, cons->tls_certfile,
785 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
787 if (!cons->tls_ctx) {
788 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
790 terminate_console(0);
795 /* Initialize Director TLS context */
796 if (dir->tls_enable || dir->tls_require) {
797 /* Generate passphrase prompt */
798 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
800 /* Initialize TLS context:
801 * Args: CA certfile, CA certdir, Certfile, Keyfile,
802 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
803 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
804 dir->tls_ca_certdir, dir->tls_certfile,
805 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
808 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
810 terminate_console(0);
815 if (dir->heartbeat_interval) {
816 heart_beat = dir->heartbeat_interval;
818 heart_beat = cons->heartbeat_interval;
822 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
823 NULL, dir->DIRport, 0);
824 if (UA_sock == NULL) {
825 terminate_console(0);
828 jcr.dir_bsock = UA_sock;
830 /* If cons==NULL, default console will be used */
831 if (!authenticate_director(&jcr, dir, cons)) {
832 terminate_console(0);
836 Dmsg0(40, "Opened connection with Director daemon\n");
838 sendit(_("Enter a period to cancel a command.\n"));
840 /* Read/Update history file if HOME exists */
841 POOL_MEM history_file;
843 /* Run commands in ~/.bconsolerc if any */
844 char *env = getenv("HOME");
847 pm_strcpy(&UA_sock->msg, env);
848 pm_strcat(&UA_sock->msg, "/.bconsolerc");
849 fd = fopen(UA_sock->msg, "rb");
851 read_and_process_input(fd, UA_sock);
855 pm_strcpy(history_file, env);
856 pm_strcat(history_file, "/.bconsole_history");
857 console_init_history(history_file.c_str());
860 read_and_process_input(stdin, UA_sock);
863 UA_sock->signal(BNET_TERMINATE); /* send EOF */
868 console_update_history(history_file.c_str());
871 terminate_console(0);
875 /* Cleanup and then exit */
876 static void terminate_console(int sig)
879 static bool already_here = false;
881 if (already_here) { /* avoid recursive temination problems */
886 config->free_resources();
890 free_pool_memory(args);
894 (void)WSACleanup(); /* Cleanup Windows sockets */
904 * Make a quick check to see that we have all the
907 static int check_resources()
916 foreach_res(director, R_DIRECTOR) {
919 /* tls_require implies tls_enable */
920 if (director->tls_require) {
922 director->tls_enable = true;
924 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
929 tls_needed = director->tls_enable || director->tls_authenticate;
931 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
932 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
933 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
934 " At least one CA certificate store is required.\n"),
935 director->hdr.name, configfile);
941 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
942 "Without that I don't how to speak to the Director :-(\n"), configfile);
947 /* Loop over Consoles */
948 foreach_res(cons, R_CONSOLE) {
949 /* tls_require implies tls_enable */
950 if (cons->tls_require) {
952 cons->tls_enable = true;
954 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
959 tls_needed = cons->tls_enable || cons->tls_authenticate;
960 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
961 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
962 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
963 cons->hdr.name, configfile);
973 static int versioncmd(FILE *input, BSOCK *UA_sock)
975 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
976 HOST_OS, DISTNAME, DISTVER);
980 static int inputcmd(FILE *input, BSOCK *UA_sock)
985 sendit(_("Too many arguments on input command.\n"));
989 sendit(_("First argument to input command must be a filename.\n"));
992 fd = fopen(argk[1], "rb");
995 senditf(_("Cannot open file %s for input. ERR=%s\n"),
996 argk[1], be.bstrerror());
999 read_and_process_input(fd, UA_sock);
1004 /* Send output to both termina and specified file */
1005 static int teecmd(FILE *input, BSOCK *UA_sock)
1008 return do_outputcmd(input, UA_sock);
1011 /* Send output to specified "file" */
1012 static int outputcmd(FILE *input, BSOCK *UA_sock)
1015 return do_outputcmd(input, UA_sock);
1019 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1022 const char *mode = "a+b";
1025 sendit(_("Too many arguments on output/tee command.\n"));
1029 if (output != stdout) {
1039 fd = fopen(argk[1], mode);
1042 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1043 argk[1], be.bstrerror(errno));
1051 * exec "some-command" [wait-seconds]
1053 static int execcmd(FILE *input, BSOCK *UA_sock)
1061 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1065 wait = atoi(argk[2]);
1067 bpipe = open_bpipe(argk[1], wait, "r");
1070 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1071 argk[1], be.bstrerror(errno));
1075 while (fgets(line, sizeof(line), bpipe->rfd)) {
1076 senditf("%s", line);
1078 stat = close_bpipe(bpipe);
1082 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1088 static int echocmd(FILE *input, BSOCK *UA_sock)
1090 for (int i=1; i < argc; i++) {
1091 senditf("%s ", argk[i]);
1097 static int quitcmd(FILE *input, BSOCK *UA_sock)
1102 static int helpcmd(FILE *input, BSOCK *UA_sock)
1105 for (i=0; i<comsize; i++) {
1106 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1112 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1115 sleep(atoi(argk[1]));
1121 static int timecmd(FILE *input, BSOCK *UA_sock)
1124 time_t ttime = time(NULL);
1126 (void)localtime_r(&ttime, &tm);
1127 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1133 * Send a line to the output file and or the terminal
1135 void senditf(const char *fmt,...)
1140 va_start(arg_ptr, fmt);
1141 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1146 void sendit(const char *buf)
1150 if (output == stdout || teeout) {
1153 * Here, we convert every \n into \r\n because the
1154 * terminal is in raw mode when we are using
1157 for (p=q=buf; (p=strchr(q, '\n')); ) {
1160 memcpy(obuf, q, len);
1162 memcpy(obuf+len, "\r\n", 3);
1163 q = ++p; /* point after \n */
1164 fputs(obuf, output);
1171 if (output != stdout) {