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)) {
357 } else if (argc == 1) {
361 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 line = readline((char *)prompt); /* cast needed for old readlines */
380 strip_trailing_junk(line);
385 sendit(_("Command logic problem\n"));
392 * Split "line" into multiple commands separated by the eol character.
393 * Each part is pointed to by "next" until finally it becomes null.
398 next = strchr(command, eol);
403 if (command != line && isatty(fileno(input))) {
404 senditf("%s%s\n", prompt, command);
407 sock->msglen = pm_strcpy(&sock->msg, command);
422 #else /* no readline, do it ourselves */
425 static bool bisatty(int fd)
435 * Returns: 1 if data available
440 wait_for_data(int fd, int sec)
442 #if defined(HAVE_WIN32)
452 FD_SET((unsigned)fd, &fdset);
453 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
454 case 0: /* timeout */
457 if (errno == EINTR || errno == EAGAIN) {
460 return -1; /* error return */
469 * Get next input command from terminal.
471 * Returns: 1 if got input
476 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
480 if (output == stdout || teeout) {
485 switch (wait_for_data(fileno(input), sec)) {
487 return 0; /* timeout */
489 return -1; /* error */
491 len = sizeof_pool_memory(sock->msg) - 1;
497 if (bisatty(fileno(input))) {
498 input_line(sock->msg, len);
502 #ifdef HAVE_WIN32 /* use special console for input on win32 */
503 if (input == stdin) {
504 if (win32_cgets(sock->msg, len) == NULL) {
510 if (fgets(sock->msg, len, input) == NULL) {
519 strip_trailing_junk(sock->msg);
520 sock->msglen = strlen(sock->msg);
524 #endif /* ! HAVE_READLINE */
527 static int console_update_history(const char *histfile)
532 /* first, try to truncate the history file, and if it
533 * fail, the file is probably not present, and we
534 * can use write_history to create it
537 if (history_truncate_file(histfile, 100) == 0) {
538 ret = append_history(history_length, histfile);
540 ret = write_history(histfile);
548 static int console_init_history(const char *histfile)
555 ret = read_history(histfile);
562 /*********************************************************************
564 * Main Bacula Console -- User Interface Program
567 int main(int argc, char *argv[])
570 bool no_signals = false;
571 bool test_config = false;
575 setlocale(LC_ALL, "");
576 bindtextdomain("bacula", LOCALEDIR);
577 textdomain("bacula");
580 my_name_is(argc, argv, "bconsole");
581 init_msg(NULL, NULL);
582 working_directory = "/tmp";
583 args = get_pool_memory(PM_FNAME);
585 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
587 case 'c': /* configuration file */
588 if (configfile != NULL) {
591 configfile = bstrdup(optarg);
595 if (*optarg == 't') {
596 dbg_timestamp = true;
598 debug_level = atoi(optarg);
599 if (debug_level <= 0) {
605 case 'n': /* no conio */
609 case 's': /* turn off signals */
627 init_signals(terminate_console);
631 #if !defined(HAVE_WIN32)
632 /* Override Bacula default signals */
633 signal(SIGQUIT, SIG_IGN);
634 signal(SIGTSTP, got_sigstop);
635 signal(SIGCONT, got_sigcontinue);
636 signal(SIGTTIN, got_sigtin);
637 signal(SIGTTOU, got_sigtout);
648 if (configfile == NULL) {
649 configfile = bstrdup(CONFIG_FILE);
652 config = new_config_parser();
653 parse_cons_config(config, configfile, M_ERROR_TERM);
655 if (init_crypto() != 0) {
656 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
659 if (!check_resources()) {
660 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
668 terminate_console(0);
672 memset(&jcr, 0, sizeof(jcr));
674 (void)WSA_Init(); /* Initialize Windows sockets */
678 foreach_res(dir, R_DIRECTOR) {
682 foreach_res(cons, R_CONSOLE) {
688 struct sockaddr client_addr;
689 memset(&client_addr, 0, sizeof(client_addr));
690 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
692 sendit(_("Available Directors:\n"));
695 foreach_res(dir, R_DIRECTOR) {
696 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
700 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
701 (void)WSACleanup(); /* Cleanup Windows sockets */
704 if (!is_a_number(UA_sock->msg)) {
705 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
706 UA_sock->msg, numdir);
709 item = atoi(UA_sock->msg);
710 if (item < 0 || item > numdir) {
711 senditf(_("You must enter a number between 1 and %d\n"), numdir);
716 for (i=0; i<item; i++) {
717 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
719 /* Look for a console linked to this director */
720 for (i=0; i<numcon; i++) {
721 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
722 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
727 /* Look for the first non-linked console */
729 for (i=0; i<numcon; i++) {
730 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
731 if (cons->director == NULL)
738 /* If no director, take first one */
741 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
744 /* If no console, take first one */
747 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
751 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
754 /* Initialize Console TLS context */
755 if (cons && (cons->tls_enable || cons->tls_require)) {
756 /* Generate passphrase prompt */
757 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
759 /* Initialize TLS context:
760 * Args: CA certfile, CA certdir, Certfile, Keyfile,
761 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
763 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
764 cons->tls_ca_certdir, cons->tls_certfile,
765 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
767 if (!cons->tls_ctx) {
768 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
770 terminate_console(0);
775 /* Initialize Director TLS context */
776 if (dir->tls_enable || dir->tls_require) {
777 /* Generate passphrase prompt */
778 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
780 /* Initialize TLS context:
781 * Args: CA certfile, CA certdir, Certfile, Keyfile,
782 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
783 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
784 dir->tls_ca_certdir, dir->tls_certfile,
785 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
788 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
790 terminate_console(0);
795 if (dir->heartbeat_interval) {
796 heart_beat = dir->heartbeat_interval;
798 heart_beat = cons->heartbeat_interval;
802 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
803 NULL, dir->DIRport, 0);
804 if (UA_sock == NULL) {
805 terminate_console(0);
808 jcr.dir_bsock = UA_sock;
810 /* If cons==NULL, default console will be used */
811 if (!authenticate_director(&jcr, dir, cons)) {
812 terminate_console(0);
816 Dmsg0(40, "Opened connection with Director daemon\n");
818 sendit(_("Enter a period to cancel a command.\n"));
820 /* Read/Update history file if HOME exists */
821 POOL_MEM history_file;
823 /* Run commands in ~/.bconsolerc if any */
824 char *env = getenv("HOME");
827 pm_strcpy(&UA_sock->msg, env);
828 pm_strcat(&UA_sock->msg, "/.bconsolerc");
829 fd = fopen(UA_sock->msg, "rb");
831 read_and_process_input(fd, UA_sock);
835 pm_strcpy(history_file, env);
836 pm_strcat(history_file, "/.bconsole_history");
837 console_init_history(history_file.c_str());
840 read_and_process_input(stdin, UA_sock);
843 UA_sock->signal(BNET_TERMINATE); /* send EOF */
848 console_update_history(history_file.c_str());
851 terminate_console(0);
855 /* Cleanup and then exit */
856 static void terminate_console(int sig)
859 static bool already_here = false;
861 if (already_here) { /* avoid recursive temination problems */
865 config->free_resources();
869 free_pool_memory(args);
873 (void)WSACleanup(); /* Cleanup Windows sockets */
881 * Make a quick check to see that we have all the
884 static int check_resources()
893 foreach_res(director, R_DIRECTOR) {
896 /* tls_require implies tls_enable */
897 if (director->tls_require) {
899 director->tls_enable = true;
901 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
906 tls_needed = director->tls_enable || director->tls_authenticate;
908 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
909 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
910 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
911 " At least one CA certificate store is required.\n"),
912 director->hdr.name, configfile);
918 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
919 "Without that I don't how to speak to the Director :-(\n"), configfile);
924 /* Loop over Consoles */
925 foreach_res(cons, R_CONSOLE) {
926 /* tls_require implies tls_enable */
927 if (cons->tls_require) {
929 cons->tls_enable = true;
931 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
936 tls_needed = cons->tls_enable || cons->tls_authenticate;
937 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
938 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
939 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
940 cons->hdr.name, configfile);
950 static int versioncmd(FILE *input, BSOCK *UA_sock)
952 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
953 HOST_OS, DISTNAME, DISTVER);
957 static int inputcmd(FILE *input, BSOCK *UA_sock)
962 sendit(_("Too many arguments on input command.\n"));
966 sendit(_("First argument to input command must be a filename.\n"));
969 fd = fopen(argk[1], "rb");
972 senditf(_("Cannot open file %s for input. ERR=%s\n"),
973 argk[1], be.bstrerror());
976 read_and_process_input(fd, UA_sock);
981 /* Send output to both termina and specified file */
982 static int teecmd(FILE *input, BSOCK *UA_sock)
985 return do_outputcmd(input, UA_sock);
988 /* Send output to specified "file" */
989 static int outputcmd(FILE *input, BSOCK *UA_sock)
992 return do_outputcmd(input, UA_sock);
996 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
999 const char *mode = "a+b";
1002 sendit(_("Too many arguments on output/tee command.\n"));
1006 if (output != stdout) {
1016 fd = fopen(argk[1], mode);
1019 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1020 argk[1], be.bstrerror(errno));
1028 * exec "some-command" [wait-seconds]
1030 static int execcmd(FILE *input, BSOCK *UA_sock)
1038 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1042 wait = atoi(argk[2]);
1044 bpipe = open_bpipe(argk[1], wait, "r");
1047 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1048 argk[1], be.bstrerror(errno));
1052 while (fgets(line, sizeof(line), bpipe->rfd)) {
1053 senditf("%s", line);
1055 stat = close_bpipe(bpipe);
1059 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1065 static int echocmd(FILE *input, BSOCK *UA_sock)
1067 for (int i=1; i < argc; i++) {
1068 senditf("%s", argk[i]);
1075 static int quitcmd(FILE *input, BSOCK *UA_sock)
1080 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1083 sleep(atoi(argk[1]));
1089 static int timecmd(FILE *input, BSOCK *UA_sock)
1092 time_t ttime = time(NULL);
1094 (void)localtime_r(&ttime, &tm);
1095 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1101 * Send a line to the output file and or the terminal
1103 void senditf(const char *fmt,...)
1108 va_start(arg_ptr, fmt);
1109 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1114 void sendit(const char *buf)
1118 if (output == stdout || teeout) {
1121 * Here, we convert every \n into \r\n because the
1122 * terminal is in raw mode when we are using
1125 for (p=q=buf; (p=strchr(q, '\n')); ) {
1128 memcpy(obuf, q, len);
1130 memcpy(obuf+len, "\r\n", 3);
1131 q = ++p; /* point after \n */
1132 fputs(obuf, output);
1139 if (output != stdout) {