3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
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);
107 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
112 "Copyright (C) 2000-%s Kern Sibbald\n"
113 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
114 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
115 " -c <file> set configuration file to file\n"
116 " -dnn set debug level to nn\n"
119 " -t test - read configuration and exit\n"
120 " -? print this message.\n"
121 "\n"), BYEAR, HOST_OS, DISTNAME, DISTVER);
126 void got_sigstop(int sig)
132 void got_sigcontinue(int sig)
138 void got_sigtout(int sig)
140 // printf("Got tout\n");
144 void got_sigtin(int sig)
146 // printf("Got tin\n");
150 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
157 * These are the @command
159 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
160 static struct cmdstruct commands[] = {
161 { N_("input"), inputcmd, _("input from file")},
162 { N_("output"), outputcmd, _("output to file")},
163 { N_("quit"), quitcmd, _("quit")},
164 { N_("tee"), teecmd, _("output to file and terminal")},
165 { N_("sleep"), sleepcmd, _("sleep specified time")},
166 { N_("time"), timecmd, _("print current time")},
167 { N_("version"), versioncmd, _("print Console's version")},
168 { N_("echo"), echocmd, _("echo command string")},
169 { N_("exit"), quitcmd, _("exit = quit")},
170 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
172 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
174 static int do_a_command(FILE *input, BSOCK *UA_sock)
185 Dmsg1(120, "Command: %s\n", UA_sock->msg);
191 if (*cmd == '#') { /* comment */
195 for (i=0; i<comsize; i++) { /* search for command */
196 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
197 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
203 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
204 UA_sock->msglen = strlen(UA_sock->msg);
205 sendit(UA_sock->msg);
211 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
213 const char *prompt = "*";
214 bool at_prompt = false;
215 int tty_input = isatty(fileno(input));
219 if (at_prompt) { /* don't prompt multiple times */
226 stat = get_cmd(input, prompt, UA_sock, 30);
234 /* Reading input from a file */
235 int len = sizeof_pool_memory(UA_sock->msg) - 1;
239 if (fgets(UA_sock->msg, len, input) == NULL) {
242 sendit(UA_sock->msg); /* echo to terminal */
243 strip_trailing_junk(UA_sock->msg);
244 UA_sock->msglen = strlen(UA_sock->msg);
249 break; /* error or interrupt */
250 } else if (stat == 0) { /* timeout */
251 if (strcmp(prompt, "*") == 0) {
252 bnet_fsend(UA_sock, ".messages");
258 /* @ => internal command for us */
259 if (UA_sock->msg[0] == '@') {
260 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
261 if (!do_a_command(input, UA_sock)) {
266 if (!bnet_send(UA_sock)) { /* send command */
270 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
273 while ((stat = bnet_recv(UA_sock)) >= 0) {
280 /* Suppress output if running in background or user hit ctl-c */
281 if (!stop && !usrbrk()) {
282 sendit(UA_sock->msg);
293 if (is_bnet_stop(UA_sock)) {
294 break; /* error or term */
295 } else if (stat == BNET_SIGNAL) {
296 if (UA_sock->msglen == BNET_PROMPT) {
299 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
305 * Call-back for reading a passphrase for an encrypted PEM file
306 * This function uses getpass(),
307 * which uses a static buffer and is NOT thread-safe.
309 static int tls_pem_callback(char *buf, int size, const void *userdata)
312 const char *prompt = (const char *)userdata;
313 # if defined(HAVE_WIN32)
315 if (win32_cgets(buf, size) == NULL) {
324 passwd = getpass(prompt);
325 bstrncpy(buf, passwd, size);
335 /*********************************************************************
337 * Main Bacula Console -- User Interface Program
340 int main(int argc, char *argv[])
343 bool no_signals = false;
344 bool test_config = false;
347 setlocale(LC_ALL, "");
348 bindtextdomain("bacula", LOCALEDIR);
349 textdomain("bacula");
352 my_name_is(argc, argv, "bconsole");
353 init_msg(NULL, NULL);
354 working_directory = "/tmp";
355 args = get_pool_memory(PM_FNAME);
357 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
359 case 'c': /* configuration file */
360 if (configfile != NULL) {
363 configfile = bstrdup(optarg);
367 debug_level = atoi(optarg);
368 if (debug_level <= 0) {
373 case 'n': /* no conio */
377 case 's': /* turn off signals */
395 init_signals(terminate_console);
399 #if !defined(HAVE_WIN32)
400 /* Override Bacula default signals */
401 signal(SIGQUIT, SIG_IGN);
402 signal(SIGTSTP, got_sigstop);
403 signal(SIGCONT, got_sigcontinue);
404 signal(SIGTTIN, got_sigtin);
405 signal(SIGTTOU, got_sigtout);
416 if (configfile == NULL) {
417 configfile = bstrdup(CONFIG_FILE);
420 parse_config(configfile);
422 if (init_crypto() != 0) {
423 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
426 if (!check_resources()) {
427 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
435 terminate_console(0);
439 memset(&jcr, 0, sizeof(jcr));
441 (void)WSA_Init(); /* Initialize Windows sockets */
445 foreach_res(dir, R_DIRECTOR) {
449 foreach_res(cons, R_CONSOLE) {
455 struct sockaddr client_addr;
456 memset(&client_addr, 0, sizeof(client_addr));
457 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
459 sendit(_("Available Directors:\n"));
462 foreach_res(dir, R_DIRECTOR) {
463 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
467 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
468 (void)WSACleanup(); /* Cleanup Windows sockets */
471 item = atoi(UA_sock->msg);
472 if (item < 0 || item > numdir) {
473 senditf(_("You must enter a number between 1 and %d\n"), numdir);
478 for (i=0; i<item; i++) {
479 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
481 /* Look for a console linked to this director */
482 for (i=0; i<numcon; i++) {
483 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
484 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
489 /* Look for the first non-linked console */
491 for (i=0; i<numcon; i++) {
492 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
493 if (cons->director == NULL)
500 /* If no director, take first one */
503 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
506 /* If no console, take first one */
509 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
513 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
516 /* Initialize Console TLS context */
517 if (cons && (cons->tls_enable || cons->tls_require)) {
518 /* Generate passphrase prompt */
519 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
521 /* Initialize TLS context:
522 * Args: CA certfile, CA certdir, Certfile, Keyfile,
523 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
524 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
525 cons->tls_ca_certdir, cons->tls_certfile,
526 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
528 if (!cons->tls_ctx) {
529 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
531 terminate_console(0);
536 /* Initialize Director TLS context */
537 if (dir->tls_enable || dir->tls_require) {
538 /* Generate passphrase prompt */
539 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
541 /* Initialize TLS context:
542 * Args: CA certfile, CA certdir, Certfile, Keyfile,
543 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
544 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
545 dir->tls_ca_certdir, dir->tls_certfile,
546 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
549 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
551 terminate_console(0);
556 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
557 NULL, dir->DIRport, 0);
558 if (UA_sock == NULL) {
559 terminate_console(0);
562 jcr.dir_bsock = UA_sock;
564 /* If cons==NULL, default console will be used */
565 if (!authenticate_director(&jcr, dir, cons)) {
566 terminate_console(0);
570 Dmsg0(40, "Opened connection with Director daemon\n");
572 sendit(_("Enter a period to cancel a command.\n"));
574 /* Run commands in ~/.bconsolerc if any */
575 char *env = getenv("HOME");
578 pm_strcpy(&UA_sock->msg, env);
579 pm_strcat(&UA_sock->msg, "/.bconsolerc");
580 fd = fopen(UA_sock->msg, "rb");
582 read_and_process_input(fd, UA_sock);
587 read_and_process_input(stdin, UA_sock);
590 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
594 terminate_console(0);
599 /* Cleanup and then exit */
600 static void terminate_console(int sig)
603 static bool already_here = false;
605 if (already_here) { /* avoid recursive temination problems */
610 free_pool_memory(args);
614 (void)WSACleanup(); /* Cleanup Windows sockets */
622 * Make a quick check to see that we have all the
625 static int check_resources()
633 foreach_res(director, R_DIRECTOR) {
636 /* tls_require implies tls_enable */
637 if (director->tls_require) {
639 director->tls_enable = true;
641 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
647 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
648 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
649 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
650 " At least one CA certificate store is required.\n"),
651 director->hdr.name, configfile);
657 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
658 "Without that I don't how to speak to the Director :-(\n"), configfile);
663 /* Loop over Consoles */
664 foreach_res(cons, R_CONSOLE) {
665 /* tls_require implies tls_enable */
666 if (cons->tls_require) {
668 cons->tls_enable = true;
670 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
676 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
677 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
678 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
679 cons->hdr.name, configfile);
691 #define READLINE_LIBRARY 1
693 #include "readline.h"
698 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
702 rl_catch_signals = 0; /* do it ourselves */
703 line = readline((char *)prompt); /* cast needed for old readlines */
708 strip_trailing_junk(line);
709 sock->msglen = pm_strcpy(&sock->msg, line);
711 add_history(sock->msg);
717 #else /* no readline, do it ourselves */
719 #if !defined(HAVE_WIN32)
720 static bool bisatty(int fd)
730 * Returns: 1 if data available
735 wait_for_data(int fd, int sec)
737 #if defined(HAVE_WIN32)
747 FD_SET((unsigned)fd, &fdset);
748 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
749 case 0: /* timeout */
752 if (errno == EINTR || errno == EAGAIN) {
755 return -1; /* error return */
764 * Get next input command from terminal.
766 * Returns: 1 if got input
771 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
775 if (output == stdout || teeout) {
780 switch (wait_for_data(fileno(input), sec)) {
782 return 0; /* timeout */
784 return -1; /* error */
786 len = sizeof_pool_memory(sock->msg) - 1;
792 if (bisatty(fileno(input))) {
793 input_line(sock->msg, len);
797 #ifdef HAVE_WIN32 /* use special console for input on win32 */
798 if (input == stdin) {
799 if (win32_cgets(sock->msg, len) == NULL) {
805 if (fgets(sock->msg, len, input) == NULL) {
814 strip_trailing_junk(sock->msg);
815 sock->msglen = strlen(sock->msg);
819 #endif /* end non-readline code */
821 static int versioncmd(FILE *input, BSOCK *UA_sock)
823 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
824 HOST_OS, DISTNAME, DISTVER);
828 static int inputcmd(FILE *input, BSOCK *UA_sock)
833 sendit(_("Too many arguments on input command.\n"));
837 sendit(_("First argument to input command must be a filename.\n"));
840 fd = fopen(argk[1], "rb");
842 senditf(_("Cannot open file %s for input. ERR=%s\n"),
843 argk[1], strerror(errno));
846 read_and_process_input(fd, UA_sock);
851 /* Send output to both termina and specified file */
852 static int teecmd(FILE *input, BSOCK *UA_sock)
855 return do_outputcmd(input, UA_sock);
858 /* Send output to specified "file" */
859 static int outputcmd(FILE *input, BSOCK *UA_sock)
862 return do_outputcmd(input, UA_sock);
866 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
869 const char *mode = "a+b";
872 sendit(_("Too many arguments on output/tee command.\n"));
876 if (output != stdout) {
886 fd = fopen(argk[1], mode);
888 senditf(_("Cannot open file %s for output. ERR=%s\n"),
889 argk[1], strerror(errno));
896 static int echocmd(FILE *intut, BSOCK *UA_sock)
898 for (int i=1; i < argc; i++) {
899 senditf("%s", argk[i]);
906 static int quitcmd(FILE *input, BSOCK *UA_sock)
911 static int sleepcmd(FILE *input, BSOCK *UA_sock)
914 sleep(atoi(argk[1]));
920 static int timecmd(FILE *input, BSOCK *UA_sock)
923 time_t ttime = time(NULL);
925 (void)localtime_r(&ttime, &tm);
926 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
932 * Send a line to the output file and or the terminal
934 void senditf(const char *fmt,...)
939 va_start(arg_ptr, fmt);
940 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
945 void sendit(const char *buf)
949 if (output == stdout || teeout) {
952 * Here, we convert every \n into \r\n because the
953 * terminal is in raw mode when we are using
956 for (p=q=buf; (p=strchr(q, '\n')); ) {
959 memcpy(obuf, q, len);
961 memcpy(obuf+len, "\r\n", 3);
962 q = ++p; /* point after \n */
970 if (output != stdout) {