2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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;
93 static char *argk[MAX_CMD_ARGS];
94 static char *argv[MAX_CMD_ARGS];
95 static CONFIG *config;
98 /* Command prototypes */
99 static int versioncmd(FILE *input, BSOCK *UA_sock);
100 static int inputcmd(FILE *input, BSOCK *UA_sock);
101 static int outputcmd(FILE *input, BSOCK *UA_sock);
102 static int teecmd(FILE *input, BSOCK *UA_sock);
103 static int quitcmd(FILE *input, BSOCK *UA_sock);
104 static int echocmd(FILE *input, BSOCK *UA_sock);
105 static int timecmd(FILE *input, BSOCK *UA_sock);
106 static int sleepcmd(FILE *input, BSOCK *UA_sock);
107 static int execcmd(FILE *input, BSOCK *UA_sock);
109 static int eolcmd(FILE *input, BSOCK *UA_sock);
113 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
119 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
120 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
121 " -c <file> set configuration file to file\n"
122 " -d <nn> set debug level to <nn>\n"
123 " -dt print timestamp in debug output\n"
126 " -t test - read configuration and exit\n"
127 " -? print this message.\n"
128 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
133 void got_sigstop(int sig)
139 void got_sigcontinue(int sig)
145 void got_sigtout(int sig)
147 // printf("Got tout\n");
151 void got_sigtin(int sig)
153 // printf("Got tin\n");
157 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
164 * These are the @command
166 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
167 static struct cmdstruct commands[] = {
168 { N_("input"), inputcmd, _("input from file")},
169 { N_("output"), outputcmd, _("output to file")},
170 { N_("quit"), quitcmd, _("quit")},
171 { N_("tee"), teecmd, _("output to file and terminal")},
172 { N_("sleep"), sleepcmd, _("sleep specified time")},
173 { N_("time"), timecmd, _("print current time")},
174 { N_("version"), versioncmd, _("print Console's version")},
175 { N_("echo"), echocmd, _("echo command string")},
176 { N_("exec"), execcmd, _("execute an external command")},
177 { N_("exit"), quitcmd, _("exit = quit")},
178 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
180 { N_("separator"), eolcmd, _("set command separator")},
183 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
185 static int do_a_command(FILE *input, BSOCK *UA_sock)
196 Dmsg1(120, "Command: %s\n", UA_sock->msg);
202 if (*cmd == '#') { /* comment */
206 for (i=0; i<comsize; i++) { /* search for command */
207 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
208 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
214 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
215 UA_sock->msglen = strlen(UA_sock->msg);
216 sendit(UA_sock->msg);
222 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
224 const char *prompt = "*";
225 bool at_prompt = false;
226 int tty_input = isatty(fileno(input));
230 if (at_prompt) { /* don't prompt multiple times */
237 stat = get_cmd(input, prompt, UA_sock, 30);
245 /* Reading input from a file */
246 int len = sizeof_pool_memory(UA_sock->msg) - 1;
250 if (fgets(UA_sock->msg, len, input) == NULL) {
253 sendit(UA_sock->msg); /* echo to terminal */
254 strip_trailing_junk(UA_sock->msg);
255 UA_sock->msglen = strlen(UA_sock->msg);
260 break; /* error or interrupt */
261 } else if (stat == 0) { /* timeout */
262 if (strcmp(prompt, "*") == 0) {
263 bnet_fsend(UA_sock, ".messages");
269 /* @ => internal command for us */
270 if (UA_sock->msg[0] == '@') {
271 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
272 if (!do_a_command(input, UA_sock)) {
277 if (!bnet_send(UA_sock)) { /* send command */
281 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
284 while ((stat = bnet_recv(UA_sock)) >= 0) {
291 /* Suppress output if running in background or user hit ctl-c */
292 if (!stop && !usrbrk()) {
293 sendit(UA_sock->msg);
304 if (is_bnet_stop(UA_sock)) {
305 break; /* error or term */
306 } else if (stat == BNET_SIGNAL) {
307 if (UA_sock->msglen == BNET_PROMPT) {
310 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
316 * Call-back for reading a passphrase for an encrypted PEM file
317 * This function uses getpass(),
318 * which uses a static buffer and is NOT thread-safe.
320 static int tls_pem_callback(char *buf, int size, const void *userdata)
323 const char *prompt = (const char *)userdata;
324 # if defined(HAVE_WIN32)
326 if (win32_cgets(buf, size) == NULL) {
335 passwd = getpass(prompt);
336 bstrncpy(buf, passwd, size);
346 #define READLINE_LIBRARY 1
348 #include "readline.h"
351 static char eol = '\0';
352 static int eolcmd(FILE *input, BSOCK *UA_sock)
354 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
356 } else if (argc == 1) {
359 sendit(_("Illegal separator character.\n"));
365 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
367 static char *line = NULL;
368 static char *next = NULL;
369 static int do_history = 0;
374 rl_catch_signals = 0; /* do it ourselves */
375 line = readline((char *)prompt); /* cast needed for old readlines */
379 strip_trailing_junk(line);
384 sendit(_("Command logic problem\n"));
391 * Split "line" into multiple commands separated by the eol character.
392 * Each part is pointed to by "next" until finally it becomes null.
397 next = strchr(command, eol);
402 if (command != line && isatty(fileno(input))) {
403 senditf("%s%s\n", prompt, command);
406 sock->msglen = pm_strcpy(&sock->msg, command);
421 #else /* no readline, do it ourselves */
424 static bool bisatty(int fd)
434 * Returns: 1 if data available
439 wait_for_data(int fd, int sec)
441 #if defined(HAVE_WIN32)
451 FD_SET((unsigned)fd, &fdset);
452 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
453 case 0: /* timeout */
456 if (errno == EINTR || errno == EAGAIN) {
459 return -1; /* error return */
468 * Get next input command from terminal.
470 * Returns: 1 if got input
475 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
479 if (output == stdout || teeout) {
484 switch (wait_for_data(fileno(input), sec)) {
486 return 0; /* timeout */
488 return -1; /* error */
490 len = sizeof_pool_memory(sock->msg) - 1;
496 if (bisatty(fileno(input))) {
497 input_line(sock->msg, len);
501 #ifdef HAVE_WIN32 /* use special console for input on win32 */
502 if (input == stdin) {
503 if (win32_cgets(sock->msg, len) == NULL) {
509 if (fgets(sock->msg, len, input) == NULL) {
518 strip_trailing_junk(sock->msg);
519 sock->msglen = strlen(sock->msg);
523 #endif /* ! HAVE_READLINE */
526 static int console_update_history(const char *histfile)
531 /* first, try to truncate the history file, and if it
532 * fail, the file is probably not present, and we
533 * can use write_history to create it
536 if (history_truncate_file(histfile, 100) == 0) {
537 ret = append_history(history_length, histfile);
539 ret = write_history(histfile);
547 static int console_init_history(const char *histfile)
554 ret = read_history(histfile);
561 /*********************************************************************
563 * Main Bacula Console -- User Interface Program
566 int main(int argc, char *argv[])
569 bool no_signals = false;
570 bool test_config = false;
574 setlocale(LC_ALL, "");
575 bindtextdomain("bacula", LOCALEDIR);
576 textdomain("bacula");
579 my_name_is(argc, argv, "bconsole");
580 init_msg(NULL, NULL);
581 working_directory = "/tmp";
582 args = get_pool_memory(PM_FNAME);
584 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
586 case 'c': /* configuration file */
587 if (configfile != NULL) {
590 configfile = bstrdup(optarg);
594 if (*optarg == 't') {
595 dbg_timestamp = true;
597 debug_level = atoi(optarg);
598 if (debug_level <= 0) {
604 case 'n': /* no conio */
608 case 's': /* turn off signals */
626 init_signals(terminate_console);
630 #if !defined(HAVE_WIN32)
631 /* Override Bacula default signals */
632 signal(SIGQUIT, SIG_IGN);
633 signal(SIGTSTP, got_sigstop);
634 signal(SIGCONT, got_sigcontinue);
635 signal(SIGTTIN, got_sigtin);
636 signal(SIGTTOU, got_sigtout);
647 if (configfile == NULL) {
648 configfile = bstrdup(CONFIG_FILE);
651 config = new_config_parser();
652 parse_cons_config(config, configfile, M_ERROR_TERM);
654 if (init_crypto() != 0) {
655 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
658 if (!check_resources()) {
659 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
667 terminate_console(0);
671 memset(&jcr, 0, sizeof(jcr));
673 (void)WSA_Init(); /* Initialize Windows sockets */
677 foreach_res(dir, R_DIRECTOR) {
681 foreach_res(cons, R_CONSOLE) {
687 struct sockaddr client_addr;
688 memset(&client_addr, 0, sizeof(client_addr));
689 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
691 sendit(_("Available Directors:\n"));
694 foreach_res(dir, R_DIRECTOR) {
695 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
699 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
700 (void)WSACleanup(); /* Cleanup Windows sockets */
703 if (!is_a_number(UA_sock->msg)) {
704 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
705 UA_sock->msg, numdir);
708 item = atoi(UA_sock->msg);
709 if (item < 0 || item > numdir) {
710 senditf(_("You must enter a number between 1 and %d\n"), numdir);
715 for (i=0; i<item; i++) {
716 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
718 /* Look for a console linked to this director */
719 for (i=0; i<numcon; i++) {
720 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
721 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
726 /* Look for the first non-linked console */
728 for (i=0; i<numcon; i++) {
729 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
730 if (cons->director == NULL)
737 /* If no director, take first one */
740 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
743 /* If no console, take first one */
746 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
750 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
753 /* Initialize Console TLS context */
754 if (cons && (cons->tls_enable || cons->tls_require)) {
755 /* Generate passphrase prompt */
756 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
758 /* Initialize TLS context:
759 * Args: CA certfile, CA certdir, Certfile, Keyfile,
760 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
762 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
763 cons->tls_ca_certdir, cons->tls_certfile,
764 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
766 if (!cons->tls_ctx) {
767 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
769 terminate_console(0);
774 /* Initialize Director TLS context */
775 if (dir->tls_enable || dir->tls_require) {
776 /* Generate passphrase prompt */
777 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
779 /* Initialize TLS context:
780 * Args: CA certfile, CA certdir, Certfile, Keyfile,
781 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
782 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
783 dir->tls_ca_certdir, dir->tls_certfile,
784 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
787 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
789 terminate_console(0);
794 if (dir->heartbeat_interval) {
795 heart_beat = dir->heartbeat_interval;
797 heart_beat = cons->heartbeat_interval;
801 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
802 NULL, dir->DIRport, 0);
803 if (UA_sock == NULL) {
804 terminate_console(0);
807 jcr.dir_bsock = UA_sock;
809 /* If cons==NULL, default console will be used */
810 if (!authenticate_director(&jcr, dir, cons)) {
811 terminate_console(0);
815 Dmsg0(40, "Opened connection with Director daemon\n");
817 sendit(_("Enter a period to cancel a command.\n"));
819 /* Read/Update history file if HOME exists */
820 POOL_MEM history_file;
822 /* Run commands in ~/.bconsolerc if any */
823 char *env = getenv("HOME");
826 pm_strcpy(&UA_sock->msg, env);
827 pm_strcat(&UA_sock->msg, "/.bconsolerc");
828 fd = fopen(UA_sock->msg, "rb");
830 read_and_process_input(fd, UA_sock);
834 pm_strcpy(history_file, env);
835 pm_strcat(history_file, "/.bconsole_history");
836 console_init_history(history_file.c_str());
839 read_and_process_input(stdin, UA_sock);
842 UA_sock->signal(BNET_TERMINATE); /* send EOF */
847 console_update_history(history_file.c_str());
850 terminate_console(0);
854 /* Cleanup and then exit */
855 static void terminate_console(int sig)
858 static bool already_here = false;
860 if (already_here) { /* avoid recursive temination problems */
864 config->free_resources();
868 free_pool_memory(args);
872 (void)WSACleanup(); /* Cleanup Windows sockets */
882 * Make a quick check to see that we have all the
885 static int check_resources()
894 foreach_res(director, R_DIRECTOR) {
897 /* tls_require implies tls_enable */
898 if (director->tls_require) {
900 director->tls_enable = true;
902 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
907 tls_needed = director->tls_enable || director->tls_authenticate;
909 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
910 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
911 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
912 " At least one CA certificate store is required.\n"),
913 director->hdr.name, configfile);
919 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
920 "Without that I don't how to speak to the Director :-(\n"), configfile);
925 /* Loop over Consoles */
926 foreach_res(cons, R_CONSOLE) {
927 /* tls_require implies tls_enable */
928 if (cons->tls_require) {
930 cons->tls_enable = true;
932 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
937 tls_needed = cons->tls_enable || cons->tls_authenticate;
938 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
939 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
940 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
941 cons->hdr.name, configfile);
951 static int versioncmd(FILE *input, BSOCK *UA_sock)
953 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
954 HOST_OS, DISTNAME, DISTVER);
958 static int inputcmd(FILE *input, BSOCK *UA_sock)
963 sendit(_("Too many arguments on input command.\n"));
967 sendit(_("First argument to input command must be a filename.\n"));
970 fd = fopen(argk[1], "rb");
973 senditf(_("Cannot open file %s for input. ERR=%s\n"),
974 argk[1], be.bstrerror());
977 read_and_process_input(fd, UA_sock);
982 /* Send output to both termina and specified file */
983 static int teecmd(FILE *input, BSOCK *UA_sock)
986 return do_outputcmd(input, UA_sock);
989 /* Send output to specified "file" */
990 static int outputcmd(FILE *input, BSOCK *UA_sock)
993 return do_outputcmd(input, UA_sock);
997 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1000 const char *mode = "a+b";
1003 sendit(_("Too many arguments on output/tee command.\n"));
1007 if (output != stdout) {
1017 fd = fopen(argk[1], mode);
1020 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1021 argk[1], be.bstrerror(errno));
1029 * exec "some-command" [wait-seconds]
1031 static int execcmd(FILE *input, BSOCK *UA_sock)
1039 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1043 wait = atoi(argk[2]);
1045 bpipe = open_bpipe(argk[1], wait, "r");
1048 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1049 argk[1], be.bstrerror(errno));
1053 while (fgets(line, sizeof(line), bpipe->rfd)) {
1054 senditf("%s", line);
1056 stat = close_bpipe(bpipe);
1060 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1066 static int echocmd(FILE *input, BSOCK *UA_sock)
1068 for (int i=1; i < argc; i++) {
1069 senditf("%s", argk[i]);
1076 static int quitcmd(FILE *input, BSOCK *UA_sock)
1081 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1084 sleep(atoi(argk[1]));
1090 static int timecmd(FILE *input, BSOCK *UA_sock)
1093 time_t ttime = time(NULL);
1095 (void)localtime_r(&ttime, &tm);
1096 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1102 * Send a line to the output file and or the terminal
1104 void senditf(const char *fmt,...)
1109 va_start(arg_ptr, fmt);
1110 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1115 void sendit(const char *buf)
1119 if (output == stdout || teeout) {
1122 * Here, we convert every \n into \r\n because the
1123 * terminal is in raw mode when we are using
1126 for (p=q=buf; (p=strchr(q, '\n')); ) {
1129 memcpy(obuf, q, len);
1131 memcpy(obuf+len, "\r\n", 3);
1132 q = ++p; /* point after \n */
1133 fputs(obuf, output);
1140 if (output != stdout) {