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);
107 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
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"), 2000, 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
525 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
526 cons->tls_ca_certdir, cons->tls_certfile,
527 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
529 if (!cons->tls_ctx) {
530 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
532 terminate_console(0);
537 /* Initialize Director TLS context */
538 if (dir->tls_enable || dir->tls_require) {
539 /* Generate passphrase prompt */
540 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
542 /* Initialize TLS context:
543 * Args: CA certfile, CA certdir, Certfile, Keyfile,
544 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
545 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
546 dir->tls_ca_certdir, dir->tls_certfile,
547 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
550 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
552 terminate_console(0);
557 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
558 NULL, dir->DIRport, 0);
559 if (UA_sock == NULL) {
560 terminate_console(0);
563 jcr.dir_bsock = UA_sock;
565 /* If cons==NULL, default console will be used */
566 if (!authenticate_director(&jcr, dir, cons)) {
567 terminate_console(0);
571 Dmsg0(40, "Opened connection with Director daemon\n");
573 sendit(_("Enter a period to cancel a command.\n"));
575 /* Run commands in ~/.bconsolerc if any */
576 char *env = getenv("HOME");
579 pm_strcpy(&UA_sock->msg, env);
580 pm_strcat(&UA_sock->msg, "/.bconsolerc");
581 fd = fopen(UA_sock->msg, "rb");
583 read_and_process_input(fd, UA_sock);
588 read_and_process_input(stdin, UA_sock);
591 UA_sock->signal(BNET_TERMINATE); /* send EOF */
595 terminate_console(0);
600 /* Cleanup and then exit */
601 static void terminate_console(int sig)
604 static bool already_here = false;
606 if (already_here) { /* avoid recursive temination problems */
611 free_pool_memory(args);
615 (void)WSACleanup(); /* Cleanup Windows sockets */
623 * Make a quick check to see that we have all the
626 static int check_resources()
634 foreach_res(director, R_DIRECTOR) {
637 /* tls_require implies tls_enable */
638 if (director->tls_require) {
640 director->tls_enable = true;
642 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
648 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
649 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
650 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
651 " At least one CA certificate store is required.\n"),
652 director->hdr.name, configfile);
658 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
659 "Without that I don't how to speak to the Director :-(\n"), configfile);
664 /* Loop over Consoles */
665 foreach_res(cons, R_CONSOLE) {
666 /* tls_require implies tls_enable */
667 if (cons->tls_require) {
669 cons->tls_enable = true;
671 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
677 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
678 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
679 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
680 cons->hdr.name, configfile);
692 #define READLINE_LIBRARY 1
694 #include "readline.h"
699 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
703 rl_catch_signals = 0; /* do it ourselves */
704 line = readline((char *)prompt); /* cast needed for old readlines */
709 strip_trailing_junk(line);
710 sock->msglen = pm_strcpy(&sock->msg, line);
712 add_history(sock->msg);
718 #else /* no readline, do it ourselves */
720 #if !defined(HAVE_WIN32)
721 static bool bisatty(int fd)
731 * Returns: 1 if data available
736 wait_for_data(int fd, int sec)
738 #if defined(HAVE_WIN32)
748 FD_SET((unsigned)fd, &fdset);
749 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
750 case 0: /* timeout */
753 if (errno == EINTR || errno == EAGAIN) {
756 return -1; /* error return */
765 * Get next input command from terminal.
767 * Returns: 1 if got input
772 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
776 if (output == stdout || teeout) {
781 switch (wait_for_data(fileno(input), sec)) {
783 return 0; /* timeout */
785 return -1; /* error */
787 len = sizeof_pool_memory(sock->msg) - 1;
793 if (bisatty(fileno(input))) {
794 input_line(sock->msg, len);
798 #ifdef HAVE_WIN32 /* use special console for input on win32 */
799 if (input == stdin) {
800 if (win32_cgets(sock->msg, len) == NULL) {
806 if (fgets(sock->msg, len, input) == NULL) {
815 strip_trailing_junk(sock->msg);
816 sock->msglen = strlen(sock->msg);
820 #endif /* end non-readline code */
822 static int versioncmd(FILE *input, BSOCK *UA_sock)
824 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
825 HOST_OS, DISTNAME, DISTVER);
829 static int inputcmd(FILE *input, BSOCK *UA_sock)
834 sendit(_("Too many arguments on input command.\n"));
838 sendit(_("First argument to input command must be a filename.\n"));
841 fd = fopen(argk[1], "rb");
843 senditf(_("Cannot open file %s for input. ERR=%s\n"),
844 argk[1], strerror(errno));
847 read_and_process_input(fd, UA_sock);
852 /* Send output to both termina and specified file */
853 static int teecmd(FILE *input, BSOCK *UA_sock)
856 return do_outputcmd(input, UA_sock);
859 /* Send output to specified "file" */
860 static int outputcmd(FILE *input, BSOCK *UA_sock)
863 return do_outputcmd(input, UA_sock);
867 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
870 const char *mode = "a+b";
873 sendit(_("Too many arguments on output/tee command.\n"));
877 if (output != stdout) {
887 fd = fopen(argk[1], mode);
889 senditf(_("Cannot open file %s for output. ERR=%s\n"),
890 argk[1], strerror(errno));
897 static int echocmd(FILE *intut, BSOCK *UA_sock)
899 for (int i=1; i < argc; i++) {
900 senditf("%s", argk[i]);
907 static int quitcmd(FILE *input, BSOCK *UA_sock)
912 static int sleepcmd(FILE *input, BSOCK *UA_sock)
915 sleep(atoi(argk[1]));
921 static int timecmd(FILE *input, BSOCK *UA_sock)
924 time_t ttime = time(NULL);
926 (void)localtime_r(&ttime, &tm);
927 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
933 * Send a line to the output file and or the terminal
935 void senditf(const char *fmt,...)
940 va_start(arg_ptr, fmt);
941 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
946 void sendit(const char *buf)
950 if (output == stdout || teeout) {
953 * Here, we convert every \n into \r\n because the
954 * terminal is in raw mode when we are using
957 for (p=q=buf; (p=strchr(q, '\n')); ) {
960 memcpy(obuf, q, len);
962 memcpy(obuf+len, "\r\n", 3);
963 q = ++p; /* point after \n */
971 if (output != stdout) {