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 plus additions
11 that are listed in the file LICENSE.
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 /*********************************************************************
339 * Main Bacula Console -- User Interface Program
342 int main(int argc, char *argv[])
345 bool no_signals = false;
346 bool test_config = false;
350 setlocale(LC_ALL, "");
351 bindtextdomain("bacula", LOCALEDIR);
352 textdomain("bacula");
355 my_name_is(argc, argv, "bconsole");
356 init_msg(NULL, NULL);
357 working_directory = "/tmp";
358 args = get_pool_memory(PM_FNAME);
360 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
362 case 'c': /* configuration file */
363 if (configfile != NULL) {
366 configfile = bstrdup(optarg);
370 debug_level = atoi(optarg);
371 if (debug_level <= 0) {
376 case 'n': /* no conio */
380 case 's': /* turn off signals */
398 init_signals(terminate_console);
402 #if !defined(HAVE_WIN32)
403 /* Override Bacula default signals */
404 signal(SIGQUIT, SIG_IGN);
405 signal(SIGTSTP, got_sigstop);
406 signal(SIGCONT, got_sigcontinue);
407 signal(SIGTTIN, got_sigtin);
408 signal(SIGTTOU, got_sigtout);
419 if (configfile == NULL) {
420 configfile = bstrdup(CONFIG_FILE);
423 parse_config(configfile);
425 if (init_crypto() != 0) {
426 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
429 if (!check_resources()) {
430 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
438 terminate_console(0);
442 memset(&jcr, 0, sizeof(jcr));
444 (void)WSA_Init(); /* Initialize Windows sockets */
448 foreach_res(dir, R_DIRECTOR) {
452 foreach_res(cons, R_CONSOLE) {
458 struct sockaddr client_addr;
459 memset(&client_addr, 0, sizeof(client_addr));
460 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
462 sendit(_("Available Directors:\n"));
465 foreach_res(dir, R_DIRECTOR) {
466 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
470 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
471 (void)WSACleanup(); /* Cleanup Windows sockets */
474 item = atoi(UA_sock->msg);
475 if (item < 0 || item > numdir) {
476 senditf(_("You must enter a number between 1 and %d\n"), numdir);
481 for (i=0; i<item; i++) {
482 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
484 /* Look for a console linked to this director */
485 for (i=0; i<numcon; i++) {
486 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
487 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
492 /* Look for the first non-linked console */
494 for (i=0; i<numcon; i++) {
495 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
496 if (cons->director == NULL)
503 /* If no director, take first one */
506 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
509 /* If no console, take first one */
512 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
516 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
519 /* Initialize Console TLS context */
520 if (cons && (cons->tls_enable || cons->tls_require)) {
521 /* Generate passphrase prompt */
522 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
524 /* Initialize TLS context:
525 * Args: CA certfile, CA certdir, Certfile, Keyfile,
526 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
528 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
529 cons->tls_ca_certdir, cons->tls_certfile,
530 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
532 if (!cons->tls_ctx) {
533 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
535 terminate_console(0);
540 /* Initialize Director TLS context */
541 if (dir->tls_enable || dir->tls_require) {
542 /* Generate passphrase prompt */
543 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
545 /* Initialize TLS context:
546 * Args: CA certfile, CA certdir, Certfile, Keyfile,
547 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
548 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
549 dir->tls_ca_certdir, dir->tls_certfile,
550 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
553 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
555 terminate_console(0);
560 if (dir->heartbeat_interval) {
561 heart_beat = dir->heartbeat_interval;
563 heart_beat = cons->heartbeat_interval;
567 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
568 NULL, dir->DIRport, 0);
569 if (UA_sock == NULL) {
570 terminate_console(0);
573 jcr.dir_bsock = UA_sock;
575 /* If cons==NULL, default console will be used */
576 if (!authenticate_director(&jcr, dir, cons)) {
577 terminate_console(0);
581 Dmsg0(40, "Opened connection with Director daemon\n");
583 sendit(_("Enter a period to cancel a command.\n"));
585 /* Run commands in ~/.bconsolerc if any */
586 char *env = getenv("HOME");
589 pm_strcpy(&UA_sock->msg, env);
590 pm_strcat(&UA_sock->msg, "/.bconsolerc");
591 fd = fopen(UA_sock->msg, "rb");
593 read_and_process_input(fd, UA_sock);
598 read_and_process_input(stdin, UA_sock);
601 UA_sock->signal(BNET_TERMINATE); /* send EOF */
605 terminate_console(0);
610 /* Cleanup and then exit */
611 static void terminate_console(int sig)
614 static bool already_here = false;
616 if (already_here) { /* avoid recursive temination problems */
621 free_pool_memory(args);
625 (void)WSACleanup(); /* Cleanup Windows sockets */
633 * Make a quick check to see that we have all the
636 static int check_resources()
644 foreach_res(director, R_DIRECTOR) {
647 /* tls_require implies tls_enable */
648 if (director->tls_require) {
650 director->tls_enable = true;
652 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
658 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
659 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
660 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
661 " At least one CA certificate store is required.\n"),
662 director->hdr.name, configfile);
668 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
669 "Without that I don't how to speak to the Director :-(\n"), configfile);
674 /* Loop over Consoles */
675 foreach_res(cons, R_CONSOLE) {
676 /* tls_require implies tls_enable */
677 if (cons->tls_require) {
679 cons->tls_enable = true;
681 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
687 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
688 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
689 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
690 cons->hdr.name, configfile);
702 #define READLINE_LIBRARY 1
704 #include "readline.h"
709 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
713 rl_catch_signals = 0; /* do it ourselves */
714 line = readline((char *)prompt); /* cast needed for old readlines */
719 strip_trailing_junk(line);
720 sock->msglen = pm_strcpy(&sock->msg, line);
722 add_history(sock->msg);
728 #else /* no readline, do it ourselves */
730 #if !defined(HAVE_WIN32)
731 static bool bisatty(int fd)
741 * Returns: 1 if data available
746 wait_for_data(int fd, int sec)
748 #if defined(HAVE_WIN32)
758 FD_SET((unsigned)fd, &fdset);
759 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
760 case 0: /* timeout */
763 if (errno == EINTR || errno == EAGAIN) {
766 return -1; /* error return */
775 * Get next input command from terminal.
777 * Returns: 1 if got input
782 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
786 if (output == stdout || teeout) {
791 switch (wait_for_data(fileno(input), sec)) {
793 return 0; /* timeout */
795 return -1; /* error */
797 len = sizeof_pool_memory(sock->msg) - 1;
803 if (bisatty(fileno(input))) {
804 input_line(sock->msg, len);
808 #ifdef HAVE_WIN32 /* use special console for input on win32 */
809 if (input == stdin) {
810 if (win32_cgets(sock->msg, len) == NULL) {
816 if (fgets(sock->msg, len, input) == NULL) {
825 strip_trailing_junk(sock->msg);
826 sock->msglen = strlen(sock->msg);
830 #endif /* end non-readline code */
832 static int versioncmd(FILE *input, BSOCK *UA_sock)
834 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
835 HOST_OS, DISTNAME, DISTVER);
839 static int inputcmd(FILE *input, BSOCK *UA_sock)
844 sendit(_("Too many arguments on input command.\n"));
848 sendit(_("First argument to input command must be a filename.\n"));
851 fd = fopen(argk[1], "rb");
853 senditf(_("Cannot open file %s for input. ERR=%s\n"),
854 argk[1], strerror(errno));
857 read_and_process_input(fd, UA_sock);
862 /* Send output to both termina and specified file */
863 static int teecmd(FILE *input, BSOCK *UA_sock)
866 return do_outputcmd(input, UA_sock);
869 /* Send output to specified "file" */
870 static int outputcmd(FILE *input, BSOCK *UA_sock)
873 return do_outputcmd(input, UA_sock);
877 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
880 const char *mode = "a+b";
883 sendit(_("Too many arguments on output/tee command.\n"));
887 if (output != stdout) {
897 fd = fopen(argk[1], mode);
900 senditf(_("Cannot open file %s for output. ERR=%s\n"),
901 argk[1], be.bstrerror(errno));
909 * exec "some-command" [wait-seconds]
911 static int execcmd(FILE *input, BSOCK *UA_sock)
919 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
923 wait = atoi(argk[2]);
925 bpipe = open_bpipe(argk[1], wait, "r");
928 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
929 argk[1], be.bstrerror(errno));
933 while (fgets(line, sizeof(line), bpipe->rfd)) {
936 stat = close_bpipe(bpipe);
940 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
946 static int echocmd(FILE *input, BSOCK *UA_sock)
948 for (int i=1; i < argc; i++) {
949 senditf("%s", argk[i]);
956 static int quitcmd(FILE *input, BSOCK *UA_sock)
961 static int sleepcmd(FILE *input, BSOCK *UA_sock)
964 sleep(atoi(argk[1]));
970 static int timecmd(FILE *input, BSOCK *UA_sock)
973 time_t ttime = time(NULL);
975 (void)localtime_r(&ttime, &tm);
976 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
982 * Send a line to the output file and or the terminal
984 void senditf(const char *fmt,...)
989 va_start(arg_ptr, fmt);
990 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
995 void sendit(const char *buf)
999 if (output == stdout || teeout) {
1002 * Here, we convert every \n into \r\n because the
1003 * terminal is in raw mode when we are using
1006 for (p=q=buf; (p=strchr(q, '\n')); ) {
1009 memcpy(obuf, q, len);
1011 memcpy(obuf+len, "\r\n", 3);
1012 q = ++p; /* point after \n */
1013 fputs(obuf, output);
1020 if (output != stdout) {