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;
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 helpcmd(FILE *input, BSOCK *UA_sock);
105 static int echocmd(FILE *input, BSOCK *UA_sock);
106 static int timecmd(FILE *input, BSOCK *UA_sock);
107 static int sleepcmd(FILE *input, BSOCK *UA_sock);
108 static int execcmd(FILE *input, BSOCK *UA_sock);
110 static int eolcmd(FILE *input, BSOCK *UA_sock);
114 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
120 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
121 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
122 " -c <file> set configuration file to file\n"
123 " -d <nn> set debug level to <nn>\n"
124 " -dt print timestamp in debug output\n"
127 " -t test - read configuration and exit\n"
128 " -? print this message.\n"
129 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
134 void got_sigstop(int sig)
140 void got_sigcontinue(int sig)
146 void got_sigtout(int sig)
148 // printf("Got tout\n");
152 void got_sigtin(int sig)
154 // printf("Got tin\n");
158 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
165 * These are the @command
167 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
168 static struct cmdstruct commands[] = {
169 { N_("input"), inputcmd, _("input from file")},
170 { N_("output"), outputcmd, _("output to file")},
171 { N_("quit"), quitcmd, _("quit")},
172 { N_("tee"), teecmd, _("output to file and terminal")},
173 { N_("sleep"), sleepcmd, _("sleep specified time")},
174 { N_("time"), timecmd, _("print current time")},
175 { N_("version"), versioncmd, _("print Console's version")},
176 { N_("echo"), echocmd, _("echo command string")},
177 { N_("exec"), execcmd, _("execute an external command")},
178 { N_("exit"), quitcmd, _("exit = quit")},
179 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
180 { N_("help"), helpcmd, _("help listing")},
182 { N_("separator"), eolcmd, _("set command separator")},
185 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
187 static int do_a_command(FILE *input, BSOCK *UA_sock)
198 Dmsg1(120, "Command: %s\n", UA_sock->msg);
204 if (*cmd == '#') { /* comment */
208 for (i=0; i<comsize; i++) { /* search for command */
209 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
210 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
216 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
217 UA_sock->msglen = strlen(UA_sock->msg);
218 sendit(UA_sock->msg);
224 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
226 const char *prompt = "*";
227 bool at_prompt = false;
228 int tty_input = isatty(fileno(input));
232 if (at_prompt) { /* don't prompt multiple times */
239 stat = get_cmd(input, prompt, UA_sock, 30);
247 /* Reading input from a file */
248 int len = sizeof_pool_memory(UA_sock->msg) - 1;
252 if (fgets(UA_sock->msg, len, input) == NULL) {
255 sendit(UA_sock->msg); /* echo to terminal */
256 strip_trailing_junk(UA_sock->msg);
257 UA_sock->msglen = strlen(UA_sock->msg);
262 break; /* error or interrupt */
263 } else if (stat == 0) { /* timeout */
264 if (strcmp(prompt, "*") == 0) {
265 bnet_fsend(UA_sock, ".messages");
271 /* @ => internal command for us */
272 if (UA_sock->msg[0] == '@') {
273 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
274 if (!do_a_command(input, UA_sock)) {
279 if (!bnet_send(UA_sock)) { /* send command */
283 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
286 while ((stat = bnet_recv(UA_sock)) >= 0) {
293 /* Suppress output if running in background or user hit ctl-c */
294 if (!stop && !usrbrk()) {
295 sendit(UA_sock->msg);
306 if (is_bnet_stop(UA_sock)) {
307 break; /* error or term */
308 } else if (stat == BNET_SIGNAL) {
309 if (UA_sock->msglen == BNET_PROMPT) {
312 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
318 * Call-back for reading a passphrase for an encrypted PEM file
319 * This function uses getpass(),
320 * which uses a static buffer and is NOT thread-safe.
322 static int tls_pem_callback(char *buf, int size, const void *userdata)
325 const char *prompt = (const char *)userdata;
326 # if defined(HAVE_WIN32)
328 if (win32_cgets(buf, size) == NULL) {
337 passwd = getpass(prompt);
338 bstrncpy(buf, passwd, size);
348 #define READLINE_LIBRARY 1
349 #include "readline.h"
352 static char eol = '\0';
353 static int eolcmd(FILE *input, BSOCK *UA_sock)
355 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
357 } else if (argc == 1) {
360 sendit(_("Illegal separator character.\n"));
366 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
368 static char *line = NULL;
369 static char *next = NULL;
370 static int do_history = 0;
375 rl_catch_signals = 0; /* do it ourselves */
376 /* Here, readline does ***real*** malloc
377 * so, be we have to use the real free
379 line = readline((char *)prompt); /* cast needed for old readlines */
383 strip_trailing_junk(line);
388 sendit(_("Command logic problem\n"));
395 * Split "line" into multiple commands separated by the eol character.
396 * Each part is pointed to by "next" until finally it becomes null.
401 next = strchr(command, eol);
406 if (command != line && isatty(fileno(input))) {
407 senditf("%s%s\n", prompt, command);
410 sock->msglen = pm_strcpy(&sock->msg, command);
419 actuallyfree(line); /* allocated by readline() malloc */
425 #else /* no readline, do it ourselves */
428 static bool bisatty(int fd)
438 * Returns: 1 if data available
443 wait_for_data(int fd, int sec)
445 #if defined(HAVE_WIN32)
455 FD_SET((unsigned)fd, &fdset);
456 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
457 case 0: /* timeout */
460 if (errno == EINTR || errno == EAGAIN) {
463 return -1; /* error return */
472 * Get next input command from terminal.
474 * Returns: 1 if got input
479 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
483 if (output == stdout || teeout) {
488 switch (wait_for_data(fileno(input), sec)) {
490 return 0; /* timeout */
492 return -1; /* error */
494 len = sizeof_pool_memory(sock->msg) - 1;
500 if (bisatty(fileno(input))) {
501 input_line(sock->msg, len);
505 #ifdef HAVE_WIN32 /* use special console for input on win32 */
506 if (input == stdin) {
507 if (win32_cgets(sock->msg, len) == NULL) {
513 if (fgets(sock->msg, len, input) == NULL) {
522 strip_trailing_junk(sock->msg);
523 sock->msglen = strlen(sock->msg);
527 #endif /* ! HAVE_READLINE */
530 static int console_update_history(const char *histfile)
535 /* first, try to truncate the history file, and if it
536 * fail, the file is probably not present, and we
537 * can use write_history to create it
540 if (history_truncate_file(histfile, 100) == 0) {
541 ret = append_history(history_length, histfile);
543 ret = write_history(histfile);
551 static int console_init_history(const char *histfile)
558 ret = read_history(histfile);
565 /*********************************************************************
567 * Main Bacula Console -- User Interface Program
570 int main(int argc, char *argv[])
573 bool no_signals = false;
574 bool test_config = false;
578 setlocale(LC_ALL, "");
579 bindtextdomain("bacula", LOCALEDIR);
580 textdomain("bacula");
583 my_name_is(argc, argv, "bconsole");
584 init_msg(NULL, NULL);
585 working_directory = "/tmp";
586 args = get_pool_memory(PM_FNAME);
588 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
590 case 'c': /* configuration file */
591 if (configfile != NULL) {
594 configfile = bstrdup(optarg);
598 if (*optarg == 't') {
599 dbg_timestamp = true;
601 debug_level = atoi(optarg);
602 if (debug_level <= 0) {
608 case 'n': /* no conio */
612 case 's': /* turn off signals */
630 init_signals(terminate_console);
634 #if !defined(HAVE_WIN32)
635 /* Override Bacula default signals */
636 signal(SIGQUIT, SIG_IGN);
637 signal(SIGTSTP, got_sigstop);
638 signal(SIGCONT, got_sigcontinue);
639 signal(SIGTTIN, got_sigtin);
640 signal(SIGTTOU, got_sigtout);
651 if (configfile == NULL) {
652 configfile = bstrdup(CONFIG_FILE);
655 config = new_config_parser();
656 parse_cons_config(config, configfile, M_ERROR_TERM);
658 if (init_crypto() != 0) {
659 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
662 if (!check_resources()) {
663 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
671 terminate_console(0);
675 memset(&jcr, 0, sizeof(jcr));
677 (void)WSA_Init(); /* Initialize Windows sockets */
681 foreach_res(dir, R_DIRECTOR) {
685 foreach_res(cons, R_CONSOLE) {
691 struct sockaddr client_addr;
692 memset(&client_addr, 0, sizeof(client_addr));
693 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
695 sendit(_("Available Directors:\n"));
698 foreach_res(dir, R_DIRECTOR) {
699 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
703 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
704 (void)WSACleanup(); /* Cleanup Windows sockets */
707 if (!is_a_number(UA_sock->msg)) {
708 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
709 UA_sock->msg, numdir);
712 item = atoi(UA_sock->msg);
713 if (item < 0 || item > numdir) {
714 senditf(_("You must enter a number between 1 and %d\n"), numdir);
719 for (i=0; i<item; i++) {
720 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
722 /* Look for a console linked to this director */
723 for (i=0; i<numcon; i++) {
724 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
725 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
730 /* Look for the first non-linked console */
732 for (i=0; i<numcon; i++) {
733 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
734 if (cons->director == NULL)
741 /* If no director, take first one */
744 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
747 /* If no console, take first one */
750 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
754 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
757 /* Initialize Console TLS context */
758 if (cons && (cons->tls_enable || cons->tls_require)) {
759 /* Generate passphrase prompt */
760 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
762 /* Initialize TLS context:
763 * Args: CA certfile, CA certdir, Certfile, Keyfile,
764 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
766 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
767 cons->tls_ca_certdir, cons->tls_certfile,
768 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
770 if (!cons->tls_ctx) {
771 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
773 terminate_console(0);
778 /* Initialize Director TLS context */
779 if (dir->tls_enable || dir->tls_require) {
780 /* Generate passphrase prompt */
781 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
783 /* Initialize TLS context:
784 * Args: CA certfile, CA certdir, Certfile, Keyfile,
785 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
786 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
787 dir->tls_ca_certdir, dir->tls_certfile,
788 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
791 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
793 terminate_console(0);
798 if (dir->heartbeat_interval) {
799 heart_beat = dir->heartbeat_interval;
801 heart_beat = cons->heartbeat_interval;
805 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
806 NULL, dir->DIRport, 0);
807 if (UA_sock == NULL) {
808 terminate_console(0);
811 jcr.dir_bsock = UA_sock;
813 /* If cons==NULL, default console will be used */
814 if (!authenticate_director(&jcr, dir, cons)) {
815 terminate_console(0);
819 Dmsg0(40, "Opened connection with Director daemon\n");
821 sendit(_("Enter a period to cancel a command.\n"));
823 /* Read/Update history file if HOME exists */
824 POOL_MEM history_file;
826 /* Run commands in ~/.bconsolerc if any */
827 char *env = getenv("HOME");
830 pm_strcpy(&UA_sock->msg, env);
831 pm_strcat(&UA_sock->msg, "/.bconsolerc");
832 fd = fopen(UA_sock->msg, "rb");
834 read_and_process_input(fd, UA_sock);
838 pm_strcpy(history_file, env);
839 pm_strcat(history_file, "/.bconsole_history");
840 console_init_history(history_file.c_str());
843 read_and_process_input(stdin, UA_sock);
846 UA_sock->signal(BNET_TERMINATE); /* send EOF */
851 console_update_history(history_file.c_str());
854 terminate_console(0);
858 /* Cleanup and then exit */
859 static void terminate_console(int sig)
862 static bool already_here = false;
864 if (already_here) { /* avoid recursive temination problems */
868 config->free_resources();
872 free_pool_memory(args);
876 (void)WSACleanup(); /* Cleanup Windows sockets */
886 * Make a quick check to see that we have all the
889 static int check_resources()
898 foreach_res(director, R_DIRECTOR) {
901 /* tls_require implies tls_enable */
902 if (director->tls_require) {
904 director->tls_enable = true;
906 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
911 tls_needed = director->tls_enable || director->tls_authenticate;
913 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
914 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
915 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
916 " At least one CA certificate store is required.\n"),
917 director->hdr.name, configfile);
923 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
924 "Without that I don't how to speak to the Director :-(\n"), configfile);
929 /* Loop over Consoles */
930 foreach_res(cons, R_CONSOLE) {
931 /* tls_require implies tls_enable */
932 if (cons->tls_require) {
934 cons->tls_enable = true;
936 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
941 tls_needed = cons->tls_enable || cons->tls_authenticate;
942 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
943 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
944 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
945 cons->hdr.name, configfile);
955 static int versioncmd(FILE *input, BSOCK *UA_sock)
957 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
958 HOST_OS, DISTNAME, DISTVER);
962 static int inputcmd(FILE *input, BSOCK *UA_sock)
967 sendit(_("Too many arguments on input command.\n"));
971 sendit(_("First argument to input command must be a filename.\n"));
974 fd = fopen(argk[1], "rb");
977 senditf(_("Cannot open file %s for input. ERR=%s\n"),
978 argk[1], be.bstrerror());
981 read_and_process_input(fd, UA_sock);
986 /* Send output to both termina and specified file */
987 static int teecmd(FILE *input, BSOCK *UA_sock)
990 return do_outputcmd(input, UA_sock);
993 /* Send output to specified "file" */
994 static int outputcmd(FILE *input, BSOCK *UA_sock)
997 return do_outputcmd(input, UA_sock);
1001 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1004 const char *mode = "a+b";
1007 sendit(_("Too many arguments on output/tee command.\n"));
1011 if (output != stdout) {
1021 fd = fopen(argk[1], mode);
1024 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1025 argk[1], be.bstrerror(errno));
1033 * exec "some-command" [wait-seconds]
1035 static int execcmd(FILE *input, BSOCK *UA_sock)
1043 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1047 wait = atoi(argk[2]);
1049 bpipe = open_bpipe(argk[1], wait, "r");
1052 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1053 argk[1], be.bstrerror(errno));
1057 while (fgets(line, sizeof(line), bpipe->rfd)) {
1058 senditf("%s", line);
1060 stat = close_bpipe(bpipe);
1064 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1070 static int echocmd(FILE *input, BSOCK *UA_sock)
1072 for (int i=1; i < argc; i++) {
1073 senditf("%s ", argk[i]);
1079 static int quitcmd(FILE *input, BSOCK *UA_sock)
1084 static int helpcmd(FILE *input, BSOCK *UA_sock)
1087 for (i=0; i<comsize; i++) {
1088 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1094 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1097 sleep(atoi(argk[1]));
1103 static int timecmd(FILE *input, BSOCK *UA_sock)
1106 time_t ttime = time(NULL);
1108 (void)localtime_r(&ttime, &tm);
1109 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1115 * Send a line to the output file and or the terminal
1117 void senditf(const char *fmt,...)
1122 va_start(arg_ptr, fmt);
1123 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1128 void sendit(const char *buf)
1132 if (output == stdout || teeout) {
1135 * Here, we convert every \n into \r\n because the
1136 * terminal is in raw mode when we are using
1139 for (p=q=buf; (p=strchr(q, '\n')); ) {
1142 memcpy(obuf, q, len);
1144 memcpy(obuf+len, "\r\n", 3);
1145 q = ++p; /* point after \n */
1146 fputs(obuf, output);
1153 if (output != stdout) {