2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 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 John Walker.
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);
65 /* Forward referenced functions */
66 static void terminate_console(int sig);
67 static int check_resources();
68 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
69 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
70 void senditf(const char *fmt, ...);
71 void sendit(const char *buf);
73 extern "C" void got_sigstop(int sig);
74 extern "C" void got_sigcontinue(int sig);
75 extern "C" void got_sigtout(int sig);
76 extern "C" void got_sigtin(int sig);
79 /* Static variables */
80 static char *configfile = NULL;
81 static BSOCK *UA_sock = NULL;
82 static DIRRES *dir = NULL;
83 static CONRES *cons = NULL;
84 static FILE *output = stdout;
85 static bool teeout = false; /* output to output and stdout */
86 static bool stop = false;
87 static bool no_conio = false;
92 static char *argk[MAX_CMD_ARGS];
93 static char *argv[MAX_CMD_ARGS];
96 /* Command prototypes */
97 static int versioncmd(FILE *input, BSOCK *UA_sock);
98 static int inputcmd(FILE *input, BSOCK *UA_sock);
99 static int outputcmd(FILE *input, BSOCK *UA_sock);
100 static int teecmd(FILE *input, BSOCK *UA_sock);
101 static int quitcmd(FILE *input, BSOCK *UA_sock);
102 static int echocmd(FILE *input, BSOCK *UA_sock);
103 static int timecmd(FILE *input, BSOCK *UA_sock);
104 static int sleepcmd(FILE *input, BSOCK *UA_sock);
105 static int execcmd(FILE *input, BSOCK *UA_sock);
108 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
114 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
115 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
116 " -c <file> set configuration file to file\n"
117 " -d <nn> set debug level to <nn>\n"
118 " -dt print timestamp in debug output\n"
121 " -t test - read configuration and exit\n"
122 " -? print this message.\n"
123 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
128 void got_sigstop(int sig)
134 void got_sigcontinue(int sig)
140 void got_sigtout(int sig)
142 // printf("Got tout\n");
146 void got_sigtin(int sig)
148 // printf("Got tin\n");
152 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
159 * These are the @command
161 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
162 static struct cmdstruct commands[] = {
163 { N_("input"), inputcmd, _("input from file")},
164 { N_("output"), outputcmd, _("output to file")},
165 { N_("quit"), quitcmd, _("quit")},
166 { N_("tee"), teecmd, _("output to file and terminal")},
167 { N_("sleep"), sleepcmd, _("sleep specified time")},
168 { N_("time"), timecmd, _("print current time")},
169 { N_("version"), versioncmd, _("print Console's version")},
170 { N_("echo"), echocmd, _("echo command string")},
171 { N_("exec"), execcmd, _("execute an external command")},
172 { N_("exit"), quitcmd, _("exit = quit")},
173 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
175 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
177 static int do_a_command(FILE *input, BSOCK *UA_sock)
188 Dmsg1(120, "Command: %s\n", UA_sock->msg);
194 if (*cmd == '#') { /* comment */
198 for (i=0; i<comsize; i++) { /* search for command */
199 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
200 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
206 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
207 UA_sock->msglen = strlen(UA_sock->msg);
208 sendit(UA_sock->msg);
214 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
216 const char *prompt = "*";
217 bool at_prompt = false;
218 int tty_input = isatty(fileno(input));
222 if (at_prompt) { /* don't prompt multiple times */
229 stat = get_cmd(input, prompt, UA_sock, 30);
237 /* Reading input from a file */
238 int len = sizeof_pool_memory(UA_sock->msg) - 1;
242 if (fgets(UA_sock->msg, len, input) == NULL) {
245 sendit(UA_sock->msg); /* echo to terminal */
246 strip_trailing_junk(UA_sock->msg);
247 UA_sock->msglen = strlen(UA_sock->msg);
252 break; /* error or interrupt */
253 } else if (stat == 0) { /* timeout */
254 if (strcmp(prompt, "*") == 0) {
255 bnet_fsend(UA_sock, ".messages");
261 /* @ => internal command for us */
262 if (UA_sock->msg[0] == '@') {
263 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
264 if (!do_a_command(input, UA_sock)) {
269 if (!bnet_send(UA_sock)) { /* send command */
273 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
276 while ((stat = bnet_recv(UA_sock)) >= 0) {
283 /* Suppress output if running in background or user hit ctl-c */
284 if (!stop && !usrbrk()) {
285 sendit(UA_sock->msg);
296 if (is_bnet_stop(UA_sock)) {
297 break; /* error or term */
298 } else if (stat == BNET_SIGNAL) {
299 if (UA_sock->msglen == BNET_PROMPT) {
302 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
308 * Call-back for reading a passphrase for an encrypted PEM file
309 * This function uses getpass(),
310 * which uses a static buffer and is NOT thread-safe.
312 static int tls_pem_callback(char *buf, int size, const void *userdata)
315 const char *prompt = (const char *)userdata;
316 # if defined(HAVE_WIN32)
318 if (win32_cgets(buf, size) == NULL) {
327 passwd = getpass(prompt);
328 bstrncpy(buf, passwd, size);
338 #define READLINE_LIBRARY 1
340 #include "readline.h"
344 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
348 rl_catch_signals = 0; /* do it ourselves */
349 line = readline((char *)prompt); /* cast needed for old readlines */
354 strip_trailing_junk(line);
355 sock->msglen = pm_strcpy(&sock->msg, line);
357 add_history(sock->msg);
363 #else /* no readline, do it ourselves */
365 #if !defined(HAVE_WIN32)
366 static bool bisatty(int fd)
376 * Returns: 1 if data available
381 wait_for_data(int fd, int sec)
383 #if defined(HAVE_WIN32)
393 FD_SET((unsigned)fd, &fdset);
394 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
395 case 0: /* timeout */
398 if (errno == EINTR || errno == EAGAIN) {
401 return -1; /* error return */
410 * Get next input command from terminal.
412 * Returns: 1 if got input
417 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
421 if (output == stdout || teeout) {
426 switch (wait_for_data(fileno(input), sec)) {
428 return 0; /* timeout */
430 return -1; /* error */
432 len = sizeof_pool_memory(sock->msg) - 1;
438 if (bisatty(fileno(input))) {
439 input_line(sock->msg, len);
443 #ifdef HAVE_WIN32 /* use special console for input on win32 */
444 if (input == stdin) {
445 if (win32_cgets(sock->msg, len) == NULL) {
451 if (fgets(sock->msg, len, input) == NULL) {
460 strip_trailing_junk(sock->msg);
461 sock->msglen = strlen(sock->msg);
465 #endif /* ! HAVE_READLINE */
468 static int console_update_history(const char *histfile)
473 /* first, try to truncate the history file, and if it
474 * fail, the file is probably not present, and we
475 * can use write_history to create it
478 if (history_truncate_file(histfile, 100) == 0) {
479 ret = append_history(history_length, histfile);
481 ret = write_history(histfile);
489 static int console_init_history(const char *histfile)
496 ret = read_history(histfile);
503 /*********************************************************************
505 * Main Bacula Console -- User Interface Program
508 int main(int argc, char *argv[])
511 bool no_signals = false;
512 bool test_config = false;
516 setlocale(LC_ALL, "");
517 bindtextdomain("bacula", LOCALEDIR);
518 textdomain("bacula");
521 my_name_is(argc, argv, "bconsole");
522 init_msg(NULL, NULL);
523 working_directory = "/tmp";
524 args = get_pool_memory(PM_FNAME);
526 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
528 case 'c': /* configuration file */
529 if (configfile != NULL) {
532 configfile = bstrdup(optarg);
536 if (*optarg == 't') {
537 dbg_timestamp = true;
539 debug_level = atoi(optarg);
540 if (debug_level <= 0) {
546 case 'n': /* no conio */
550 case 's': /* turn off signals */
568 init_signals(terminate_console);
572 #if !defined(HAVE_WIN32)
573 /* Override Bacula default signals */
574 signal(SIGQUIT, SIG_IGN);
575 signal(SIGTSTP, got_sigstop);
576 signal(SIGCONT, got_sigcontinue);
577 signal(SIGTTIN, got_sigtin);
578 signal(SIGTTOU, got_sigtout);
589 if (configfile == NULL) {
590 configfile = bstrdup(CONFIG_FILE);
593 parse_config(configfile);
595 if (init_crypto() != 0) {
596 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
599 if (!check_resources()) {
600 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
608 terminate_console(0);
612 memset(&jcr, 0, sizeof(jcr));
614 (void)WSA_Init(); /* Initialize Windows sockets */
618 foreach_res(dir, R_DIRECTOR) {
622 foreach_res(cons, R_CONSOLE) {
628 struct sockaddr client_addr;
629 memset(&client_addr, 0, sizeof(client_addr));
630 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
632 sendit(_("Available Directors:\n"));
635 foreach_res(dir, R_DIRECTOR) {
636 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
640 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
641 (void)WSACleanup(); /* Cleanup Windows sockets */
644 if (!is_a_number(UA_sock->msg)) {
645 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
646 UA_sock->msg, numdir);
649 item = atoi(UA_sock->msg);
650 if (item < 0 || item > numdir) {
651 senditf(_("You must enter a number between 1 and %d\n"), numdir);
656 for (i=0; i<item; i++) {
657 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
659 /* Look for a console linked to this director */
660 for (i=0; i<numcon; i++) {
661 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
662 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
667 /* Look for the first non-linked console */
669 for (i=0; i<numcon; i++) {
670 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
671 if (cons->director == NULL)
678 /* If no director, take first one */
681 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
684 /* If no console, take first one */
687 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
691 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
694 /* Initialize Console TLS context */
695 if (cons && (cons->tls_enable || cons->tls_require)) {
696 /* Generate passphrase prompt */
697 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
699 /* Initialize TLS context:
700 * Args: CA certfile, CA certdir, Certfile, Keyfile,
701 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
703 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
704 cons->tls_ca_certdir, cons->tls_certfile,
705 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
707 if (!cons->tls_ctx) {
708 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
710 terminate_console(0);
715 /* Initialize Director TLS context */
716 if (dir->tls_enable || dir->tls_require) {
717 /* Generate passphrase prompt */
718 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
720 /* Initialize TLS context:
721 * Args: CA certfile, CA certdir, Certfile, Keyfile,
722 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
723 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
724 dir->tls_ca_certdir, dir->tls_certfile,
725 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
728 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
730 terminate_console(0);
735 if (dir->heartbeat_interval) {
736 heart_beat = dir->heartbeat_interval;
738 heart_beat = cons->heartbeat_interval;
742 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
743 NULL, dir->DIRport, 0);
744 if (UA_sock == NULL) {
745 terminate_console(0);
748 jcr.dir_bsock = UA_sock;
750 /* If cons==NULL, default console will be used */
751 if (!authenticate_director(&jcr, dir, cons)) {
752 terminate_console(0);
756 Dmsg0(40, "Opened connection with Director daemon\n");
758 sendit(_("Enter a period to cancel a command.\n"));
760 /* Read/Update history file if HOME exists */
761 POOL_MEM history_file;
763 /* Run commands in ~/.bconsolerc if any */
764 char *env = getenv("HOME");
767 pm_strcpy(&UA_sock->msg, env);
768 pm_strcat(&UA_sock->msg, "/.bconsolerc");
769 fd = fopen(UA_sock->msg, "rb");
771 read_and_process_input(fd, UA_sock);
775 pm_strcpy(history_file, env);
776 pm_strcat(history_file, "/.bconsole_history");
777 console_init_history(history_file.c_str());
780 read_and_process_input(stdin, UA_sock);
783 UA_sock->signal(BNET_TERMINATE); /* send EOF */
788 console_update_history(history_file.c_str());
791 terminate_console(0);
795 /* Cleanup and then exit */
796 static void terminate_console(int sig)
799 static bool already_here = false;
801 if (already_here) { /* avoid recursive temination problems */
806 free_pool_memory(args);
810 (void)WSACleanup(); /* Cleanup Windows sockets */
818 * Make a quick check to see that we have all the
821 static int check_resources()
829 foreach_res(director, R_DIRECTOR) {
832 /* tls_require implies tls_enable */
833 if (director->tls_require) {
835 director->tls_enable = true;
837 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
843 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
844 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
845 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
846 " At least one CA certificate store is required.\n"),
847 director->hdr.name, configfile);
853 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
854 "Without that I don't how to speak to the Director :-(\n"), configfile);
859 /* Loop over Consoles */
860 foreach_res(cons, R_CONSOLE) {
861 /* tls_require implies tls_enable */
862 if (cons->tls_require) {
864 cons->tls_enable = true;
866 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
872 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
873 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
874 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
875 cons->hdr.name, configfile);
885 static int versioncmd(FILE *input, BSOCK *UA_sock)
887 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
888 HOST_OS, DISTNAME, DISTVER);
892 static int inputcmd(FILE *input, BSOCK *UA_sock)
897 sendit(_("Too many arguments on input command.\n"));
901 sendit(_("First argument to input command must be a filename.\n"));
904 fd = fopen(argk[1], "rb");
907 senditf(_("Cannot open file %s for input. ERR=%s\n"),
908 argk[1], be.bstrerror());
911 read_and_process_input(fd, UA_sock);
916 /* Send output to both termina and specified file */
917 static int teecmd(FILE *input, BSOCK *UA_sock)
920 return do_outputcmd(input, UA_sock);
923 /* Send output to specified "file" */
924 static int outputcmd(FILE *input, BSOCK *UA_sock)
927 return do_outputcmd(input, UA_sock);
931 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
934 const char *mode = "a+b";
937 sendit(_("Too many arguments on output/tee command.\n"));
941 if (output != stdout) {
951 fd = fopen(argk[1], mode);
954 senditf(_("Cannot open file %s for output. ERR=%s\n"),
955 argk[1], be.bstrerror(errno));
963 * exec "some-command" [wait-seconds]
965 static int execcmd(FILE *input, BSOCK *UA_sock)
973 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
977 wait = atoi(argk[2]);
979 bpipe = open_bpipe(argk[1], wait, "r");
982 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
983 argk[1], be.bstrerror(errno));
987 while (fgets(line, sizeof(line), bpipe->rfd)) {
990 stat = close_bpipe(bpipe);
994 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1000 static int echocmd(FILE *input, BSOCK *UA_sock)
1002 for (int i=1; i < argc; i++) {
1003 senditf("%s", argk[i]);
1010 static int quitcmd(FILE *input, BSOCK *UA_sock)
1015 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1018 sleep(atoi(argk[1]));
1024 static int timecmd(FILE *input, BSOCK *UA_sock)
1027 time_t ttime = time(NULL);
1029 (void)localtime_r(&ttime, &tm);
1030 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1036 * Send a line to the output file and or the terminal
1038 void senditf(const char *fmt,...)
1043 va_start(arg_ptr, fmt);
1044 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1049 void sendit(const char *buf)
1053 if (output == stdout || teeout) {
1056 * Here, we convert every \n into \r\n because the
1057 * terminal is in raw mode when we are using
1060 for (p=q=buf; (p=strchr(q, '\n')); ) {
1063 memcpy(obuf, q, len);
1065 memcpy(obuf+len, "\r\n", 3);
1066 q = ++p; /* point after \n */
1067 fputs(obuf, output);
1074 if (output != stdout) {