2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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 " -d <nn> set debug level to <nn>\n"
118 " -dt print timestamp in debug output\n"
121 " -t test - read configuration and exit\n"
122 " -? print this message.\n"
123 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
128 void got_sigstop(int sig)
134 void got_sigcontinue(int sig)
140 void got_sigtout(int sig)
142 // printf("Got tout\n");
146 void got_sigtin(int sig)
148 // printf("Got tin\n");
152 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
159 * These are the @command
161 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
162 static struct cmdstruct commands[] = {
163 { N_("input"), inputcmd, _("input from file")},
164 { N_("output"), outputcmd, _("output to file")},
165 { N_("quit"), quitcmd, _("quit")},
166 { N_("tee"), teecmd, _("output to file and terminal")},
167 { N_("sleep"), sleepcmd, _("sleep specified time")},
168 { N_("time"), timecmd, _("print current time")},
169 { N_("version"), versioncmd, _("print Console's version")},
170 { N_("echo"), echocmd, _("echo command string")},
171 { N_("exec"), execcmd, _("execute an external command")},
172 { N_("exit"), quitcmd, _("exit = quit")},
173 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
175 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
177 static int do_a_command(FILE *input, BSOCK *UA_sock)
188 Dmsg1(120, "Command: %s\n", UA_sock->msg);
194 if (*cmd == '#') { /* comment */
198 for (i=0; i<comsize; i++) { /* search for command */
199 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
200 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
206 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
207 UA_sock->msglen = strlen(UA_sock->msg);
208 sendit(UA_sock->msg);
214 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
216 const char *prompt = "*";
217 bool at_prompt = false;
218 int tty_input = isatty(fileno(input));
222 if (at_prompt) { /* don't prompt multiple times */
229 stat = get_cmd(input, prompt, UA_sock, 30);
237 /* Reading input from a file */
238 int len = sizeof_pool_memory(UA_sock->msg) - 1;
242 if (fgets(UA_sock->msg, len, input) == NULL) {
245 sendit(UA_sock->msg); /* echo to terminal */
246 strip_trailing_junk(UA_sock->msg);
247 UA_sock->msglen = strlen(UA_sock->msg);
252 break; /* error or interrupt */
253 } else if (stat == 0) { /* timeout */
254 if (strcmp(prompt, "*") == 0) {
255 bnet_fsend(UA_sock, ".messages");
261 /* @ => internal command for us */
262 if (UA_sock->msg[0] == '@') {
263 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
264 if (!do_a_command(input, UA_sock)) {
269 if (!bnet_send(UA_sock)) { /* send command */
273 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
276 while ((stat = bnet_recv(UA_sock)) >= 0) {
283 /* Suppress output if running in background or user hit ctl-c */
284 if (!stop && !usrbrk()) {
285 sendit(UA_sock->msg);
296 if (is_bnet_stop(UA_sock)) {
297 break; /* error or term */
298 } else if (stat == BNET_SIGNAL) {
299 if (UA_sock->msglen == BNET_PROMPT) {
302 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
308 * Call-back for reading a passphrase for an encrypted PEM file
309 * This function uses getpass(),
310 * which uses a static buffer and is NOT thread-safe.
312 static int tls_pem_callback(char *buf, int size, const void *userdata)
315 const char *prompt = (const char *)userdata;
316 # if defined(HAVE_WIN32)
318 if (win32_cgets(buf, size) == NULL) {
327 passwd = getpass(prompt);
328 bstrncpy(buf, passwd, size);
338 #define READLINE_LIBRARY 1
340 #include "readline.h"
344 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
348 rl_catch_signals = 0; /* do it ourselves */
349 line = readline((char *)prompt); /* cast needed for old readlines */
354 strip_trailing_junk(line);
355 sock->msglen = pm_strcpy(&sock->msg, line);
357 add_history(sock->msg);
363 #else /* no readline, do it ourselves */
365 #if !defined(HAVE_WIN32)
366 static bool bisatty(int fd)
376 * Returns: 1 if data available
381 wait_for_data(int fd, int sec)
383 #if defined(HAVE_WIN32)
393 FD_SET((unsigned)fd, &fdset);
394 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
395 case 0: /* timeout */
398 if (errno == EINTR || errno == EAGAIN) {
401 return -1; /* error return */
410 * Get next input command from terminal.
412 * Returns: 1 if got input
417 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
421 if (output == stdout || teeout) {
426 switch (wait_for_data(fileno(input), sec)) {
428 return 0; /* timeout */
430 return -1; /* error */
432 len = sizeof_pool_memory(sock->msg) - 1;
438 if (bisatty(fileno(input))) {
439 input_line(sock->msg, len);
443 #ifdef HAVE_WIN32 /* use special console for input on win32 */
444 if (input == stdin) {
445 if (win32_cgets(sock->msg, len) == NULL) {
451 if (fgets(sock->msg, len, input) == NULL) {
460 strip_trailing_junk(sock->msg);
461 sock->msglen = strlen(sock->msg);
465 #endif /* ! HAVE_READLINE */
468 static int console_update_history(const char *histfile)
473 /* first, try to truncate the history file, and if it
474 * fail, the file is probably not present, and we
475 * can use write_history to create it
478 if (history_truncate_file(histfile, 100) == 0) {
479 ret = append_history(history_length, histfile);
481 ret = write_history(histfile);
489 static int console_init_history(const char *histfile)
496 ret = read_history(histfile);
503 /*********************************************************************
505 * Main Bacula Console -- User Interface Program
508 int main(int argc, char *argv[])
511 bool no_signals = false;
512 bool test_config = false;
516 setlocale(LC_ALL, "");
517 bindtextdomain("bacula", LOCALEDIR);
518 textdomain("bacula");
521 my_name_is(argc, argv, "bconsole");
522 init_msg(NULL, NULL);
523 working_directory = "/tmp";
524 args = get_pool_memory(PM_FNAME);
526 while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
528 case 'c': /* configuration file */
529 if (configfile != NULL) {
532 configfile = bstrdup(optarg);
536 if (*optarg == 't') {
537 dbg_timestamp = true;
539 debug_level = atoi(optarg);
540 if (debug_level <= 0) {
546 case 'n': /* no conio */
550 case 's': /* turn off signals */
568 init_signals(terminate_console);
572 #if !defined(HAVE_WIN32)
573 /* Override Bacula default signals */
574 signal(SIGQUIT, SIG_IGN);
575 signal(SIGTSTP, got_sigstop);
576 signal(SIGCONT, got_sigcontinue);
577 signal(SIGTTIN, got_sigtin);
578 signal(SIGTTOU, got_sigtout);
589 if (configfile == NULL) {
590 configfile = bstrdup(CONFIG_FILE);
593 parse_config(configfile);
595 if (init_crypto() != 0) {
596 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
599 if (!check_resources()) {
600 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
608 terminate_console(0);
612 memset(&jcr, 0, sizeof(jcr));
614 (void)WSA_Init(); /* Initialize Windows sockets */
618 foreach_res(dir, R_DIRECTOR) {
622 foreach_res(cons, R_CONSOLE) {
628 struct sockaddr client_addr;
629 memset(&client_addr, 0, sizeof(client_addr));
630 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
632 sendit(_("Available Directors:\n"));
635 foreach_res(dir, R_DIRECTOR) {
636 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
640 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
641 (void)WSACleanup(); /* Cleanup Windows sockets */
644 if (!is_a_number(UA_sock->msg)) {
645 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
646 UA_sock->msg, numdir);
649 item = atoi(UA_sock->msg);
650 if (item < 0 || item > numdir) {
651 senditf(_("You must enter a number between 1 and %d\n"), numdir);
656 for (i=0; i<item; i++) {
657 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
659 /* Look for a console linked to this director */
660 for (i=0; i<numcon; i++) {
661 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
662 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
667 /* Look for the first non-linked console */
669 for (i=0; i<numcon; i++) {
670 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
671 if (cons->director == NULL)
678 /* If no director, take first one */
681 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
684 /* If no console, take first one */
687 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
691 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
694 /* Initialize Console TLS context */
695 if (cons && (cons->tls_enable || cons->tls_require)) {
696 /* Generate passphrase prompt */
697 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
699 /* Initialize TLS context:
700 * Args: CA certfile, CA certdir, Certfile, Keyfile,
701 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
703 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
704 cons->tls_ca_certdir, cons->tls_certfile,
705 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
707 if (!cons->tls_ctx) {
708 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
710 terminate_console(0);
715 /* Initialize Director TLS context */
716 if (dir->tls_enable || dir->tls_require) {
717 /* Generate passphrase prompt */
718 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
720 /* Initialize TLS context:
721 * Args: CA certfile, CA certdir, Certfile, Keyfile,
722 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
723 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
724 dir->tls_ca_certdir, dir->tls_certfile,
725 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
728 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
730 terminate_console(0);
735 if (dir->heartbeat_interval) {
736 heart_beat = dir->heartbeat_interval;
738 heart_beat = cons->heartbeat_interval;
742 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
743 NULL, dir->DIRport, 0);
744 if (UA_sock == NULL) {
745 terminate_console(0);
748 jcr.dir_bsock = UA_sock;
750 /* If cons==NULL, default console will be used */
751 if (!authenticate_director(&jcr, dir, cons)) {
752 terminate_console(0);
756 Dmsg0(40, "Opened connection with Director daemon\n");
758 sendit(_("Enter a period to cancel a command.\n"));
760 /* Read/Update history file if HOME exists */
761 POOL_MEM history_file;
763 /* Run commands in ~/.bconsolerc if any */
764 char *env = getenv("HOME");
767 pm_strcpy(&UA_sock->msg, env);
768 pm_strcat(&UA_sock->msg, "/.bconsolerc");
769 fd = fopen(UA_sock->msg, "rb");
771 read_and_process_input(fd, UA_sock);
775 pm_strcpy(history_file, env);
776 pm_strcat(history_file, "/.bconsole_history");
777 console_init_history(history_file.c_str());
780 read_and_process_input(stdin, UA_sock);
783 UA_sock->signal(BNET_TERMINATE); /* send EOF */
788 console_update_history(history_file.c_str());
791 terminate_console(0);
795 /* Cleanup and then exit */
796 static void terminate_console(int sig)
799 static bool already_here = false;
801 if (already_here) { /* avoid recursive temination problems */
806 free_pool_memory(args);
810 (void)WSACleanup(); /* Cleanup Windows sockets */
818 * Make a quick check to see that we have all the
821 static int check_resources()
830 foreach_res(director, R_DIRECTOR) {
833 /* tls_require implies tls_enable */
834 if (director->tls_require) {
836 director->tls_enable = true;
838 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
843 tls_needed = director->tls_enable || director->tls_authenticate;
845 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
846 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
847 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
848 " At least one CA certificate store is required.\n"),
849 director->hdr.name, configfile);
855 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
856 "Without that I don't how to speak to the Director :-(\n"), configfile);
861 /* Loop over Consoles */
862 foreach_res(cons, R_CONSOLE) {
863 /* tls_require implies tls_enable */
864 if (cons->tls_require) {
866 cons->tls_enable = true;
868 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
873 tls_needed = cons->tls_enable || cons->tls_authenticate;
874 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
875 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
876 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
877 cons->hdr.name, configfile);
887 static int versioncmd(FILE *input, BSOCK *UA_sock)
889 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
890 HOST_OS, DISTNAME, DISTVER);
894 static int inputcmd(FILE *input, BSOCK *UA_sock)
899 sendit(_("Too many arguments on input command.\n"));
903 sendit(_("First argument to input command must be a filename.\n"));
906 fd = fopen(argk[1], "rb");
909 senditf(_("Cannot open file %s for input. ERR=%s\n"),
910 argk[1], be.bstrerror());
913 read_and_process_input(fd, UA_sock);
918 /* Send output to both termina and specified file */
919 static int teecmd(FILE *input, BSOCK *UA_sock)
922 return do_outputcmd(input, UA_sock);
925 /* Send output to specified "file" */
926 static int outputcmd(FILE *input, BSOCK *UA_sock)
929 return do_outputcmd(input, UA_sock);
933 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
936 const char *mode = "a+b";
939 sendit(_("Too many arguments on output/tee command.\n"));
943 if (output != stdout) {
953 fd = fopen(argk[1], mode);
956 senditf(_("Cannot open file %s for output. ERR=%s\n"),
957 argk[1], be.bstrerror(errno));
965 * exec "some-command" [wait-seconds]
967 static int execcmd(FILE *input, BSOCK *UA_sock)
975 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
979 wait = atoi(argk[2]);
981 bpipe = open_bpipe(argk[1], wait, "r");
984 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
985 argk[1], be.bstrerror(errno));
989 while (fgets(line, sizeof(line), bpipe->rfd)) {
992 stat = close_bpipe(bpipe);
996 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1002 static int echocmd(FILE *input, BSOCK *UA_sock)
1004 for (int i=1; i < argc; i++) {
1005 senditf("%s", argk[i]);
1012 static int quitcmd(FILE *input, BSOCK *UA_sock)
1017 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1020 sleep(atoi(argk[1]));
1026 static int timecmd(FILE *input, BSOCK *UA_sock)
1029 time_t ttime = time(NULL);
1031 (void)localtime_r(&ttime, &tm);
1032 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1038 * Send a line to the output file and or the terminal
1040 void senditf(const char *fmt,...)
1045 va_start(arg_ptr, fmt);
1046 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1051 void sendit(const char *buf)
1055 if (output == stdout || teeout) {
1058 * Here, we convert every \n into \r\n because the
1059 * terminal is in raw mode when we are using
1062 for (p=q=buf; (p=strchr(q, '\n')); ) {
1065 memcpy(obuf, q, len);
1067 memcpy(obuf+len, "\r\n", 3);
1068 q = ++p; /* point after \n */
1069 fputs(obuf, output);
1076 if (output != stdout) {