3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
11 Copyright (C) 2000-2004 Kern Sibbald and John Walker
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, or (at your option) any later version.
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-2004 Kern Sibbald and John Walker\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) {