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 if (timeout) tid = start_bsock_timer(UA_sock, timeout);
269 bnet_fsend(UA_sock, ".messages");
270 if (timeout) 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 if (timeout) tid = start_bsock_timer(UA_sock, timeout);
285 if (!bnet_send(UA_sock)) { /* send command */
286 if (timeout) stop_bsock_timer(tid);
289 if (timeout) stop_bsock_timer(tid);
291 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
294 if (timeout) 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 if (timeout) 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");
593 my_name_is(argc, argv, "bconsole");
594 init_msg(NULL, NULL);
595 working_directory = "/tmp";
596 args = get_pool_memory(PM_FNAME);
598 while ((ch = getopt(argc, argv, "bc:d:nstu:?")) != -1) {
600 case 'c': /* configuration file */
601 if (configfile != NULL) {
604 configfile = bstrdup(optarg);
608 if (*optarg == 't') {
609 dbg_timestamp = true;
611 debug_level = atoi(optarg);
612 if (debug_level <= 0) {
618 case 'n': /* no conio */
622 case 's': /* turn off signals */
631 timeout = atoi(optarg);
644 init_signals(terminate_console);
648 #if !defined(HAVE_WIN32)
649 /* Override Bacula default signals */
650 signal(SIGQUIT, SIG_IGN);
651 signal(SIGTSTP, got_sigstop);
652 signal(SIGCONT, got_sigcontinue);
653 signal(SIGTTIN, got_sigtin);
654 signal(SIGTTOU, got_sigtout);
665 if (configfile == NULL) {
666 configfile = bstrdup(CONFIG_FILE);
669 config = new_config_parser();
670 parse_cons_config(config, configfile, M_ERROR_TERM);
672 if (init_crypto() != 0) {
673 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
676 if (!check_resources()) {
677 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
685 terminate_console(0);
689 memset(&jcr, 0, sizeof(jcr));
691 (void)WSA_Init(); /* Initialize Windows sockets */
693 start_watchdog(); /* Start socket watchdog */
697 foreach_res(dir, R_DIRECTOR) {
701 foreach_res(cons, R_CONSOLE) {
707 struct sockaddr client_addr;
708 memset(&client_addr, 0, sizeof(client_addr));
709 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
711 sendit(_("Available Directors:\n"));
714 foreach_res(dir, R_DIRECTOR) {
715 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
719 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
720 (void)WSACleanup(); /* Cleanup Windows sockets */
723 if (!is_a_number(UA_sock->msg)) {
724 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
725 UA_sock->msg, numdir);
728 item = atoi(UA_sock->msg);
729 if (item < 0 || item > numdir) {
730 senditf(_("You must enter a number between 1 and %d\n"), numdir);
735 for (i=0; i<item; i++) {
736 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
738 /* Look for a console linked to this director */
739 for (i=0; i<numcon; i++) {
740 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
741 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
746 /* Look for the first non-linked console */
748 for (i=0; i<numcon; i++) {
749 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
750 if (cons->director == NULL)
757 /* If no director, take first one */
760 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
763 /* If no console, take first one */
766 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
770 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
773 /* Initialize Console TLS context */
774 if (cons && (cons->tls_enable || cons->tls_require)) {
775 /* Generate passphrase prompt */
776 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
778 /* Initialize TLS context:
779 * Args: CA certfile, CA certdir, Certfile, Keyfile,
780 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
782 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
783 cons->tls_ca_certdir, cons->tls_certfile,
784 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
786 if (!cons->tls_ctx) {
787 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
789 terminate_console(0);
794 /* Initialize Director TLS context */
795 if (dir->tls_enable || dir->tls_require) {
796 /* Generate passphrase prompt */
797 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
799 /* Initialize TLS context:
800 * Args: CA certfile, CA certdir, Certfile, Keyfile,
801 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
802 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
803 dir->tls_ca_certdir, dir->tls_certfile,
804 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
807 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
809 terminate_console(0);
814 if (dir->heartbeat_interval) {
815 heart_beat = dir->heartbeat_interval;
817 heart_beat = cons->heartbeat_interval;
821 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
822 NULL, dir->DIRport, 0);
823 if (UA_sock == NULL) {
824 terminate_console(0);
827 jcr.dir_bsock = UA_sock;
829 /* If cons==NULL, default console will be used */
830 if (!authenticate_director(&jcr, dir, cons)) {
831 terminate_console(0);
835 Dmsg0(40, "Opened connection with Director daemon\n");
837 sendit(_("Enter a period to cancel a command.\n"));
839 /* Read/Update history file if HOME exists */
840 POOL_MEM history_file;
842 /* Run commands in ~/.bconsolerc if any */
843 char *env = getenv("HOME");
846 pm_strcpy(&UA_sock->msg, env);
847 pm_strcat(&UA_sock->msg, "/.bconsolerc");
848 fd = fopen(UA_sock->msg, "rb");
850 read_and_process_input(fd, UA_sock);
854 pm_strcpy(history_file, env);
855 pm_strcat(history_file, "/.bconsole_history");
856 console_init_history(history_file.c_str());
859 read_and_process_input(stdin, UA_sock);
862 UA_sock->signal(BNET_TERMINATE); /* send EOF */
867 console_update_history(history_file.c_str());
870 terminate_console(0);
874 /* Cleanup and then exit */
875 static void terminate_console(int sig)
878 static bool already_here = false;
880 if (already_here) { /* avoid recursive temination problems */
885 config->free_resources();
889 free_pool_memory(args);
893 (void)WSACleanup(); /* Cleanup Windows sockets */
903 * Make a quick check to see that we have all the
906 static int check_resources()
915 foreach_res(director, R_DIRECTOR) {
918 /* tls_require implies tls_enable */
919 if (director->tls_require) {
921 director->tls_enable = true;
923 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
928 tls_needed = director->tls_enable || director->tls_authenticate;
930 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
931 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
932 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
933 " At least one CA certificate store is required.\n"),
934 director->hdr.name, configfile);
940 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
941 "Without that I don't how to speak to the Director :-(\n"), configfile);
946 /* Loop over Consoles */
947 foreach_res(cons, R_CONSOLE) {
948 /* tls_require implies tls_enable */
949 if (cons->tls_require) {
951 cons->tls_enable = true;
953 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
958 tls_needed = cons->tls_enable || cons->tls_authenticate;
959 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
960 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
961 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
962 cons->hdr.name, configfile);
972 static int versioncmd(FILE *input, BSOCK *UA_sock)
974 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
975 HOST_OS, DISTNAME, DISTVER);
979 static int inputcmd(FILE *input, BSOCK *UA_sock)
984 sendit(_("Too many arguments on input command.\n"));
988 sendit(_("First argument to input command must be a filename.\n"));
991 fd = fopen(argk[1], "rb");
994 senditf(_("Cannot open file %s for input. ERR=%s\n"),
995 argk[1], be.bstrerror());
998 read_and_process_input(fd, UA_sock);
1003 /* Send output to both termina and specified file */
1004 static int teecmd(FILE *input, BSOCK *UA_sock)
1007 return do_outputcmd(input, UA_sock);
1010 /* Send output to specified "file" */
1011 static int outputcmd(FILE *input, BSOCK *UA_sock)
1014 return do_outputcmd(input, UA_sock);
1018 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1021 const char *mode = "a+b";
1024 sendit(_("Too many arguments on output/tee command.\n"));
1028 if (output != stdout) {
1038 fd = fopen(argk[1], mode);
1041 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1042 argk[1], be.bstrerror(errno));
1050 * exec "some-command" [wait-seconds]
1052 static int execcmd(FILE *input, BSOCK *UA_sock)
1060 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1064 wait = atoi(argk[2]);
1066 bpipe = open_bpipe(argk[1], wait, "r");
1069 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1070 argk[1], be.bstrerror(errno));
1074 while (fgets(line, sizeof(line), bpipe->rfd)) {
1075 senditf("%s", line);
1077 stat = close_bpipe(bpipe);
1081 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1087 static int echocmd(FILE *input, BSOCK *UA_sock)
1089 for (int i=1; i < argc; i++) {
1090 senditf("%s ", argk[i]);
1096 static int quitcmd(FILE *input, BSOCK *UA_sock)
1101 static int helpcmd(FILE *input, BSOCK *UA_sock)
1104 for (i=0; i<comsize; i++) {
1105 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1111 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1114 sleep(atoi(argk[1]));
1120 static int timecmd(FILE *input, BSOCK *UA_sock)
1123 time_t ttime = time(NULL);
1125 (void)localtime_r(&ttime, &tm);
1126 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1132 * Send a line to the output file and or the terminal
1134 void senditf(const char *fmt,...)
1139 va_start(arg_ptr, fmt);
1140 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1145 void sendit(const char *buf)
1149 if (output == stdout || teeout) {
1152 * Here, we convert every \n into \r\n because the
1153 * terminal is in raw mode when we are using
1156 for (p=q=buf; (p=strchr(q, '\n')); ) {
1159 memcpy(obuf, q, len);
1161 memcpy(obuf+len, "\r\n", 3);
1162 q = ++p; /* point after \n */
1163 fputs(obuf, output);
1170 if (output != stdout) {