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;
349 setlocale(LC_ALL, "");
350 bindtextdomain("bacula", LOCALEDIR);
351 textdomain("bacula");
354 my_name_is(argc, argv, "bconsole");
355 init_msg(NULL, NULL);
356 working_directory = "/tmp";
357 args = get_pool_memory(PM_FNAME);
359 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
361 case 'c': /* configuration file */
362 if (configfile != NULL) {
365 configfile = bstrdup(optarg);
369 debug_level = atoi(optarg);
370 if (debug_level <= 0) {
375 case 'n': /* no conio */
379 case 's': /* turn off signals */
397 init_signals(terminate_console);
401 #if !defined(HAVE_WIN32)
402 /* Override Bacula default signals */
403 signal(SIGQUIT, SIG_IGN);
404 signal(SIGTSTP, got_sigstop);
405 signal(SIGCONT, got_sigcontinue);
406 signal(SIGTTIN, got_sigtin);
407 signal(SIGTTOU, got_sigtout);
418 if (configfile == NULL) {
419 configfile = bstrdup(CONFIG_FILE);
422 parse_config(configfile);
424 if (init_crypto() != 0) {
425 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
428 if (!check_resources()) {
429 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
437 terminate_console(0);
441 memset(&jcr, 0, sizeof(jcr));
443 (void)WSA_Init(); /* Initialize Windows sockets */
447 foreach_res(dir, R_DIRECTOR) {
451 foreach_res(cons, R_CONSOLE) {
457 struct sockaddr client_addr;
458 memset(&client_addr, 0, sizeof(client_addr));
459 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
461 sendit(_("Available Directors:\n"));
464 foreach_res(dir, R_DIRECTOR) {
465 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
469 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
470 (void)WSACleanup(); /* Cleanup Windows sockets */
473 item = atoi(UA_sock->msg);
474 if (item < 0 || item > numdir) {
475 senditf(_("You must enter a number between 1 and %d\n"), numdir);
480 for (i=0; i<item; i++) {
481 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
483 /* Look for a console linked to this director */
484 for (i=0; i<numcon; i++) {
485 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
486 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
491 /* Look for the first non-linked console */
493 for (i=0; i<numcon; i++) {
494 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
495 if (cons->director == NULL)
502 /* If no director, take first one */
505 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
508 /* If no console, take first one */
511 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
515 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
518 /* Initialize Console TLS context */
519 if (cons && (cons->tls_enable || cons->tls_require)) {
520 /* Generate passphrase prompt */
521 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
523 /* Initialize TLS context:
524 * Args: CA certfile, CA certdir, Certfile, Keyfile,
525 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
527 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
528 cons->tls_ca_certdir, cons->tls_certfile,
529 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
531 if (!cons->tls_ctx) {
532 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
534 terminate_console(0);
539 /* Initialize Director TLS context */
540 if (dir->tls_enable || dir->tls_require) {
541 /* Generate passphrase prompt */
542 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
544 /* Initialize TLS context:
545 * Args: CA certfile, CA certdir, Certfile, Keyfile,
546 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
547 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
548 dir->tls_ca_certdir, dir->tls_certfile,
549 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
552 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
554 terminate_console(0);
559 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
560 NULL, dir->DIRport, 0);
561 if (UA_sock == NULL) {
562 terminate_console(0);
565 jcr.dir_bsock = UA_sock;
567 /* If cons==NULL, default console will be used */
568 if (!authenticate_director(&jcr, dir, cons)) {
569 terminate_console(0);
573 Dmsg0(40, "Opened connection with Director daemon\n");
575 sendit(_("Enter a period to cancel a command.\n"));
577 /* Run commands in ~/.bconsolerc if any */
578 char *env = getenv("HOME");
581 pm_strcpy(&UA_sock->msg, env);
582 pm_strcat(&UA_sock->msg, "/.bconsolerc");
583 fd = fopen(UA_sock->msg, "rb");
585 read_and_process_input(fd, UA_sock);
590 read_and_process_input(stdin, UA_sock);
593 UA_sock->signal(BNET_TERMINATE); /* send EOF */
597 terminate_console(0);
602 /* Cleanup and then exit */
603 static void terminate_console(int sig)
606 static bool already_here = false;
608 if (already_here) { /* avoid recursive temination problems */
613 free_pool_memory(args);
617 (void)WSACleanup(); /* Cleanup Windows sockets */
625 * Make a quick check to see that we have all the
628 static int check_resources()
636 foreach_res(director, R_DIRECTOR) {
639 /* tls_require implies tls_enable */
640 if (director->tls_require) {
642 director->tls_enable = true;
644 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
650 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
651 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
652 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
653 " At least one CA certificate store is required.\n"),
654 director->hdr.name, configfile);
660 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
661 "Without that I don't how to speak to the Director :-(\n"), configfile);
666 /* Loop over Consoles */
667 foreach_res(cons, R_CONSOLE) {
668 /* tls_require implies tls_enable */
669 if (cons->tls_require) {
671 cons->tls_enable = true;
673 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
679 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
680 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
681 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
682 cons->hdr.name, configfile);
694 #define READLINE_LIBRARY 1
696 #include "readline.h"
701 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
705 rl_catch_signals = 0; /* do it ourselves */
706 line = readline((char *)prompt); /* cast needed for old readlines */
711 strip_trailing_junk(line);
712 sock->msglen = pm_strcpy(&sock->msg, line);
714 add_history(sock->msg);
720 #else /* no readline, do it ourselves */
722 #if !defined(HAVE_WIN32)
723 static bool bisatty(int fd)
733 * Returns: 1 if data available
738 wait_for_data(int fd, int sec)
740 #if defined(HAVE_WIN32)
750 FD_SET((unsigned)fd, &fdset);
751 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
752 case 0: /* timeout */
755 if (errno == EINTR || errno == EAGAIN) {
758 return -1; /* error return */
767 * Get next input command from terminal.
769 * Returns: 1 if got input
774 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
778 if (output == stdout || teeout) {
783 switch (wait_for_data(fileno(input), sec)) {
785 return 0; /* timeout */
787 return -1; /* error */
789 len = sizeof_pool_memory(sock->msg) - 1;
795 if (bisatty(fileno(input))) {
796 input_line(sock->msg, len);
800 #ifdef HAVE_WIN32 /* use special console for input on win32 */
801 if (input == stdin) {
802 if (win32_cgets(sock->msg, len) == NULL) {
808 if (fgets(sock->msg, len, input) == NULL) {
817 strip_trailing_junk(sock->msg);
818 sock->msglen = strlen(sock->msg);
822 #endif /* end non-readline code */
824 static int versioncmd(FILE *input, BSOCK *UA_sock)
826 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
827 HOST_OS, DISTNAME, DISTVER);
831 static int inputcmd(FILE *input, BSOCK *UA_sock)
836 sendit(_("Too many arguments on input command.\n"));
840 sendit(_("First argument to input command must be a filename.\n"));
843 fd = fopen(argk[1], "rb");
845 senditf(_("Cannot open file %s for input. ERR=%s\n"),
846 argk[1], strerror(errno));
849 read_and_process_input(fd, UA_sock);
854 /* Send output to both termina and specified file */
855 static int teecmd(FILE *input, BSOCK *UA_sock)
858 return do_outputcmd(input, UA_sock);
861 /* Send output to specified "file" */
862 static int outputcmd(FILE *input, BSOCK *UA_sock)
865 return do_outputcmd(input, UA_sock);
869 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
872 const char *mode = "a+b";
875 sendit(_("Too many arguments on output/tee command.\n"));
879 if (output != stdout) {
889 fd = fopen(argk[1], mode);
892 senditf(_("Cannot open file %s for output. ERR=%s\n"),
893 argk[1], be.strerror(errno));
901 * exec "some-command" [wait-seconds]
903 static int execcmd(FILE *input, BSOCK *UA_sock)
911 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
915 wait = atoi(argk[2]);
917 bpipe = open_bpipe(argk[1], wait, "r");
920 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
921 argk[1], be.strerror(errno));
925 while (fgets(line, sizeof(line), bpipe->rfd)) {
928 stat = close_bpipe(bpipe);
932 senditf(_("Autochanger error: ERR=%s\n"), be.strerror());
938 static int echocmd(FILE *input, BSOCK *UA_sock)
940 for (int i=1; i < argc; i++) {
941 senditf("%s", argk[i]);
948 static int quitcmd(FILE *input, BSOCK *UA_sock)
953 static int sleepcmd(FILE *input, BSOCK *UA_sock)
956 sleep(atoi(argk[1]));
962 static int timecmd(FILE *input, BSOCK *UA_sock)
965 time_t ttime = time(NULL);
967 (void)localtime_r(&ttime, &tm);
968 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
974 * Send a line to the output file and or the terminal
976 void senditf(const char *fmt,...)
981 va_start(arg_ptr, fmt);
982 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
987 void sendit(const char *buf)
991 if (output == stdout || teeout) {
994 * Here, we convert every \n into \r\n because the
995 * terminal is in raw mode when we are using
998 for (p=q=buf; (p=strchr(q, '\n')); ) {
1001 memcpy(obuf, q, len);
1003 memcpy(obuf+len, "\r\n", 3);
1004 q = ++p; /* point after \n */
1005 fputs(obuf, output);
1012 if (output != stdout) {