2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Bacula Console interface to the Director
23 * Kern Sibbald, September MM
28 #include "console_conf.h"
38 #define con_set_zed_keys();
44 #if defined(HAVE_WIN32)
45 #define isatty(fd) (fd==0)
48 /* Exported variables */
50 //extern int rl_catch_signals;
52 /* Imported functions */
53 int authenticate_director(BSOCK *dir, DIRRES *director, CONRES *cons);
54 extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
56 /* Forward referenced functions */
57 static void terminate_console(int sig);
58 static int check_resources();
59 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
60 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
61 void senditf(const char *fmt, ...);
62 void sendit(const char *buf);
64 extern "C" void got_sigstop(int sig);
65 extern "C" void got_sigcontinue(int sig);
66 extern "C" void got_sigtout(int sig);
67 extern "C" void got_sigtin(int sig);
70 /* Static variables */
71 static char *configfile = NULL;
72 static BSOCK *UA_sock = NULL;
73 static DIRRES *dir = NULL;
74 static CONRES *cons = NULL;
75 static FILE *output = stdout;
76 static bool teeout = false; /* output to output and stdout */
77 static bool teein = false; /* input to output and stdout */
78 static bool stop = false;
79 static bool no_conio = false;
80 static int timeout = 0;
84 static char *argk[MAX_CMD_ARGS];
85 static char *argv[MAX_CMD_ARGS];
86 static CONFIG *config;
89 /* Command prototypes */
90 static int versioncmd(FILE *input, BSOCK *UA_sock);
91 static int inputcmd(FILE *input, BSOCK *UA_sock);
92 static int outputcmd(FILE *input, BSOCK *UA_sock);
93 static int teecmd(FILE *input, BSOCK *UA_sock);
94 static int teeallcmd(FILE *input, BSOCK *UA_sock);
95 static int quitcmd(FILE *input, BSOCK *UA_sock);
96 static int helpcmd(FILE *input, BSOCK *UA_sock);
97 static int echocmd(FILE *input, BSOCK *UA_sock);
98 static int timecmd(FILE *input, BSOCK *UA_sock);
99 static int sleepcmd(FILE *input, BSOCK *UA_sock);
100 static int execcmd(FILE *input, BSOCK *UA_sock);
103 static int eolcmd(FILE *input, BSOCK *UA_sock);
105 # ifndef HAVE_REGEX_H
106 # include "lib/bregex.h"
114 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
120 "\n%sVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
121 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
122 " -D <dir> select a Director\n"
123 " -l list Directors defined\n"
124 " -c <file> set configuration file to file\n"
125 " -d <nn> set debug level to <nn>\n"
126 " -dt print timestamp in debug output\n"
129 " -u <nn> set command execution timeout to <nn> seconds\n"
130 " -t test - read configuration and exit\n"
131 " -? print this message.\n"
132 "\n"), 2000, "", HOST_OS, DISTNAME, DISTVER);
137 void got_sigstop(int sig)
143 void got_sigcontinue(int sig)
149 void got_sigtout(int sig)
151 // printf("Got tout\n");
155 void got_sigtin(int sig)
157 // printf("Got tin\n");
161 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
168 * These are the @command
170 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
171 static struct cmdstruct commands[] = {
172 { N_("input"), inputcmd, _("input from file")},
173 { N_("output"), outputcmd, _("output to file")},
174 { N_("quit"), quitcmd, _("quit")},
175 { N_("tee"), teecmd, _("output to file and terminal")},
176 { N_("tall"), teeallcmd, _("output everything to file and terminal (tee all)")},
177 { N_("sleep"), sleepcmd, _("sleep specified time")},
178 { N_("time"), timecmd, _("print current time")},
179 { N_("version"), versioncmd, _("print Console's version")},
180 { N_("echo"), echocmd, _("echo command string")},
181 { N_("exec"), execcmd, _("execute an external command")},
182 { N_("exit"), quitcmd, _("exit = quit")},
183 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
184 { N_("help"), helpcmd, _("help listing")},
186 { N_("separator"), eolcmd, _("set command separator")},
189 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
191 static int do_a_command(FILE *input, BSOCK *UA_sock)
202 Dmsg1(120, "Command: %s\n", UA_sock->msg);
208 if (*cmd == '#') { /* comment */
212 for (i=0; i<comsize; i++) { /* search for command */
213 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
214 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
220 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
221 UA_sock->msglen = strlen(UA_sock->msg);
222 sendit(UA_sock->msg);
227 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
229 const char *prompt = "*";
230 bool at_prompt = false;
231 int tty_input = isatty(fileno(input));
236 if (at_prompt) { /* don't prompt multiple times */
243 stat = get_cmd(input, prompt, UA_sock, 30);
251 /* Reading input from a file */
255 if (bfgets(UA_sock->msg, input) == NULL) {
258 sendit(UA_sock->msg); /* echo to terminal */
259 strip_trailing_junk(UA_sock->msg);
260 UA_sock->msglen = strlen(UA_sock->msg);
265 break; /* error or interrupt */
266 } else if (stat == 0) { /* timeout */
267 if (strcmp(prompt, "*") == 0) {
268 tid = start_bsock_timer(UA_sock, timeout);
269 UA_sock->fsend(".messages");
270 stop_bsock_timer(tid);
276 /* @ => internal command for us */
277 if (UA_sock->msg[0] == '@') {
278 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
279 if (!do_a_command(input, UA_sock)) {
284 tid = start_bsock_timer(UA_sock, timeout);
285 if (!UA_sock->send()) { /* send command */
286 stop_bsock_timer(tid);
289 stop_bsock_timer(tid);
291 if (strcasecmp(UA_sock->msg, ".quit") == 0 || strcasecmp(UA_sock->msg, ".exit") == 0) {
294 tid = start_bsock_timer(UA_sock, timeout);
296 stat = UA_sock->recv();
308 /* Suppress output if running in background or user hit ctl-c */
309 if (!stop && !usrbrk()) {
310 sendit(UA_sock->msg);
313 stop_bsock_timer(tid);
322 if (UA_sock->is_stop()) {
323 break; /* error or term */
324 } else if (stat == BNET_SIGNAL) {
325 if (UA_sock->msglen == BNET_SUB_PROMPT) {
328 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock->msglen));
334 * Call-back for reading a passphrase for an encrypted PEM file
335 * This function uses getpass(),
336 * which uses a static buffer and is NOT thread-safe.
338 static int tls_pem_callback(char *buf, int size, const void *userdata)
341 const char *prompt = (const char *)userdata;
342 # if defined(HAVE_WIN32)
344 if (win32_cgets(buf, size) == NULL) {
353 passwd = getpass(prompt);
354 bstrncpy(buf, passwd, size);
364 #define READLINE_LIBRARY 1
365 #include "readline.h"
368 /* Get the first keyword of the line */
374 char *first_space = strchr(rl_line_buffer, ' ');
376 len = first_space - rl_line_buffer;
377 ret = (char *) malloc((len + 1) * sizeof(char));
378 memcpy(ret, rl_line_buffer, len);
385 * Return the command before the current point.
386 * Set nb to the number of command to skip
389 get_previous_keyword(int current_point, int nb)
391 int i, end=-1, start, inquotes=0;
395 /* first we look for a space before the current word */
396 for (i = current_point; i >= 0; i--) {
397 if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
402 /* find the end of the command */
403 for (; i >= 0; i--) {
404 if (rl_line_buffer[i] != ' ') {
410 /* no end of string */
415 /* look for the start of the command */
416 for (start = end; start > 0; start--) {
417 if (rl_line_buffer[start] == '"') {
418 inquotes = !inquotes;
420 if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
423 current_point = start;
427 s = (char *)malloc(end - start + 2);
428 memcpy(s, rl_line_buffer + start, end - start + 1);
429 s[end - start + 1] = 0;
431 // printf("=======> %i:%i <%s>\n", start, end, s);
436 /* Simple structure that will contain the completion list */
441 static ItemList *items = NULL;
445 items = (ItemList*) malloc(sizeof(ItemList));
446 memset(items, 0, sizeof(ItemList));
449 items->list.destroy();
455 /* Match a regexp and add the result to the items list
456 * This function is recursive
458 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
462 regmatch_t pmatch[nmatch];
467 rc = regexec(preg, what, nmatch, pmatch, 0);
470 Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n 10 20 30\n", what);
471 Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo);
472 Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo);
473 Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo);
474 Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo);
476 size = pmatch[1].rm_eo - pmatch[1].rm_so;
477 *buf = check_pool_memory_size(*buf, size + 1);
478 memcpy(*buf, what+pmatch[1].rm_so, size);
481 items->list.append(bstrdup(*buf));
482 /* We search for the next keyword in the line */
483 match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf);
487 /* fill the items list with the output of the help command */
488 void get_arguments(const char *what)
495 rc = regcomp(&preg, "(([a-z]+=)|([a-z]+)( |$))", REG_EXTENDED);
500 buf = get_pool_memory(PM_MESSAGE);
501 UA_sock->fsend(".help item=%s", what);
502 while (UA_sock->recv() > 0) {
503 strip_trailing_junk(UA_sock->msg);
504 match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
506 free_pool_memory(buf);
510 /* retreive a simple list (.pool, .client) and store it into items */
511 void get_items(const char *what)
515 UA_sock->fsend("%s", what);
516 while (UA_sock->recv() > 0) {
517 strip_trailing_junk(UA_sock->msg);
518 items->list.append(bstrdup(UA_sock->msg));
524 ITEM_ARG, /* item with simple list like .jobs */
525 ITEM_HELP /* use help item=xxx and detect all arguments */
528 /* Generator function for command completion. STATE lets us know whether
529 * to start from scratch; without any state (i.e. STATE == 0), then we
530 * start at the top of the list.
532 static char *item_generator(const char *text, int state,
533 const char *item, cpl_item_t type)
535 static int list_index, len;
538 /* If this is a new word to complete, initialize now. This includes
539 * saving the length of TEXT for efficiency, and initializing the index
556 /* Return the next name which partially matches from the command list. */
557 while (items && list_index < items->list.size())
559 name = (char *)items->list[list_index];
562 if (strncmp(name, text, len) == 0) {
563 char *ret = (char *) actuallymalloc(strlen(name)+1);
569 /* If no names matched, then return NULL. */
570 return ((char *)NULL);
573 /* gobal variables for the type and the item to search
574 * the readline API doesn' permit to pass user data.
576 static const char *cpl_item;
577 static cpl_item_t cpl_type;
579 static char *cpl_generator(const char *text, int state)
581 return item_generator(text, state, cpl_item, cpl_type);
584 /* this function is used to not use the default filename completion */
585 static char *dummy_completion_function(const char *text, int state)
590 struct cpl_keywords_t {
595 static struct cpl_keywords_t cpl_keywords[] = {
597 {"fileset=", ".fileset" },
598 {"client=", ".client" },
600 {"restore_job=",".jobs type=R" },
601 {"level=", ".level" },
602 {"storage=", ".storage" },
603 {"schedule=", ".schedule" },
604 {"volume=", ".media" },
605 {"oldvolume=", ".media" },
606 {"volstatus=", ".volstatus" },
611 {"unmark", ".lsmark" },
612 {"catalog=", ".catalogs" },
613 {"actiononpurge=", ".actiononpurge" },
615 {"recylepool=", ".pool" },
616 {"allfrompool=",".pool" }
618 #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
620 /* Attempt to complete on the contents of TEXT. START and END bound the
621 * region of rl_line_buffer that contains the word to complete. TEXT is
622 * the word to complete. We can use the entire contents of rl_line_buffer
623 * in case we want to do some simple parsing. Return the array of matches,
624 * or NULL if there aren't any.
626 static char **readline_completion(const char *text, int start, int end)
631 matches = (char **)NULL;
633 /* If this word is at the start of the line, then it is a command
634 * to complete. Otherwise it is the name of a file in the current
637 s = get_previous_keyword(start, 0);
638 cmd = get_first_keyword();
640 for (int i=0; i < key_size; i++) {
641 if (!strcasecmp(s, cpl_keywords[i].key)) {
642 cpl_item = cpl_keywords[i].cmd;
644 matches = rl_completion_matches(text, cpl_generator);
650 if (!found) { /* we try to get help with the first command */
652 cpl_type = ITEM_HELP;
653 /* we don't want to append " " at the end */
654 rl_completion_suppress_append=true;
655 matches = rl_completion_matches(text, cpl_generator);
658 } else { /* nothing on the line, display all commands */
659 cpl_item = ".help all";
661 matches = rl_completion_matches(text, cpl_generator);
669 static char eol = '\0';
670 static int eolcmd(FILE *input, BSOCK *UA_sock)
672 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
674 } else if (argc == 1) {
677 sendit(_("Illegal separator character.\n"));
685 * -1 error (must stop)
688 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
690 static char *line = NULL;
691 static char *next = NULL;
692 static int do_history = 0;
697 rl_catch_signals = 0; /* do it ourselves */
698 /* Here, readline does ***real*** malloc
699 * so, be we have to use the real free
701 line = readline((char *)prompt); /* cast needed for old readlines */
703 return -1; /* error return and exit */
705 strip_trailing_junk(line);
710 sendit(_("Command logic problem\n"));
713 return 0; /* No input */
717 * Split "line" into multiple commands separated by the eol character.
718 * Each part is pointed to by "next" until finally it becomes null.
723 next = strchr(command, eol);
728 if (command != line && isatty(fileno(input))) {
729 senditf("%s%s\n", prompt, command);
732 /* Send the intput to the output file if needed */
733 if (teein && output != stdout) {
734 fputs(prompt, output);
735 fputs(command, output);
740 sock->msglen = pm_strcpy(&sock->msg, command);
749 actuallyfree(line); /* allocated by readline() malloc */
755 #else /* no readline, do it ourselves */
758 static bool bisatty(int fd)
768 * Returns: 1 if data available
773 wait_for_data(int fd, int sec)
775 #if defined(HAVE_WIN32)
785 FD_SET((unsigned)fd, &fdset);
786 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
787 case 0: /* timeout */
790 if (errno == EINTR || errno == EAGAIN) {
793 return -1; /* error return */
802 * Get next input command from terminal.
804 * Returns: 1 if got input
809 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
813 if (output == stdout || teeout) {
818 switch (wait_for_data(fileno(input), sec)) {
820 return 0; /* timeout */
822 return -1; /* error */
824 len = sizeof_pool_memory(sock->msg) - 1;
830 if (bisatty(fileno(input))) {
831 input_line(sock->msg, len);
835 #ifdef HAVE_WIN32 /* use special console for input on win32 */
836 if (input == stdin) {
837 if (win32_cgets(sock->msg, len) == NULL) {
843 if (bfgets(sock->msg, input) == NULL) {
852 strip_trailing_junk(sock->msg);
853 sock->msglen = strlen(sock->msg);
855 /* Send input to log file if needed */
856 if (teein && output != stdout) {
857 fputs(sock->msg, output);
864 #endif /* ! HAVE_READLINE */
867 static int console_update_history(const char *histfile)
873 * first, try to truncate the history file, and if it
874 * fails, the file is probably not present, and we
875 * can use write_history to create it
878 if (history_truncate_file(histfile, 100) == 0) {
879 ret = append_history(history_length, histfile);
881 ret = write_history(histfile);
888 static int console_init_history(const char *histfile)
894 ret = read_history(histfile);
895 /* Tell the completer that we want a complete . */
896 rl_completion_entry_function = dummy_completion_function;
897 rl_attempted_completion_function = readline_completion;
898 rl_filename_completion_desired = 0;
905 static bool select_director(const char *director, DIRRES **ret_dir, CONRES **ret_cons)
907 int numcon=0, numdir=0;
918 foreach_res(dir, R_DIRECTOR) {
922 foreach_res(cons, R_CONSOLE) {
927 if (numdir == 1) { /* No choose */
928 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
931 if (director) { /* Command line choice overwrite the no choose option */
933 foreach_res(dir, R_DIRECTOR) {
934 if (bstrcasecmp(dir->hdr.name, director)) {
939 if (!dir) { /* Can't find Director used as argument */
940 senditf(_("Can't find %s in Director list\n"), director);
945 if (dir == NULL) { /* prompt for director */
946 UA_sock = new_bsock();
948 sendit(_("Available Directors:\n"));
951 foreach_res(dir, R_DIRECTOR) {
952 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name,
953 dir->address, dir->DIRport);
956 if (get_cmd(stdin, _("Select Director by entering a number: "),
959 (void)WSACleanup(); /* Cleanup Windows sockets */
962 if (!is_a_number(UA_sock->msg)) {
963 senditf(_("%s is not a number. You must enter a number between "
965 UA_sock->msg, numdir);
968 item = atoi(UA_sock->msg);
969 if (item < 0 || item > numdir) {
970 senditf(_("You must enter a number between 1 and %d\n"), numdir);
975 for (i=0; i<item; i++) {
976 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
981 /* Look for a console linked to this director */
982 for (i=0; i<numcon; i++) {
983 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
984 if (cons->director && strcasecmp(cons->director, dir->hdr.name) == 0) {
987 if (i == (numcon - 1)) {
992 /* Look for the first non-linked console */
994 for (i=0; i<numcon; i++) {
995 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
996 if (cons->director == NULL) {
999 if (i == (numcon - 1)) {
1005 /* If no console, take first one */
1007 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
1017 /*********************************************************************
1019 * Main Bacula Console -- User Interface Program
1022 int main(int argc, char *argv[])
1025 char *director = NULL;
1026 bool list_directors=false;
1027 bool no_signals = false;
1028 bool test_config = false;
1032 setlocale(LC_ALL, "");
1033 bindtextdomain("bacula", LOCALEDIR);
1034 textdomain("bacula");
1038 my_name_is(argc, argv, "bconsole");
1039 init_msg(NULL, NULL);
1040 working_directory = "/tmp";
1041 args = get_pool_memory(PM_FNAME);
1043 while ((ch = getopt(argc, argv, "D:lc:d:nstu:?")) != -1) {
1045 case 'D': /* Director */
1049 director = bstrdup(optarg);
1053 list_directors = true;
1057 case 'c': /* configuration file */
1058 if (configfile != NULL) {
1061 configfile = bstrdup(optarg);
1065 if (*optarg == 't') {
1066 dbg_timestamp = true;
1068 debug_level = atoi(optarg);
1069 if (debug_level <= 0) {
1075 case 'n': /* no conio */
1079 case 's': /* turn off signals */
1088 timeout = atoi(optarg);
1101 init_signals(terminate_console);
1105 #if !defined(HAVE_WIN32)
1106 /* Override Bacula default signals */
1107 signal(SIGQUIT, SIG_IGN);
1108 signal(SIGTSTP, got_sigstop);
1109 signal(SIGCONT, got_sigcontinue);
1110 signal(SIGTTIN, got_sigtin);
1111 signal(SIGTTOU, got_sigtout);
1122 if (configfile == NULL) {
1123 configfile = bstrdup(CONFIG_FILE);
1126 config = new_config_parser();
1127 parse_cons_config(config, configfile, M_ERROR_TERM);
1129 if (init_crypto() != 0) {
1130 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
1133 if (!check_resources()) {
1134 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
1141 if (list_directors) {
1143 foreach_res(dir, R_DIRECTOR) {
1144 senditf("%s\n", dir->hdr.name);
1150 terminate_console(0);
1154 memset(&jcr, 0, sizeof(jcr));
1156 (void)WSA_Init(); /* Initialize Windows sockets */
1158 start_watchdog(); /* Start socket watchdog */
1160 if (!select_director(director, &dir, &cons)) {
1161 terminate_console(0);
1165 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1168 /* Initialize Console TLS context */
1169 if (cons && (cons->tls_enable || cons->tls_require)) {
1170 /* Generate passphrase prompt */
1171 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1173 /* Initialize TLS context:
1174 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1175 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1177 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1178 cons->tls_ca_certdir, cons->tls_certfile,
1179 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1181 if (!cons->tls_ctx) {
1182 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1184 terminate_console(0);
1189 /* Initialize Director TLS context */
1190 if (dir->tls_enable || dir->tls_require) {
1191 /* Generate passphrase prompt */
1192 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1194 /* Initialize TLS context:
1195 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1196 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1197 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1198 dir->tls_ca_certdir, dir->tls_certfile,
1199 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1201 if (!dir->tls_ctx) {
1202 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1204 terminate_console(0);
1209 if (dir->heartbeat_interval) {
1210 heart_beat = dir->heartbeat_interval;
1212 heart_beat = cons->heartbeat_interval;
1217 UA_sock = new_bsock();
1219 if (!UA_sock->connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1220 NULL, dir->DIRport, 0)) {
1223 terminate_console(0);
1226 jcr.dir_bsock = UA_sock;
1228 /* If cons==NULL, default console will be used */
1229 if (!authenticate_director(UA_sock, dir, cons)) {
1230 terminate_console(0);
1234 Dmsg0(40, "Opened connection with Director daemon\n");
1236 sendit(_("Enter a period to cancel a command.\n"));
1238 /* Read/Update history file if HOME exists */
1239 POOL_MEM history_file;
1241 /* Run commands in ~/.bconsolerc if any */
1242 char *env = getenv("HOME");
1245 pm_strcpy(&UA_sock->msg, env);
1246 pm_strcat(&UA_sock->msg, "/.bconsolerc");
1247 fd = fopen(UA_sock->msg, "rb");
1249 read_and_process_input(fd, UA_sock);
1253 pm_strcpy(history_file, env);
1254 pm_strcat(history_file, "/.bconsole_history");
1255 console_init_history(history_file.c_str());
1258 read_and_process_input(stdin, UA_sock);
1261 UA_sock->signal(BNET_TERMINATE); /* send EOF */
1266 console_update_history(history_file.c_str());
1269 terminate_console(0);
1273 /* Cleanup and then exit */
1274 static void terminate_console(int sig)
1277 static bool already_here = false;
1279 if (already_here) { /* avoid recursive temination problems */
1282 already_here = true;
1284 config->free_resources();
1288 free_pool_memory(args);
1292 (void)WSACleanup(); /* Cleanup Windows sockets */
1293 lmgr_cleanup_main();
1302 * Make a quick check to see that we have all the
1305 static int check_resources()
1314 foreach_res(director, R_DIRECTOR) {
1317 /* tls_require implies tls_enable */
1318 if (director->tls_require) {
1320 director->tls_enable = true;
1322 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1327 tls_needed = director->tls_enable || director->tls_authenticate;
1329 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1330 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1331 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1332 " At least one CA certificate store is required.\n"),
1333 director->hdr.name, configfile);
1339 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1340 "Without that I don't how to speak to the Director :-(\n"), configfile);
1345 /* Loop over Consoles */
1346 foreach_res(cons, R_CONSOLE) {
1347 /* tls_require implies tls_enable */
1348 if (cons->tls_require) {
1350 cons->tls_enable = true;
1352 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1357 tls_needed = cons->tls_enable || cons->tls_authenticate;
1358 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1359 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1360 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1361 cons->hdr.name, configfile);
1372 static int versioncmd(FILE *input, BSOCK *UA_sock)
1374 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1375 HOST_OS, DISTNAME, DISTVER);
1379 /* @input <input-filename> */
1380 static int inputcmd(FILE *input, BSOCK *UA_sock)
1385 sendit(_("Too many arguments on input command.\n"));
1389 sendit(_("First argument to input command must be a filename.\n"));
1392 fd = fopen(argk[1], "rb");
1395 senditf(_("Cannot open file %s for input. ERR=%s\n"),
1396 argk[1], be.bstrerror());
1399 read_and_process_input(fd, UA_sock);
1404 /* @tall <output-filename> */
1405 /* Send input/output to both terminal and specified file */
1406 static int teeallcmd(FILE *input, BSOCK *UA_sock)
1410 return do_outputcmd(input, UA_sock);
1413 /* @tee <output-filename> */
1414 /* Send output to both terminal and specified file */
1415 static int teecmd(FILE *input, BSOCK *UA_sock)
1419 return do_outputcmd(input, UA_sock);
1422 /* @output <output-filename> */
1423 /* Send output to specified "file" */
1424 static int outputcmd(FILE *input, BSOCK *UA_sock)
1428 return do_outputcmd(input, UA_sock);
1432 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1435 const char *mode = "a+b";
1438 sendit(_("Too many arguments on output/tee command.\n"));
1442 if (output != stdout) {
1453 fd = fopen(argk[1], mode);
1456 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1457 argk[1], be.bstrerror(errno));
1465 * @exec "some-command" [wait-seconds]
1467 static int execcmd(FILE *input, BSOCK *UA_sock)
1475 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1479 wait = atoi(argk[2]);
1481 bpipe = open_bpipe(argk[1], wait, "r");
1484 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1485 argk[1], be.bstrerror(errno));
1489 while (fgets(line, sizeof(line), bpipe->rfd)) {
1490 senditf("%s", line);
1492 stat = close_bpipe(bpipe);
1496 senditf(_("@exec error: ERR=%s\n"), be.bstrerror());
1502 static int echocmd(FILE *input, BSOCK *UA_sock)
1504 for (int i=1; i < argc; i++) {
1505 senditf("%s ", argk[i]);
1512 static int quitcmd(FILE *input, BSOCK *UA_sock)
1518 static int helpcmd(FILE *input, BSOCK *UA_sock)
1521 for (i=0; i<comsize; i++) {
1522 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1529 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1532 sleep(atoi(argk[1]));
1538 static int timecmd(FILE *input, BSOCK *UA_sock)
1541 time_t ttime = time(NULL);
1543 (void)localtime_r(&ttime, &tm);
1544 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1550 * Send a line to the output file and or the terminal
1552 void senditf(const char *fmt,...)
1557 va_start(arg_ptr, fmt);
1558 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1563 void sendit(const char *buf)
1567 if (output == stdout || teeout) {
1570 * Here, we convert every \n into \r\n because the
1571 * terminal is in raw mode when we are using
1574 for (p=q=buf; (p=strchr(q, '\n')); ) {
1577 memcpy(obuf, q, len);
1579 memcpy(obuf+len, "\r\n", 3);
1580 q = ++p; /* point after \n */
1581 fputs(obuf, output);
1588 if (output != stdout) {