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 /*********************************************************************
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( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
470 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
471 (void)WSACleanup(); /* Cleanup Windows sockets */
474 if (!is_a_number(UA_sock->msg)) {
475 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
476 UA_sock->msg, numdir);
479 item = atoi(UA_sock->msg);
480 if (item < 0 || item > numdir) {
481 senditf(_("You must enter a number between 1 and %d\n"), numdir);
486 for (i=0; i<item; i++) {
487 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
489 /* Look for a console linked to this director */
490 for (i=0; i<numcon; i++) {
491 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
492 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
497 /* Look for the first non-linked console */
499 for (i=0; i<numcon; i++) {
500 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
501 if (cons->director == NULL)
508 /* If no director, take first one */
511 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
514 /* If no console, take first one */
517 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
521 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
524 /* Initialize Console TLS context */
525 if (cons && (cons->tls_enable || cons->tls_require)) {
526 /* Generate passphrase prompt */
527 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
529 /* Initialize TLS context:
530 * Args: CA certfile, CA certdir, Certfile, Keyfile,
531 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
533 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
534 cons->tls_ca_certdir, cons->tls_certfile,
535 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
537 if (!cons->tls_ctx) {
538 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
540 terminate_console(0);
545 /* Initialize Director TLS context */
546 if (dir->tls_enable || dir->tls_require) {
547 /* Generate passphrase prompt */
548 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
550 /* Initialize TLS context:
551 * Args: CA certfile, CA certdir, Certfile, Keyfile,
552 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
553 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
554 dir->tls_ca_certdir, dir->tls_certfile,
555 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
558 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
560 terminate_console(0);
565 if (dir->heartbeat_interval) {
566 heart_beat = dir->heartbeat_interval;
568 heart_beat = cons->heartbeat_interval;
572 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
573 NULL, dir->DIRport, 0);
574 if (UA_sock == NULL) {
575 terminate_console(0);
578 jcr.dir_bsock = UA_sock;
580 /* If cons==NULL, default console will be used */
581 if (!authenticate_director(&jcr, dir, cons)) {
582 terminate_console(0);
586 Dmsg0(40, "Opened connection with Director daemon\n");
588 sendit(_("Enter a period to cancel a command.\n"));
590 /* Run commands in ~/.bconsolerc if any */
591 char *env = getenv("HOME");
594 pm_strcpy(&UA_sock->msg, env);
595 pm_strcat(&UA_sock->msg, "/.bconsolerc");
596 fd = fopen(UA_sock->msg, "rb");
598 read_and_process_input(fd, UA_sock);
603 read_and_process_input(stdin, UA_sock);
606 UA_sock->signal(BNET_TERMINATE); /* send EOF */
610 terminate_console(0);
615 /* Cleanup and then exit */
616 static void terminate_console(int sig)
619 static bool already_here = false;
621 if (already_here) { /* avoid recursive temination problems */
626 free_pool_memory(args);
630 (void)WSACleanup(); /* Cleanup Windows sockets */
638 * Make a quick check to see that we have all the
641 static int check_resources()
649 foreach_res(director, R_DIRECTOR) {
652 /* tls_require implies tls_enable */
653 if (director->tls_require) {
655 director->tls_enable = true;
657 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
663 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
664 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
665 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
666 " At least one CA certificate store is required.\n"),
667 director->hdr.name, configfile);
673 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
674 "Without that I don't how to speak to the Director :-(\n"), configfile);
679 /* Loop over Consoles */
680 foreach_res(cons, R_CONSOLE) {
681 /* tls_require implies tls_enable */
682 if (cons->tls_require) {
684 cons->tls_enable = true;
686 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
692 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
693 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
694 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
695 cons->hdr.name, configfile);
707 #define READLINE_LIBRARY 1
709 #include "readline.h"
714 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
718 rl_catch_signals = 0; /* do it ourselves */
719 line = readline((char *)prompt); /* cast needed for old readlines */
724 strip_trailing_junk(line);
725 sock->msglen = pm_strcpy(&sock->msg, line);
727 add_history(sock->msg);
733 #else /* no readline, do it ourselves */
735 #if !defined(HAVE_WIN32)
736 static bool bisatty(int fd)
746 * Returns: 1 if data available
751 wait_for_data(int fd, int sec)
753 #if defined(HAVE_WIN32)
763 FD_SET((unsigned)fd, &fdset);
764 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
765 case 0: /* timeout */
768 if (errno == EINTR || errno == EAGAIN) {
771 return -1; /* error return */
780 * Get next input command from terminal.
782 * Returns: 1 if got input
787 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
791 if (output == stdout || teeout) {
796 switch (wait_for_data(fileno(input), sec)) {
798 return 0; /* timeout */
800 return -1; /* error */
802 len = sizeof_pool_memory(sock->msg) - 1;
808 if (bisatty(fileno(input))) {
809 input_line(sock->msg, len);
813 #ifdef HAVE_WIN32 /* use special console for input on win32 */
814 if (input == stdin) {
815 if (win32_cgets(sock->msg, len) == NULL) {
821 if (fgets(sock->msg, len, input) == NULL) {
830 strip_trailing_junk(sock->msg);
831 sock->msglen = strlen(sock->msg);
835 #endif /* end non-readline code */
837 static int versioncmd(FILE *input, BSOCK *UA_sock)
839 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
840 HOST_OS, DISTNAME, DISTVER);
844 static int inputcmd(FILE *input, BSOCK *UA_sock)
849 sendit(_("Too many arguments on input command.\n"));
853 sendit(_("First argument to input command must be a filename.\n"));
856 fd = fopen(argk[1], "rb");
858 senditf(_("Cannot open file %s for input. ERR=%s\n"),
859 argk[1], strerror(errno));
862 read_and_process_input(fd, UA_sock);
867 /* Send output to both termina and specified file */
868 static int teecmd(FILE *input, BSOCK *UA_sock)
871 return do_outputcmd(input, UA_sock);
874 /* Send output to specified "file" */
875 static int outputcmd(FILE *input, BSOCK *UA_sock)
878 return do_outputcmd(input, UA_sock);
882 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
885 const char *mode = "a+b";
888 sendit(_("Too many arguments on output/tee command.\n"));
892 if (output != stdout) {
902 fd = fopen(argk[1], mode);
905 senditf(_("Cannot open file %s for output. ERR=%s\n"),
906 argk[1], be.bstrerror(errno));
914 * exec "some-command" [wait-seconds]
916 static int execcmd(FILE *input, BSOCK *UA_sock)
924 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
928 wait = atoi(argk[2]);
930 bpipe = open_bpipe(argk[1], wait, "r");
933 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
934 argk[1], be.bstrerror(errno));
938 while (fgets(line, sizeof(line), bpipe->rfd)) {
941 stat = close_bpipe(bpipe);
945 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
951 static int echocmd(FILE *input, BSOCK *UA_sock)
953 for (int i=1; i < argc; i++) {
954 senditf("%s", argk[i]);
961 static int quitcmd(FILE *input, BSOCK *UA_sock)
966 static int sleepcmd(FILE *input, BSOCK *UA_sock)
969 sleep(atoi(argk[1]));
975 static int timecmd(FILE *input, BSOCK *UA_sock)
978 time_t ttime = time(NULL);
980 (void)localtime_r(&ttime, &tm);
981 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
987 * Send a line to the output file and or the terminal
989 void senditf(const char *fmt,...)
994 va_start(arg_ptr, fmt);
995 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1000 void sendit(const char *buf)
1004 if (output == stdout || teeout) {
1007 * Here, we convert every \n into \r\n because the
1008 * terminal is in raw mode when we are using
1011 for (p=q=buf; (p=strchr(q, '\n')); ) {
1014 memcpy(obuf, q, len);
1016 memcpy(obuf+len, "\r\n", 3);
1017 q = ++p; /* point after \n */
1018 fputs(obuf, output);
1025 if (output != stdout) {