3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
11 Copyright (C) 2000-2005 Kern Sibbald
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2.1 of the License.
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the Free
25 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
31 #include "console_conf.h"
39 #define con_set_zed_keys();
47 #include "../lib/winapi.h"
48 #define isatty(fd) (fd==0)
51 /* Exported variables */
56 extern int rl_catch_signals;
59 /* Imported functions */
60 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
63 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
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;
83 static FILE *output = stdout;
84 static bool tee = false; /* output to output and stdout */
85 static bool stop = false;
89 static char *argk[MAX_CMD_ARGS];
90 static char *argv[MAX_CMD_ARGS];
93 /* Command prototypes */
94 static int versioncmd(FILE *input, BSOCK *UA_sock);
95 static int inputcmd(FILE *input, BSOCK *UA_sock);
96 static int outputcmd(FILE *input, BSOCK *UA_sock);
97 static int teecmd(FILE *input, BSOCK *UA_sock);
98 static int quitcmd(FILE *input, BSOCK *UA_sock);
99 static int timecmd(FILE *input, BSOCK *UA_sock);
100 static int sleepcmd(FILE *input, BSOCK *UA_sock);
103 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
108 "Copyright (C) 2000-2005 Kern Sibbald\n"
109 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
110 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
111 " -c <file> set configuration file to file\n"
112 " -dnn set debug level to nn\n"
114 " -t test - read configuration and exit\n"
115 " -? print this message.\n"
116 "\n"), HOST_OS, DISTNAME, DISTVER);
121 void got_sigstop(int sig)
127 void got_sigcontinue(int sig)
133 void got_sigtout(int sig)
135 // printf("Got tout\n");
139 void got_sigtin(int sig)
141 // printf("Got tin\n");
145 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
152 * These are the @command
154 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
155 static struct cmdstruct commands[] = {
156 { N_("input"), inputcmd, _("input from file")},
157 { N_("output"), outputcmd, _("output to file")},
158 { N_("quit"), quitcmd, _("quit")},
159 { N_("tee"), teecmd, _("output to file and terminal")},
160 { N_("sleep"), sleepcmd, _("sleep specified time")},
161 { N_("time"), timecmd, _("print current time")},
162 { N_("version"), versioncmd, _("print Console's version")},
163 { N_("exit"), quitcmd, _("exit = quit")},
164 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
166 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
168 static int do_a_command(FILE *input, BSOCK *UA_sock)
179 Dmsg1(120, "Command: %s\n", UA_sock->msg);
185 if (*cmd == '#') { /* comment */
189 for (i=0; i<comsize; i++) { /* search for command */
190 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
191 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
197 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
198 UA_sock->msglen = strlen(UA_sock->msg);
199 sendit(UA_sock->msg);
205 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
207 const char *prompt = "*";
208 bool at_prompt = false;
209 int tty_input = isatty(fileno(input));
213 if (at_prompt) { /* don't prompt multiple times */
220 stat = get_cmd(input, prompt, UA_sock, 30);
228 /* Reading input from a file */
229 int len = sizeof_pool_memory(UA_sock->msg) - 1;
233 if (fgets(UA_sock->msg, len, input) == NULL) {
236 sendit(UA_sock->msg); /* echo to terminal */
237 strip_trailing_junk(UA_sock->msg);
238 UA_sock->msglen = strlen(UA_sock->msg);
243 break; /* error or interrupt */
244 } else if (stat == 0) { /* timeout */
245 if (strcmp(prompt, "*") == 0) {
246 bnet_fsend(UA_sock, ".messages");
252 /* @ => internal command for us */
253 if (UA_sock->msg[0] == '@') {
254 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
255 if (!do_a_command(input, UA_sock)) {
260 if (!bnet_send(UA_sock)) { /* send command */
264 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
267 while ((stat = bnet_recv(UA_sock)) >= 0) {
274 /* Suppress output if running in background or user hit ctl-c */
275 if (!stop && !usrbrk()) {
276 sendit(UA_sock->msg);
287 if (is_bnet_stop(UA_sock)) {
288 break; /* error or term */
289 } else if (stat == BNET_SIGNAL) {
290 if (UA_sock->msglen == BNET_PROMPT) {
293 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
300 * Call-back for reading a passphrase for an encrypted PEM file
301 * This function uses getpass(), which uses a static buffer and is NOT thread-safe.
303 static int tls_pem_callback(char *buf, int size, const void *userdata)
305 const char *prompt = (const char *) userdata;
308 passwd = getpass(prompt);
309 bstrncpy(buf, passwd, size);
311 return (strlen(buf));
316 /*********************************************************************
318 * Main Bacula Console -- User Interface Program
321 int main(int argc, char *argv[])
324 bool no_signals = false;
325 bool test_config = false;
329 my_name_is(argc, argv, "bconsole");
330 textdomain("bacula");
331 init_msg(NULL, NULL);
332 working_directory = "/tmp";
333 args = get_pool_memory(PM_FNAME);
336 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
338 case 'c': /* configuration file */
339 if (configfile != NULL) {
342 configfile = bstrdup(optarg);
346 debug_level = atoi(optarg);
347 if (debug_level <= 0) {
352 case 's': /* turn off signals */
371 init_signals(terminate_console);
374 #if !defined(HAVE_WIN32)
375 /* Override Bacula default signals */
376 signal(SIGCHLD, SIG_IGN);
377 signal(SIGQUIT, SIG_IGN);
378 signal(SIGTSTP, got_sigstop);
379 signal(SIGCONT, got_sigcontinue);
380 signal(SIGTTIN, got_sigtin);
381 signal(SIGTTOU, got_sigtout);
394 if (configfile == NULL) {
395 configfile = bstrdup(CONFIG_FILE);
398 parse_config(configfile);
400 if (init_tls() != 0) {
401 Emsg0(M_ERROR_TERM, 0, _("TLS library initialization failed.\n"));
404 if (!check_resources()) {
405 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
409 terminate_console(0);
413 memset(&jcr, 0, sizeof(jcr));
415 (void)WSA_Init(); /* Initialize Windows sockets */
418 struct sockaddr client_addr;
419 memset(&client_addr, 0, sizeof(client_addr));
420 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
422 sendit(_("Available Directors:\n"));
425 foreach_res(dir, R_DIRECTOR) {
426 senditf( _("%d %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
430 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
431 (void)WSACleanup(); /* Cleanup Windows sockets */
434 item = atoi(UA_sock->msg);
435 if (item < 0 || item > numdir) {
436 senditf(_("You must enter a number between 1 and %d\n"), numdir);
441 for (i=0; i<item; i++) {
442 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
448 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
453 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
456 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
460 /* Initialize Console TLS context */
461 if (cons && (cons->tls_enable || cons->tls_require)) {
462 /* Generate passphrase prompt */
463 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
465 /* Initialize TLS context:
466 * Args: CA certfile, CA certdir, Certfile, Keyfile,
467 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
468 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
469 cons->tls_ca_certdir, cons->tls_certfile,
470 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
472 if (!cons->tls_ctx) {
473 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
475 terminate_console(0);
481 /* Initialize Director TLS context */
482 if (dir->tls_enable || dir->tls_require) {
483 /* Generate passphrase prompt */
484 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
486 /* Initialize TLS context:
487 * Args: CA certfile, CA certdir, Certfile, Keyfile,
488 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
489 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
490 dir->tls_ca_certdir, dir->tls_certfile,
491 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
494 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
496 terminate_console(0);
500 #endif /* HAVE_TLS */
502 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
503 NULL, dir->DIRport, 0);
504 if (UA_sock == NULL) {
505 terminate_console(0);
508 jcr.dir_bsock = UA_sock;
510 /* If cons==NULL, default console will be used */
511 if (!authenticate_director(&jcr, dir, cons)) {
512 terminate_console(0);
516 Dmsg0(40, "Opened connection with Director daemon\n");
518 sendit(_("Enter a period to cancel a command.\n"));
520 /* Run commands in ~/.bconsolerc if any */
521 char *env = getenv("HOME");
524 pm_strcpy(&UA_sock->msg, env);
525 pm_strcat(&UA_sock->msg, "/.bconsolerc");
526 fd = fopen(UA_sock->msg, "r");
528 read_and_process_input(fd, UA_sock);
533 read_and_process_input(stdin, UA_sock);
536 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
540 terminate_console(0);
545 /* Cleanup and then exit */
546 static void terminate_console(int sig)
549 static bool already_here = false;
551 if (already_here) { /* avoid recursive temination problems */
556 free_pool_memory(args);
558 (void)WSACleanup(); /* Cleanup Windows sockets */
566 * Make a quick check to see that we have all the
569 static int check_resources()
577 foreach_res(director, R_DIRECTOR) {
581 /* tls_require implies tls_enable */
582 if (director->tls_require) {
583 director->tls_enable = true;
586 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
587 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
588 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
589 " At least one CA certificate store is required.\n"),
590 director->hdr.name, configfile);
593 #endif /* HAVE_TLS */
597 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
598 "Without that I don't how to speak to the Director :-(\n"), configfile);
604 /* Loop over Consoles */
605 foreach_res(cons, R_CONSOLE) {
606 /* tls_require implies tls_enable */
607 if (cons->tls_require) {
608 cons->tls_enable = true;
611 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
612 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
613 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
614 cons->hdr.name, configfile);
618 #endif /* HAVE_TLS */
627 #define READLINE_LIBRARY 1
629 #include "readline.h"
634 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
638 rl_catch_signals = 0; /* do it ourselves */
639 line = readline((char *)prompt); /* cast needed for old readlines */
644 strip_trailing_junk(line);
645 sock->msglen = pm_strcpy(&sock->msg, line);
647 add_history(sock->msg);
653 #else /* no readline, do it ourselves */
656 * Returns: 1 if data available
661 wait_for_data(int fd, int sec)
666 return 1; /* select doesn't seem to work on Win32 */
673 FD_SET((unsigned)fd, &fdset);
674 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
675 case 0: /* timeout */
678 if (errno == EINTR || errno == EAGAIN) {
681 return -1; /* error return */
689 * Get next input command from terminal.
691 * Returns: 1 if got input
696 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
700 if (output == stdout || tee) {
705 switch (wait_for_data(fileno(input), sec)) {
707 return 0; /* timeout */
709 return -1; /* error */
711 len = sizeof_pool_memory(sock->msg) - 1;
717 if (isatty(fileno(input))) {
718 input_line(sock->msg, len);
722 #ifdef HAVE_WIN32 /* use special console for input on win32 */
723 if (input == stdin) {
724 if (win32_cgets(sock->msg, len) == NULL) {
730 if (fgets(sock->msg, len, input) == NULL) {
739 strip_trailing_junk(sock->msg);
740 sock->msglen = strlen(sock->msg);
746 static int versioncmd(FILE *input, BSOCK *UA_sock)
748 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
749 HOST_OS, DISTNAME, DISTVER);
753 static int inputcmd(FILE *input, BSOCK *UA_sock)
758 sendit(_("Too many arguments on input command.\n"));
762 sendit(_("First argument to input command must be a filename.\n"));
765 fd = fopen(argk[1], "r");
767 senditf(_("Cannot open file %s for input. ERR=%s\n"),
768 argk[1], strerror(errno));
771 read_and_process_input(fd, UA_sock);
776 /* Send output to both termina and specified file */
777 static int teecmd(FILE *input, BSOCK *UA_sock)
780 return do_outputcmd(input, UA_sock);
783 /* Send output to specified "file" */
784 static int outputcmd(FILE *input, BSOCK *UA_sock)
787 return do_outputcmd(input, UA_sock);
791 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
794 const char *mode = "a+";
797 sendit(_("Too many arguments on output/tee command.\n"));
801 if (output != stdout) {
811 fd = fopen(argk[1], mode);
813 senditf(_("Cannot open file %s for output. ERR=%s\n"),
814 argk[1], strerror(errno));
821 static int quitcmd(FILE *input, BSOCK *UA_sock)
826 static int sleepcmd(FILE *input, BSOCK *UA_sock)
829 sleep(atoi(argk[1]));
835 static int timecmd(FILE *input, BSOCK *UA_sock)
838 time_t ttime = time(NULL);
840 localtime_r(&ttime, &tm);
841 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
847 * Send a line to the output file and or the terminal
849 void senditf(const char *fmt,...)
854 va_start(arg_ptr, fmt);
855 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
860 void sendit(const char *buf)
863 if (output == stdout || tee) {
866 * Here, we convert every \n into \r\n because the
867 * terminal is in raw mode when we are using
870 for (p=q=buf; (p=strchr(q, '\n')); ) {
875 q = ++p; /* point after \n */
881 if (output != stdout) {
891 if (output != stdout || tee) {