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 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);
107 static int eolcmd(FILE *input, BSOCK *UA_sock);
111 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
117 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
118 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
119 " -c <file> set configuration file to file\n"
120 " -d <nn> set debug level to <nn>\n"
121 " -dt print timestamp in debug output\n"
124 " -t test - read configuration and exit\n"
125 " -? print this message.\n"
126 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
131 void got_sigstop(int sig)
137 void got_sigcontinue(int sig)
143 void got_sigtout(int sig)
145 // printf("Got tout\n");
149 void got_sigtin(int sig)
151 // printf("Got tin\n");
155 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
162 * These are the @command
164 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
165 static struct cmdstruct commands[] = {
166 { N_("input"), inputcmd, _("input from file")},
167 { N_("output"), outputcmd, _("output to file")},
168 { N_("quit"), quitcmd, _("quit")},
169 { N_("tee"), teecmd, _("output to file and terminal")},
170 { N_("sleep"), sleepcmd, _("sleep specified time")},
171 { N_("time"), timecmd, _("print current time")},
172 { N_("version"), versioncmd, _("print Console's version")},
173 { N_("echo"), echocmd, _("echo command string")},
174 { N_("exec"), execcmd, _("execute an external command")},
175 { N_("exit"), quitcmd, _("exit = quit")},
176 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
178 { N_("separator"), eolcmd, _("set command separator")},
181 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
183 static int do_a_command(FILE *input, BSOCK *UA_sock)
194 Dmsg1(120, "Command: %s\n", UA_sock->msg);
200 if (*cmd == '#') { /* comment */
204 for (i=0; i<comsize; i++) { /* search for command */
205 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
206 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
212 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
213 UA_sock->msglen = strlen(UA_sock->msg);
214 sendit(UA_sock->msg);
220 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
222 const char *prompt = "*";
223 bool at_prompt = false;
224 int tty_input = isatty(fileno(input));
228 if (at_prompt) { /* don't prompt multiple times */
235 stat = get_cmd(input, prompt, UA_sock, 30);
243 /* Reading input from a file */
244 int len = sizeof_pool_memory(UA_sock->msg) - 1;
248 if (fgets(UA_sock->msg, len, input) == NULL) {
251 sendit(UA_sock->msg); /* echo to terminal */
252 strip_trailing_junk(UA_sock->msg);
253 UA_sock->msglen = strlen(UA_sock->msg);
258 break; /* error or interrupt */
259 } else if (stat == 0) { /* timeout */
260 if (strcmp(prompt, "*") == 0) {
261 bnet_fsend(UA_sock, ".messages");
267 /* @ => internal command for us */
268 if (UA_sock->msg[0] == '@') {
269 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
270 if (!do_a_command(input, UA_sock)) {
275 if (!bnet_send(UA_sock)) { /* send command */
279 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
282 while ((stat = bnet_recv(UA_sock)) >= 0) {
289 /* Suppress output if running in background or user hit ctl-c */
290 if (!stop && !usrbrk()) {
291 sendit(UA_sock->msg);
302 if (is_bnet_stop(UA_sock)) {
303 break; /* error or term */
304 } else if (stat == BNET_SIGNAL) {
305 if (UA_sock->msglen == BNET_PROMPT) {
308 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
314 * Call-back for reading a passphrase for an encrypted PEM file
315 * This function uses getpass(),
316 * which uses a static buffer and is NOT thread-safe.
318 static int tls_pem_callback(char *buf, int size, const void *userdata)
321 const char *prompt = (const char *)userdata;
322 # if defined(HAVE_WIN32)
324 if (win32_cgets(buf, size) == NULL) {
333 passwd = getpass(prompt);
334 bstrncpy(buf, passwd, size);
344 #define READLINE_LIBRARY 1
346 #include "readline.h"
349 static char eol = ';';
350 static int eolcmd(FILE *input, BSOCK *UA_sock)
352 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
356 sendit(_("Missing or illegal separator character.\n"));
361 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
363 static char *line = NULL;
364 static char *next = NULL;
365 static int do_history = 0;
370 rl_catch_signals = 0; /* do it ourselves */
371 line = readline((char *)prompt); /* cast needed for old readlines */
376 strip_trailing_junk(line);
383 next = strchr(command, eol);
387 if (command != line && isatty(fileno(input))) {
388 senditf("%s%s\n", prompt, command);
391 sock->msglen = pm_strcpy(&sock->msg, command);
406 #else /* no readline, do it ourselves */
408 #if !defined(HAVE_WIN32)
409 static bool bisatty(int fd)
419 * Returns: 1 if data available
424 wait_for_data(int fd, int sec)
426 #if defined(HAVE_WIN32)
436 FD_SET((unsigned)fd, &fdset);
437 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
438 case 0: /* timeout */
441 if (errno == EINTR || errno == EAGAIN) {
444 return -1; /* error return */
453 * Get next input command from terminal.
455 * Returns: 1 if got input
460 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
464 if (output == stdout || teeout) {
469 switch (wait_for_data(fileno(input), sec)) {
471 return 0; /* timeout */
473 return -1; /* error */
475 len = sizeof_pool_memory(sock->msg) - 1;
481 if (bisatty(fileno(input))) {
482 input_line(sock->msg, len);
486 #ifdef HAVE_WIN32 /* use special console for input on win32 */
487 if (input == stdin) {
488 if (win32_cgets(sock->msg, len) == NULL) {
494 if (fgets(sock->msg, len, input) == NULL) {
503 strip_trailing_junk(sock->msg);
504 sock->msglen = strlen(sock->msg);
508 #endif /* ! HAVE_READLINE */
511 static int console_update_history(const char *histfile)
516 /* first, try to truncate the history file, and if it
517 * fail, the file is probably not present, and we
518 * can use write_history to create it
521 if (history_truncate_file(histfile, 100) == 0) {
522 ret = append_history(history_length, histfile);
524 ret = write_history(histfile);
532 static int console_init_history(const char *histfile)
539 ret = read_history(histfile);
546 /*********************************************************************
548 * Main Bacula Console -- User Interface Program
551 int main(int argc, char *argv[])
554 bool no_signals = false;
555 bool test_config = false;
559 setlocale(LC_ALL, "");
560 bindtextdomain("bacula", LOCALEDIR);
561 textdomain("bacula");
564 my_name_is(argc, argv, "bconsole");
565 init_msg(NULL, NULL);
566 working_directory = "/tmp";
567 args = get_pool_memory(PM_FNAME);
569 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
571 case 'c': /* configuration file */
572 if (configfile != NULL) {
575 configfile = bstrdup(optarg);
579 if (*optarg == 't') {
580 dbg_timestamp = true;
582 debug_level = atoi(optarg);
583 if (debug_level <= 0) {
589 case 'n': /* no conio */
593 case 's': /* turn off signals */
611 init_signals(terminate_console);
615 #if !defined(HAVE_WIN32)
616 /* Override Bacula default signals */
617 signal(SIGQUIT, SIG_IGN);
618 signal(SIGTSTP, got_sigstop);
619 signal(SIGCONT, got_sigcontinue);
620 signal(SIGTTIN, got_sigtin);
621 signal(SIGTTOU, got_sigtout);
632 if (configfile == NULL) {
633 configfile = bstrdup(CONFIG_FILE);
636 parse_config(configfile);
638 if (init_crypto() != 0) {
639 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
642 if (!check_resources()) {
643 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
651 terminate_console(0);
655 memset(&jcr, 0, sizeof(jcr));
657 (void)WSA_Init(); /* Initialize Windows sockets */
661 foreach_res(dir, R_DIRECTOR) {
665 foreach_res(cons, R_CONSOLE) {
671 struct sockaddr client_addr;
672 memset(&client_addr, 0, sizeof(client_addr));
673 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
675 sendit(_("Available Directors:\n"));
678 foreach_res(dir, R_DIRECTOR) {
679 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
683 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
684 (void)WSACleanup(); /* Cleanup Windows sockets */
687 if (!is_a_number(UA_sock->msg)) {
688 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
689 UA_sock->msg, numdir);
692 item = atoi(UA_sock->msg);
693 if (item < 0 || item > numdir) {
694 senditf(_("You must enter a number between 1 and %d\n"), numdir);
699 for (i=0; i<item; i++) {
700 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
702 /* Look for a console linked to this director */
703 for (i=0; i<numcon; i++) {
704 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
705 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
710 /* Look for the first non-linked console */
712 for (i=0; i<numcon; i++) {
713 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
714 if (cons->director == NULL)
721 /* If no director, take first one */
724 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
727 /* If no console, take first one */
730 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
734 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
737 /* Initialize Console TLS context */
738 if (cons && (cons->tls_enable || cons->tls_require)) {
739 /* Generate passphrase prompt */
740 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
742 /* Initialize TLS context:
743 * Args: CA certfile, CA certdir, Certfile, Keyfile,
744 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
746 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
747 cons->tls_ca_certdir, cons->tls_certfile,
748 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
750 if (!cons->tls_ctx) {
751 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
753 terminate_console(0);
758 /* Initialize Director TLS context */
759 if (dir->tls_enable || dir->tls_require) {
760 /* Generate passphrase prompt */
761 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
763 /* Initialize TLS context:
764 * Args: CA certfile, CA certdir, Certfile, Keyfile,
765 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
766 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
767 dir->tls_ca_certdir, dir->tls_certfile,
768 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
771 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
773 terminate_console(0);
778 if (dir->heartbeat_interval) {
779 heart_beat = dir->heartbeat_interval;
781 heart_beat = cons->heartbeat_interval;
785 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
786 NULL, dir->DIRport, 0);
787 if (UA_sock == NULL) {
788 terminate_console(0);
791 jcr.dir_bsock = UA_sock;
793 /* If cons==NULL, default console will be used */
794 if (!authenticate_director(&jcr, dir, cons)) {
795 terminate_console(0);
799 Dmsg0(40, "Opened connection with Director daemon\n");
801 sendit(_("Enter a period to cancel a command.\n"));
803 /* Read/Update history file if HOME exists */
804 POOL_MEM history_file;
806 /* Run commands in ~/.bconsolerc if any */
807 char *env = getenv("HOME");
810 pm_strcpy(&UA_sock->msg, env);
811 pm_strcat(&UA_sock->msg, "/.bconsolerc");
812 fd = fopen(UA_sock->msg, "rb");
814 read_and_process_input(fd, UA_sock);
818 pm_strcpy(history_file, env);
819 pm_strcat(history_file, "/.bconsole_history");
820 console_init_history(history_file.c_str());
823 read_and_process_input(stdin, UA_sock);
826 UA_sock->signal(BNET_TERMINATE); /* send EOF */
831 console_update_history(history_file.c_str());
834 terminate_console(0);
838 /* Cleanup and then exit */
839 static void terminate_console(int sig)
842 static bool already_here = false;
844 if (already_here) { /* avoid recursive temination problems */
849 free_pool_memory(args);
853 (void)WSACleanup(); /* Cleanup Windows sockets */
861 * Make a quick check to see that we have all the
864 static int check_resources()
873 foreach_res(director, R_DIRECTOR) {
876 /* tls_require implies tls_enable */
877 if (director->tls_require) {
879 director->tls_enable = true;
881 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
886 tls_needed = director->tls_enable || director->tls_authenticate;
888 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
889 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
890 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
891 " At least one CA certificate store is required.\n"),
892 director->hdr.name, configfile);
898 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
899 "Without that I don't how to speak to the Director :-(\n"), configfile);
904 /* Loop over Consoles */
905 foreach_res(cons, R_CONSOLE) {
906 /* tls_require implies tls_enable */
907 if (cons->tls_require) {
909 cons->tls_enable = true;
911 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
916 tls_needed = cons->tls_enable || cons->tls_authenticate;
917 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
918 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
919 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
920 cons->hdr.name, configfile);
930 static int versioncmd(FILE *input, BSOCK *UA_sock)
932 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
933 HOST_OS, DISTNAME, DISTVER);
937 static int inputcmd(FILE *input, BSOCK *UA_sock)
942 sendit(_("Too many arguments on input command.\n"));
946 sendit(_("First argument to input command must be a filename.\n"));
949 fd = fopen(argk[1], "rb");
952 senditf(_("Cannot open file %s for input. ERR=%s\n"),
953 argk[1], be.bstrerror());
956 read_and_process_input(fd, UA_sock);
961 /* Send output to both termina and specified file */
962 static int teecmd(FILE *input, BSOCK *UA_sock)
965 return do_outputcmd(input, UA_sock);
968 /* Send output to specified "file" */
969 static int outputcmd(FILE *input, BSOCK *UA_sock)
972 return do_outputcmd(input, UA_sock);
976 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
979 const char *mode = "a+b";
982 sendit(_("Too many arguments on output/tee command.\n"));
986 if (output != stdout) {
996 fd = fopen(argk[1], mode);
999 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1000 argk[1], be.bstrerror(errno));
1008 * exec "some-command" [wait-seconds]
1010 static int execcmd(FILE *input, BSOCK *UA_sock)
1018 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1022 wait = atoi(argk[2]);
1024 bpipe = open_bpipe(argk[1], wait, "r");
1027 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1028 argk[1], be.bstrerror(errno));
1032 while (fgets(line, sizeof(line), bpipe->rfd)) {
1033 senditf("%s", line);
1035 stat = close_bpipe(bpipe);
1039 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1045 static int echocmd(FILE *input, BSOCK *UA_sock)
1047 for (int i=1; i < argc; i++) {
1048 senditf("%s", argk[i]);
1055 static int quitcmd(FILE *input, BSOCK *UA_sock)
1060 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1063 sleep(atoi(argk[1]));
1069 static int timecmd(FILE *input, BSOCK *UA_sock)
1072 time_t ttime = time(NULL);
1074 (void)localtime_r(&ttime, &tm);
1075 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1081 * Send a line to the output file and or the terminal
1083 void senditf(const char *fmt,...)
1088 va_start(arg_ptr, fmt);
1089 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1094 void sendit(const char *buf)
1098 if (output == stdout || teeout) {
1101 * Here, we convert every \n into \r\n because the
1102 * terminal is in raw mode when we are using
1105 for (p=q=buf; (p=strchr(q, '\n')); ) {
1108 memcpy(obuf, q, len);
1110 memcpy(obuf+len, "\r\n", 3);
1111 q = ++p; /* point after \n */
1112 fputs(obuf, output);
1119 if (output != stdout) {