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 " -dnn set debug level to nn\n"
120 " -t test - read configuration and exit\n"
121 " -? print this message.\n"
122 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
127 void got_sigstop(int sig)
133 void got_sigcontinue(int sig)
139 void got_sigtout(int sig)
141 // printf("Got tout\n");
145 void got_sigtin(int sig)
147 // printf("Got tin\n");
151 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
158 * These are the @command
160 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
161 static struct cmdstruct commands[] = {
162 { N_("input"), inputcmd, _("input from file")},
163 { N_("output"), outputcmd, _("output to file")},
164 { N_("quit"), quitcmd, _("quit")},
165 { N_("tee"), teecmd, _("output to file and terminal")},
166 { N_("sleep"), sleepcmd, _("sleep specified time")},
167 { N_("time"), timecmd, _("print current time")},
168 { N_("version"), versioncmd, _("print Console's version")},
169 { N_("echo"), echocmd, _("echo command string")},
170 { N_("exec"), execcmd, _("execute an external command")},
171 { N_("exit"), quitcmd, _("exit = quit")},
172 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
174 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
176 static int do_a_command(FILE *input, BSOCK *UA_sock)
187 Dmsg1(120, "Command: %s\n", UA_sock->msg);
193 if (*cmd == '#') { /* comment */
197 for (i=0; i<comsize; i++) { /* search for command */
198 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
199 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
205 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
206 UA_sock->msglen = strlen(UA_sock->msg);
207 sendit(UA_sock->msg);
213 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
215 const char *prompt = "*";
216 bool at_prompt = false;
217 int tty_input = isatty(fileno(input));
221 if (at_prompt) { /* don't prompt multiple times */
228 stat = get_cmd(input, prompt, UA_sock, 30);
236 /* Reading input from a file */
237 int len = sizeof_pool_memory(UA_sock->msg) - 1;
241 if (fgets(UA_sock->msg, len, input) == NULL) {
244 sendit(UA_sock->msg); /* echo to terminal */
245 strip_trailing_junk(UA_sock->msg);
246 UA_sock->msglen = strlen(UA_sock->msg);
251 break; /* error or interrupt */
252 } else if (stat == 0) { /* timeout */
253 if (strcmp(prompt, "*") == 0) {
254 bnet_fsend(UA_sock, ".messages");
260 /* @ => internal command for us */
261 if (UA_sock->msg[0] == '@') {
262 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
263 if (!do_a_command(input, UA_sock)) {
268 if (!bnet_send(UA_sock)) { /* send command */
272 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
275 while ((stat = bnet_recv(UA_sock)) >= 0) {
282 /* Suppress output if running in background or user hit ctl-c */
283 if (!stop && !usrbrk()) {
284 sendit(UA_sock->msg);
295 if (is_bnet_stop(UA_sock)) {
296 break; /* error or term */
297 } else if (stat == BNET_SIGNAL) {
298 if (UA_sock->msglen == BNET_PROMPT) {
301 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
307 * Call-back for reading a passphrase for an encrypted PEM file
308 * This function uses getpass(),
309 * which uses a static buffer and is NOT thread-safe.
311 static int tls_pem_callback(char *buf, int size, const void *userdata)
314 const char *prompt = (const char *)userdata;
315 # if defined(HAVE_WIN32)
317 if (win32_cgets(buf, size) == NULL) {
326 passwd = getpass(prompt);
327 bstrncpy(buf, passwd, size);
337 #define READLINE_LIBRARY 1
339 #include "readline.h"
343 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
347 rl_catch_signals = 0; /* do it ourselves */
348 line = readline((char *)prompt); /* cast needed for old readlines */
353 strip_trailing_junk(line);
354 sock->msglen = pm_strcpy(&sock->msg, line);
356 add_history(sock->msg);
362 #else /* no readline, do it ourselves */
364 #if !defined(HAVE_WIN32)
365 static bool bisatty(int fd)
375 * Returns: 1 if data available
380 wait_for_data(int fd, int sec)
382 #if defined(HAVE_WIN32)
392 FD_SET((unsigned)fd, &fdset);
393 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
394 case 0: /* timeout */
397 if (errno == EINTR || errno == EAGAIN) {
400 return -1; /* error return */
409 * Get next input command from terminal.
411 * Returns: 1 if got input
416 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
420 if (output == stdout || teeout) {
425 switch (wait_for_data(fileno(input), sec)) {
427 return 0; /* timeout */
429 return -1; /* error */
431 len = sizeof_pool_memory(sock->msg) - 1;
437 if (bisatty(fileno(input))) {
438 input_line(sock->msg, len);
442 #ifdef HAVE_WIN32 /* use special console for input on win32 */
443 if (input == stdin) {
444 if (win32_cgets(sock->msg, len) == NULL) {
450 if (fgets(sock->msg, len, input) == NULL) {
459 strip_trailing_junk(sock->msg);
460 sock->msglen = strlen(sock->msg);
464 #endif /* ! HAVE_READLINE */
467 static int console_update_history(const char *histfile)
472 /* first, try to truncate the history file, and if it
473 * fail, the file is probably not present, and we
474 * can use write_history to create it
477 if (history_truncate_file(histfile, 100) == 0) {
478 ret = append_history(history_length, histfile);
480 ret = write_history(histfile);
488 static int console_init_history(const char *histfile)
495 ret = read_history(histfile);
502 /*********************************************************************
504 * Main Bacula Console -- User Interface Program
507 int main(int argc, char *argv[])
510 bool no_signals = false;
511 bool test_config = false;
515 setlocale(LC_ALL, "");
516 bindtextdomain("bacula", LOCALEDIR);
517 textdomain("bacula");
520 my_name_is(argc, argv, "bconsole");
521 init_msg(NULL, NULL);
522 working_directory = "/tmp";
523 args = get_pool_memory(PM_FNAME);
525 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
527 case 'c': /* configuration file */
528 if (configfile != NULL) {
531 configfile = bstrdup(optarg);
535 debug_level = atoi(optarg);
536 if (debug_level <= 0) {
541 case 'n': /* no conio */
545 case 's': /* turn off signals */
563 init_signals(terminate_console);
567 #if !defined(HAVE_WIN32)
568 /* Override Bacula default signals */
569 signal(SIGQUIT, SIG_IGN);
570 signal(SIGTSTP, got_sigstop);
571 signal(SIGCONT, got_sigcontinue);
572 signal(SIGTTIN, got_sigtin);
573 signal(SIGTTOU, got_sigtout);
584 if (configfile == NULL) {
585 configfile = bstrdup(CONFIG_FILE);
588 parse_config(configfile);
590 if (init_crypto() != 0) {
591 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
594 if (!check_resources()) {
595 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
603 terminate_console(0);
607 memset(&jcr, 0, sizeof(jcr));
609 (void)WSA_Init(); /* Initialize Windows sockets */
613 foreach_res(dir, R_DIRECTOR) {
617 foreach_res(cons, R_CONSOLE) {
623 struct sockaddr client_addr;
624 memset(&client_addr, 0, sizeof(client_addr));
625 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
627 sendit(_("Available Directors:\n"));
630 foreach_res(dir, R_DIRECTOR) {
631 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
635 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
636 (void)WSACleanup(); /* Cleanup Windows sockets */
639 if (!is_a_number(UA_sock->msg)) {
640 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
641 UA_sock->msg, numdir);
644 item = atoi(UA_sock->msg);
645 if (item < 0 || item > numdir) {
646 senditf(_("You must enter a number between 1 and %d\n"), numdir);
651 for (i=0; i<item; i++) {
652 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
654 /* Look for a console linked to this director */
655 for (i=0; i<numcon; i++) {
656 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
657 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
662 /* Look for the first non-linked console */
664 for (i=0; i<numcon; i++) {
665 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
666 if (cons->director == NULL)
673 /* If no director, take first one */
676 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
679 /* If no console, take first one */
682 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
686 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
689 /* Initialize Console TLS context */
690 if (cons && (cons->tls_enable || cons->tls_require)) {
691 /* Generate passphrase prompt */
692 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
694 /* Initialize TLS context:
695 * Args: CA certfile, CA certdir, Certfile, Keyfile,
696 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
698 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
699 cons->tls_ca_certdir, cons->tls_certfile,
700 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
702 if (!cons->tls_ctx) {
703 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
705 terminate_console(0);
710 /* Initialize Director TLS context */
711 if (dir->tls_enable || dir->tls_require) {
712 /* Generate passphrase prompt */
713 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
715 /* Initialize TLS context:
716 * Args: CA certfile, CA certdir, Certfile, Keyfile,
717 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
718 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
719 dir->tls_ca_certdir, dir->tls_certfile,
720 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
723 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
725 terminate_console(0);
730 if (dir->heartbeat_interval) {
731 heart_beat = dir->heartbeat_interval;
733 heart_beat = cons->heartbeat_interval;
737 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
738 NULL, dir->DIRport, 0);
739 if (UA_sock == NULL) {
740 terminate_console(0);
743 jcr.dir_bsock = UA_sock;
745 /* If cons==NULL, default console will be used */
746 if (!authenticate_director(&jcr, dir, cons)) {
747 terminate_console(0);
751 Dmsg0(40, "Opened connection with Director daemon\n");
753 sendit(_("Enter a period to cancel a command.\n"));
755 /* Read/Update history file if HOME exists */
756 POOL_MEM history_file;
758 /* Run commands in ~/.bconsolerc if any */
759 char *env = getenv("HOME");
762 pm_strcpy(&UA_sock->msg, env);
763 pm_strcat(&UA_sock->msg, "/.bconsolerc");
764 fd = fopen(UA_sock->msg, "rb");
766 read_and_process_input(fd, UA_sock);
770 pm_strcpy(history_file, env);
771 pm_strcat(history_file, "/.bconsole_history");
772 console_init_history(history_file.c_str());
775 read_and_process_input(stdin, UA_sock);
778 UA_sock->signal(BNET_TERMINATE); /* send EOF */
783 console_update_history(history_file.c_str());
786 terminate_console(0);
790 /* Cleanup and then exit */
791 static void terminate_console(int sig)
794 static bool already_here = false;
796 if (already_here) { /* avoid recursive temination problems */
801 free_pool_memory(args);
805 (void)WSACleanup(); /* Cleanup Windows sockets */
813 * Make a quick check to see that we have all the
816 static int check_resources()
824 foreach_res(director, R_DIRECTOR) {
827 /* tls_require implies tls_enable */
828 if (director->tls_require) {
830 director->tls_enable = true;
832 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
838 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
839 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
840 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
841 " At least one CA certificate store is required.\n"),
842 director->hdr.name, configfile);
848 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
849 "Without that I don't how to speak to the Director :-(\n"), configfile);
854 /* Loop over Consoles */
855 foreach_res(cons, R_CONSOLE) {
856 /* tls_require implies tls_enable */
857 if (cons->tls_require) {
859 cons->tls_enable = true;
861 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
867 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
868 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
869 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
870 cons->hdr.name, configfile);
880 static int versioncmd(FILE *input, BSOCK *UA_sock)
882 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
883 HOST_OS, DISTNAME, DISTVER);
887 static int inputcmd(FILE *input, BSOCK *UA_sock)
892 sendit(_("Too many arguments on input command.\n"));
896 sendit(_("First argument to input command must be a filename.\n"));
899 fd = fopen(argk[1], "rb");
901 senditf(_("Cannot open file %s for input. ERR=%s\n"),
902 argk[1], strerror(errno));
905 read_and_process_input(fd, UA_sock);
910 /* Send output to both termina and specified file */
911 static int teecmd(FILE *input, BSOCK *UA_sock)
914 return do_outputcmd(input, UA_sock);
917 /* Send output to specified "file" */
918 static int outputcmd(FILE *input, BSOCK *UA_sock)
921 return do_outputcmd(input, UA_sock);
925 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
928 const char *mode = "a+b";
931 sendit(_("Too many arguments on output/tee command.\n"));
935 if (output != stdout) {
945 fd = fopen(argk[1], mode);
948 senditf(_("Cannot open file %s for output. ERR=%s\n"),
949 argk[1], be.bstrerror(errno));
957 * exec "some-command" [wait-seconds]
959 static int execcmd(FILE *input, BSOCK *UA_sock)
967 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
971 wait = atoi(argk[2]);
973 bpipe = open_bpipe(argk[1], wait, "r");
976 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
977 argk[1], be.bstrerror(errno));
981 while (fgets(line, sizeof(line), bpipe->rfd)) {
984 stat = close_bpipe(bpipe);
988 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
994 static int echocmd(FILE *input, BSOCK *UA_sock)
996 for (int i=1; i < argc; i++) {
997 senditf("%s", argk[i]);
1004 static int quitcmd(FILE *input, BSOCK *UA_sock)
1009 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1012 sleep(atoi(argk[1]));
1018 static int timecmd(FILE *input, BSOCK *UA_sock)
1021 time_t ttime = time(NULL);
1023 (void)localtime_r(&ttime, &tm);
1024 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1030 * Send a line to the output file and or the terminal
1032 void senditf(const char *fmt,...)
1037 va_start(arg_ptr, fmt);
1038 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1043 void sendit(const char *buf)
1047 if (output == stdout || teeout) {
1050 * Here, we convert every \n into \r\n because the
1051 * terminal is in raw mode when we are using
1054 for (p=q=buf; (p=strchr(q, '\n')); ) {
1057 memcpy(obuf, q, len);
1059 memcpy(obuf+len, "\r\n", 3);
1060 q = ++p; /* point after \n */
1061 fputs(obuf, output);
1068 if (output != stdout) {