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 = ';';
352 static int eolcmd(FILE *input, BSOCK *UA_sock)
354 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
358 sendit(_("Missing or illegal separator character.\n"));
363 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
365 static char *line = NULL;
366 static char *next = NULL;
367 static int do_history = 0;
372 rl_catch_signals = 0; /* do it ourselves */
373 line = readline((char *)prompt); /* cast needed for old readlines */
378 strip_trailing_junk(line);
385 next = strchr(command, eol);
389 if (command != line && isatty(fileno(input))) {
390 senditf("%s%s\n", prompt, command);
393 sock->msglen = pm_strcpy(&sock->msg, command);
408 #else /* no readline, do it ourselves */
411 static bool bisatty(int fd)
421 * Returns: 1 if data available
426 wait_for_data(int fd, int sec)
428 #if defined(HAVE_WIN32)
438 FD_SET((unsigned)fd, &fdset);
439 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
440 case 0: /* timeout */
443 if (errno == EINTR || errno == EAGAIN) {
446 return -1; /* error return */
455 * Get next input command from terminal.
457 * Returns: 1 if got input
462 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
466 if (output == stdout || teeout) {
471 switch (wait_for_data(fileno(input), sec)) {
473 return 0; /* timeout */
475 return -1; /* error */
477 len = sizeof_pool_memory(sock->msg) - 1;
483 if (bisatty(fileno(input))) {
484 input_line(sock->msg, len);
488 #ifdef HAVE_WIN32 /* use special console for input on win32 */
489 if (input == stdin) {
490 if (win32_cgets(sock->msg, len) == NULL) {
496 if (fgets(sock->msg, len, input) == NULL) {
505 strip_trailing_junk(sock->msg);
506 sock->msglen = strlen(sock->msg);
510 #endif /* ! HAVE_READLINE */
513 static int console_update_history(const char *histfile)
518 /* first, try to truncate the history file, and if it
519 * fail, the file is probably not present, and we
520 * can use write_history to create it
523 if (history_truncate_file(histfile, 100) == 0) {
524 ret = append_history(history_length, histfile);
526 ret = write_history(histfile);
534 static int console_init_history(const char *histfile)
541 ret = read_history(histfile);
548 /*********************************************************************
550 * Main Bacula Console -- User Interface Program
553 int main(int argc, char *argv[])
556 bool no_signals = false;
557 bool test_config = false;
561 setlocale(LC_ALL, "");
562 bindtextdomain("bacula", LOCALEDIR);
563 textdomain("bacula");
566 my_name_is(argc, argv, "bconsole");
567 init_msg(NULL, NULL);
568 working_directory = "/tmp";
569 args = get_pool_memory(PM_FNAME);
571 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
573 case 'c': /* configuration file */
574 if (configfile != NULL) {
577 configfile = bstrdup(optarg);
581 if (*optarg == 't') {
582 dbg_timestamp = true;
584 debug_level = atoi(optarg);
585 if (debug_level <= 0) {
591 case 'n': /* no conio */
595 case 's': /* turn off signals */
613 init_signals(terminate_console);
617 #if !defined(HAVE_WIN32)
618 /* Override Bacula default signals */
619 signal(SIGQUIT, SIG_IGN);
620 signal(SIGTSTP, got_sigstop);
621 signal(SIGCONT, got_sigcontinue);
622 signal(SIGTTIN, got_sigtin);
623 signal(SIGTTOU, got_sigtout);
634 if (configfile == NULL) {
635 configfile = bstrdup(CONFIG_FILE);
638 config = new_config_parser();
639 parse_cons_config(config, configfile, M_ERROR_TERM);
641 if (init_crypto() != 0) {
642 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
645 if (!check_resources()) {
646 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
654 terminate_console(0);
658 memset(&jcr, 0, sizeof(jcr));
660 (void)WSA_Init(); /* Initialize Windows sockets */
664 foreach_res(dir, R_DIRECTOR) {
668 foreach_res(cons, R_CONSOLE) {
674 struct sockaddr client_addr;
675 memset(&client_addr, 0, sizeof(client_addr));
676 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
678 sendit(_("Available Directors:\n"));
681 foreach_res(dir, R_DIRECTOR) {
682 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
686 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
687 (void)WSACleanup(); /* Cleanup Windows sockets */
690 if (!is_a_number(UA_sock->msg)) {
691 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
692 UA_sock->msg, numdir);
695 item = atoi(UA_sock->msg);
696 if (item < 0 || item > numdir) {
697 senditf(_("You must enter a number between 1 and %d\n"), numdir);
702 for (i=0; i<item; i++) {
703 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
705 /* Look for a console linked to this director */
706 for (i=0; i<numcon; i++) {
707 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
708 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
713 /* Look for the first non-linked console */
715 for (i=0; i<numcon; i++) {
716 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
717 if (cons->director == NULL)
724 /* If no director, take first one */
727 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
730 /* If no console, take first one */
733 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
737 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
740 /* Initialize Console TLS context */
741 if (cons && (cons->tls_enable || cons->tls_require)) {
742 /* Generate passphrase prompt */
743 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
745 /* Initialize TLS context:
746 * Args: CA certfile, CA certdir, Certfile, Keyfile,
747 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
749 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
750 cons->tls_ca_certdir, cons->tls_certfile,
751 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
753 if (!cons->tls_ctx) {
754 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
756 terminate_console(0);
761 /* Initialize Director TLS context */
762 if (dir->tls_enable || dir->tls_require) {
763 /* Generate passphrase prompt */
764 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
766 /* Initialize TLS context:
767 * Args: CA certfile, CA certdir, Certfile, Keyfile,
768 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
769 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
770 dir->tls_ca_certdir, dir->tls_certfile,
771 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
774 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
776 terminate_console(0);
781 if (dir->heartbeat_interval) {
782 heart_beat = dir->heartbeat_interval;
784 heart_beat = cons->heartbeat_interval;
788 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
789 NULL, dir->DIRport, 0);
790 if (UA_sock == NULL) {
791 terminate_console(0);
794 jcr.dir_bsock = UA_sock;
796 /* If cons==NULL, default console will be used */
797 if (!authenticate_director(&jcr, dir, cons)) {
798 terminate_console(0);
802 Dmsg0(40, "Opened connection with Director daemon\n");
804 sendit(_("Enter a period to cancel a command.\n"));
806 /* Read/Update history file if HOME exists */
807 POOL_MEM history_file;
809 /* Run commands in ~/.bconsolerc if any */
810 char *env = getenv("HOME");
813 pm_strcpy(&UA_sock->msg, env);
814 pm_strcat(&UA_sock->msg, "/.bconsolerc");
815 fd = fopen(UA_sock->msg, "rb");
817 read_and_process_input(fd, UA_sock);
821 pm_strcpy(history_file, env);
822 pm_strcat(history_file, "/.bconsole_history");
823 console_init_history(history_file.c_str());
826 read_and_process_input(stdin, UA_sock);
829 UA_sock->signal(BNET_TERMINATE); /* send EOF */
834 console_update_history(history_file.c_str());
837 terminate_console(0);
841 /* Cleanup and then exit */
842 static void terminate_console(int sig)
845 static bool already_here = false;
847 if (already_here) { /* avoid recursive temination problems */
851 config->free_resources();
855 free_pool_memory(args);
859 (void)WSACleanup(); /* Cleanup Windows sockets */
867 * Make a quick check to see that we have all the
870 static int check_resources()
879 foreach_res(director, R_DIRECTOR) {
882 /* tls_require implies tls_enable */
883 if (director->tls_require) {
885 director->tls_enable = true;
887 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
892 tls_needed = director->tls_enable || director->tls_authenticate;
894 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
895 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
896 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
897 " At least one CA certificate store is required.\n"),
898 director->hdr.name, configfile);
904 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
905 "Without that I don't how to speak to the Director :-(\n"), configfile);
910 /* Loop over Consoles */
911 foreach_res(cons, R_CONSOLE) {
912 /* tls_require implies tls_enable */
913 if (cons->tls_require) {
915 cons->tls_enable = true;
917 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
922 tls_needed = cons->tls_enable || cons->tls_authenticate;
923 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
924 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
925 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
926 cons->hdr.name, configfile);
936 static int versioncmd(FILE *input, BSOCK *UA_sock)
938 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
939 HOST_OS, DISTNAME, DISTVER);
943 static int inputcmd(FILE *input, BSOCK *UA_sock)
948 sendit(_("Too many arguments on input command.\n"));
952 sendit(_("First argument to input command must be a filename.\n"));
955 fd = fopen(argk[1], "rb");
958 senditf(_("Cannot open file %s for input. ERR=%s\n"),
959 argk[1], be.bstrerror());
962 read_and_process_input(fd, UA_sock);
967 /* Send output to both termina and specified file */
968 static int teecmd(FILE *input, BSOCK *UA_sock)
971 return do_outputcmd(input, UA_sock);
974 /* Send output to specified "file" */
975 static int outputcmd(FILE *input, BSOCK *UA_sock)
978 return do_outputcmd(input, UA_sock);
982 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
985 const char *mode = "a+b";
988 sendit(_("Too many arguments on output/tee command.\n"));
992 if (output != stdout) {
1002 fd = fopen(argk[1], mode);
1005 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1006 argk[1], be.bstrerror(errno));
1014 * exec "some-command" [wait-seconds]
1016 static int execcmd(FILE *input, BSOCK *UA_sock)
1024 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1028 wait = atoi(argk[2]);
1030 bpipe = open_bpipe(argk[1], wait, "r");
1033 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1034 argk[1], be.bstrerror(errno));
1038 while (fgets(line, sizeof(line), bpipe->rfd)) {
1039 senditf("%s", line);
1041 stat = close_bpipe(bpipe);
1045 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1051 static int echocmd(FILE *input, BSOCK *UA_sock)
1053 for (int i=1; i < argc; i++) {
1054 senditf("%s", argk[i]);
1061 static int quitcmd(FILE *input, BSOCK *UA_sock)
1066 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1069 sleep(atoi(argk[1]));
1075 static int timecmd(FILE *input, BSOCK *UA_sock)
1078 time_t ttime = time(NULL);
1080 (void)localtime_r(&ttime, &tm);
1081 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1087 * Send a line to the output file and or the terminal
1089 void senditf(const char *fmt,...)
1094 va_start(arg_ptr, fmt);
1095 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1100 void sendit(const char *buf)
1104 if (output == stdout || teeout) {
1107 * Here, we convert every \n into \r\n because the
1108 * terminal is in raw mode when we are using
1111 for (p=q=buf; (p=strchr(q, '\n')); ) {
1114 memcpy(obuf, q, len);
1116 memcpy(obuf+len, "\r\n", 3);
1117 q = ++p; /* point after \n */
1118 fputs(obuf, output);
1125 if (output != stdout) {