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
347 #include "readline.h"
350 static char eol = '\0';
351 static int eolcmd(FILE *input, BSOCK *UA_sock)
353 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
355 } else if (argc == 1) {
358 sendit(_("Illegal separator character.\n"));
364 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
366 static char *line = NULL;
367 static char *next = NULL;
368 static int do_history = 0;
373 rl_catch_signals = 0; /* do it ourselves */
374 /* Here, readline does ***real*** malloc
375 * so, be we have to use the real free
377 line = readline((char *)prompt); /* cast needed for old readlines */
381 strip_trailing_junk(line);
386 sendit(_("Command logic problem\n"));
393 * Split "line" into multiple commands separated by the eol character.
394 * Each part is pointed to by "next" until finally it becomes null.
399 next = strchr(command, eol);
404 if (command != line && isatty(fileno(input))) {
405 senditf("%s%s\n", prompt, command);
408 sock->msglen = pm_strcpy(&sock->msg, command);
417 actuallyfree(line); /* allocated by readline() malloc */
423 #else /* no readline, do it ourselves */
426 static bool bisatty(int fd)
436 * Returns: 1 if data available
441 wait_for_data(int fd, int sec)
443 #if defined(HAVE_WIN32)
453 FD_SET((unsigned)fd, &fdset);
454 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
455 case 0: /* timeout */
458 if (errno == EINTR || errno == EAGAIN) {
461 return -1; /* error return */
470 * Get next input command from terminal.
472 * Returns: 1 if got input
477 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
481 if (output == stdout || teeout) {
486 switch (wait_for_data(fileno(input), sec)) {
488 return 0; /* timeout */
490 return -1; /* error */
492 len = sizeof_pool_memory(sock->msg) - 1;
498 if (bisatty(fileno(input))) {
499 input_line(sock->msg, len);
503 #ifdef HAVE_WIN32 /* use special console for input on win32 */
504 if (input == stdin) {
505 if (win32_cgets(sock->msg, len) == NULL) {
511 if (fgets(sock->msg, len, input) == NULL) {
520 strip_trailing_junk(sock->msg);
521 sock->msglen = strlen(sock->msg);
525 #endif /* ! HAVE_READLINE */
528 static int console_update_history(const char *histfile)
533 /* first, try to truncate the history file, and if it
534 * fail, the file is probably not present, and we
535 * can use write_history to create it
538 if (history_truncate_file(histfile, 100) == 0) {
539 ret = append_history(history_length, histfile);
541 ret = write_history(histfile);
549 static int console_init_history(const char *histfile)
556 ret = read_history(histfile);
563 /*********************************************************************
565 * Main Bacula Console -- User Interface Program
568 int main(int argc, char *argv[])
571 bool no_signals = false;
572 bool test_config = false;
576 setlocale(LC_ALL, "");
577 bindtextdomain("bacula", LOCALEDIR);
578 textdomain("bacula");
581 my_name_is(argc, argv, "bconsole");
582 init_msg(NULL, NULL);
583 working_directory = "/tmp";
584 args = get_pool_memory(PM_FNAME);
586 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
588 case 'c': /* configuration file */
589 if (configfile != NULL) {
592 configfile = bstrdup(optarg);
596 if (*optarg == 't') {
597 dbg_timestamp = true;
599 debug_level = atoi(optarg);
600 if (debug_level <= 0) {
606 case 'n': /* no conio */
610 case 's': /* turn off signals */
628 init_signals(terminate_console);
632 #if !defined(HAVE_WIN32)
633 /* Override Bacula default signals */
634 signal(SIGQUIT, SIG_IGN);
635 signal(SIGTSTP, got_sigstop);
636 signal(SIGCONT, got_sigcontinue);
637 signal(SIGTTIN, got_sigtin);
638 signal(SIGTTOU, got_sigtout);
649 if (configfile == NULL) {
650 configfile = bstrdup(CONFIG_FILE);
653 config = new_config_parser();
654 parse_cons_config(config, configfile, M_ERROR_TERM);
656 if (init_crypto() != 0) {
657 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
660 if (!check_resources()) {
661 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
669 terminate_console(0);
673 memset(&jcr, 0, sizeof(jcr));
675 (void)WSA_Init(); /* Initialize Windows sockets */
679 foreach_res(dir, R_DIRECTOR) {
683 foreach_res(cons, R_CONSOLE) {
689 struct sockaddr client_addr;
690 memset(&client_addr, 0, sizeof(client_addr));
691 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
693 sendit(_("Available Directors:\n"));
696 foreach_res(dir, R_DIRECTOR) {
697 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
701 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
702 (void)WSACleanup(); /* Cleanup Windows sockets */
705 if (!is_a_number(UA_sock->msg)) {
706 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
707 UA_sock->msg, numdir);
710 item = atoi(UA_sock->msg);
711 if (item < 0 || item > numdir) {
712 senditf(_("You must enter a number between 1 and %d\n"), numdir);
717 for (i=0; i<item; i++) {
718 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
720 /* Look for a console linked to this director */
721 for (i=0; i<numcon; i++) {
722 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
723 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
728 /* Look for the first non-linked console */
730 for (i=0; i<numcon; i++) {
731 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
732 if (cons->director == NULL)
739 /* If no director, take first one */
742 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
745 /* If no console, take first one */
748 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
752 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
755 /* Initialize Console TLS context */
756 if (cons && (cons->tls_enable || cons->tls_require)) {
757 /* Generate passphrase prompt */
758 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
760 /* Initialize TLS context:
761 * Args: CA certfile, CA certdir, Certfile, Keyfile,
762 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
764 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
765 cons->tls_ca_certdir, cons->tls_certfile,
766 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
768 if (!cons->tls_ctx) {
769 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
771 terminate_console(0);
776 /* Initialize Director TLS context */
777 if (dir->tls_enable || dir->tls_require) {
778 /* Generate passphrase prompt */
779 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
781 /* Initialize TLS context:
782 * Args: CA certfile, CA certdir, Certfile, Keyfile,
783 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
784 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
785 dir->tls_ca_certdir, dir->tls_certfile,
786 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
789 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
791 terminate_console(0);
796 if (dir->heartbeat_interval) {
797 heart_beat = dir->heartbeat_interval;
799 heart_beat = cons->heartbeat_interval;
803 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
804 NULL, dir->DIRport, 0);
805 if (UA_sock == NULL) {
806 terminate_console(0);
809 jcr.dir_bsock = UA_sock;
811 /* If cons==NULL, default console will be used */
812 if (!authenticate_director(&jcr, dir, cons)) {
813 terminate_console(0);
817 Dmsg0(40, "Opened connection with Director daemon\n");
819 sendit(_("Enter a period to cancel a command.\n"));
821 /* Read/Update history file if HOME exists */
822 POOL_MEM history_file;
824 /* Run commands in ~/.bconsolerc if any */
825 char *env = getenv("HOME");
828 pm_strcpy(&UA_sock->msg, env);
829 pm_strcat(&UA_sock->msg, "/.bconsolerc");
830 fd = fopen(UA_sock->msg, "rb");
832 read_and_process_input(fd, UA_sock);
836 pm_strcpy(history_file, env);
837 pm_strcat(history_file, "/.bconsole_history");
838 console_init_history(history_file.c_str());
841 read_and_process_input(stdin, UA_sock);
844 UA_sock->signal(BNET_TERMINATE); /* send EOF */
849 console_update_history(history_file.c_str());
852 terminate_console(0);
856 /* Cleanup and then exit */
857 static void terminate_console(int sig)
860 static bool already_here = false;
862 if (already_here) { /* avoid recursive temination problems */
866 config->free_resources();
870 free_pool_memory(args);
874 (void)WSACleanup(); /* Cleanup Windows sockets */
884 * Make a quick check to see that we have all the
887 static int check_resources()
896 foreach_res(director, R_DIRECTOR) {
899 /* tls_require implies tls_enable */
900 if (director->tls_require) {
902 director->tls_enable = true;
904 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
909 tls_needed = director->tls_enable || director->tls_authenticate;
911 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
912 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
913 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
914 " At least one CA certificate store is required.\n"),
915 director->hdr.name, configfile);
921 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
922 "Without that I don't how to speak to the Director :-(\n"), configfile);
927 /* Loop over Consoles */
928 foreach_res(cons, R_CONSOLE) {
929 /* tls_require implies tls_enable */
930 if (cons->tls_require) {
932 cons->tls_enable = true;
934 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
939 tls_needed = cons->tls_enable || cons->tls_authenticate;
940 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
941 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
942 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
943 cons->hdr.name, configfile);
953 static int versioncmd(FILE *input, BSOCK *UA_sock)
955 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
956 HOST_OS, DISTNAME, DISTVER);
960 static int inputcmd(FILE *input, BSOCK *UA_sock)
965 sendit(_("Too many arguments on input command.\n"));
969 sendit(_("First argument to input command must be a filename.\n"));
972 fd = fopen(argk[1], "rb");
975 senditf(_("Cannot open file %s for input. ERR=%s\n"),
976 argk[1], be.bstrerror());
979 read_and_process_input(fd, UA_sock);
984 /* Send output to both termina and specified file */
985 static int teecmd(FILE *input, BSOCK *UA_sock)
988 return do_outputcmd(input, UA_sock);
991 /* Send output to specified "file" */
992 static int outputcmd(FILE *input, BSOCK *UA_sock)
995 return do_outputcmd(input, UA_sock);
999 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1002 const char *mode = "a+b";
1005 sendit(_("Too many arguments on output/tee command.\n"));
1009 if (output != stdout) {
1019 fd = fopen(argk[1], mode);
1022 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1023 argk[1], be.bstrerror(errno));
1031 * exec "some-command" [wait-seconds]
1033 static int execcmd(FILE *input, BSOCK *UA_sock)
1041 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1045 wait = atoi(argk[2]);
1047 bpipe = open_bpipe(argk[1], wait, "r");
1050 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1051 argk[1], be.bstrerror(errno));
1055 while (fgets(line, sizeof(line), bpipe->rfd)) {
1056 senditf("%s", line);
1058 stat = close_bpipe(bpipe);
1062 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1068 static int echocmd(FILE *input, BSOCK *UA_sock)
1070 for (int i=1; i < argc; i++) {
1071 senditf("%s", argk[i]);
1078 static int quitcmd(FILE *input, BSOCK *UA_sock)
1083 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1086 sleep(atoi(argk[1]));
1092 static int timecmd(FILE *input, BSOCK *UA_sock)
1095 time_t ttime = time(NULL);
1097 (void)localtime_r(&ttime, &tm);
1098 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1104 * Send a line to the output file and or the terminal
1106 void senditf(const char *fmt,...)
1111 va_start(arg_ptr, fmt);
1112 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1117 void sendit(const char *buf)
1121 if (output == stdout || teeout) {
1124 * Here, we convert every \n into \r\n because the
1125 * terminal is in raw mode when we are using
1128 for (p=q=buf; (p=strchr(q, '\n')); ) {
1131 memcpy(obuf, q, len);
1133 memcpy(obuf+len, "\r\n", 3);
1134 q = ++p; /* point after \n */
1135 fputs(obuf, output);
1142 if (output != stdout) {