2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 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 three of the GNU Affero 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 Affero 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 Kern Sibbald.
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
37 #include "console_conf.h"
47 #define con_set_zed_keys();
53 #if defined(HAVE_WIN32)
54 #define isatty(fd) (fd==0)
57 /* Exported variables */
59 //extern int rl_catch_signals;
61 /* Imported functions */
62 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
63 extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
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;
88 static int timeout = 0;
92 static char *argk[MAX_CMD_ARGS];
93 static char *argv[MAX_CMD_ARGS];
94 static CONFIG *config;
97 /* Command prototypes */
98 static int versioncmd(FILE *input, BSOCK *UA_sock);
99 static int inputcmd(FILE *input, BSOCK *UA_sock);
100 static int outputcmd(FILE *input, BSOCK *UA_sock);
101 static int teecmd(FILE *input, BSOCK *UA_sock);
102 static int quitcmd(FILE *input, BSOCK *UA_sock);
103 static int helpcmd(FILE *input, BSOCK *UA_sock);
104 static int echocmd(FILE *input, BSOCK *UA_sock);
105 static int timecmd(FILE *input, BSOCK *UA_sock);
106 static int sleepcmd(FILE *input, BSOCK *UA_sock);
107 static int execcmd(FILE *input, BSOCK *UA_sock);
109 static int eolcmd(FILE *input, BSOCK *UA_sock);
111 # ifndef HAVE_REGEX_H
112 # include "lib/bregex.h"
120 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
126 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
127 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
128 " -D <dir> select a Director\n"
129 " -l list Directors defined\n"
130 " -c <file> set configuration file to file\n"
131 " -d <nn> set debug level to <nn>\n"
132 " -dt print timestamp in debug output\n"
135 " -u <nn> set command execution timeout to <nn> seconds\n"
136 " -t test - read configuration and exit\n"
137 " -? print this message.\n"
138 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
143 void got_sigstop(int sig)
149 void got_sigcontinue(int sig)
155 void got_sigtout(int sig)
157 // printf("Got tout\n");
161 void got_sigtin(int sig)
163 // printf("Got tin\n");
167 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
174 * These are the @command
176 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
177 static struct cmdstruct commands[] = {
178 { N_("input"), inputcmd, _("input from file")},
179 { N_("output"), outputcmd, _("output to file")},
180 { N_("quit"), quitcmd, _("quit")},
181 { N_("tee"), teecmd, _("output to file and terminal")},
182 { N_("sleep"), sleepcmd, _("sleep specified time")},
183 { N_("time"), timecmd, _("print current time")},
184 { N_("version"), versioncmd, _("print Console's version")},
185 { N_("echo"), echocmd, _("echo command string")},
186 { N_("exec"), execcmd, _("execute an external command")},
187 { N_("exit"), quitcmd, _("exit = quit")},
188 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
189 { N_("help"), helpcmd, _("help listing")},
191 { N_("separator"), eolcmd, _("set command separator")},
194 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
196 static int do_a_command(FILE *input, BSOCK *UA_sock)
207 Dmsg1(120, "Command: %s\n", UA_sock->msg);
213 if (*cmd == '#') { /* comment */
217 for (i=0; i<comsize; i++) { /* search for command */
218 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
219 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
225 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
226 UA_sock->msglen = strlen(UA_sock->msg);
227 sendit(UA_sock->msg);
233 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
235 const char *prompt = "*";
236 bool at_prompt = false;
237 int tty_input = isatty(fileno(input));
242 if (at_prompt) { /* don't prompt multiple times */
249 stat = get_cmd(input, prompt, UA_sock, 30);
257 /* Reading input from a file */
258 int len = sizeof_pool_memory(UA_sock->msg) - 1;
262 if (fgets(UA_sock->msg, len, input) == NULL) {
265 sendit(UA_sock->msg); /* echo to terminal */
266 strip_trailing_junk(UA_sock->msg);
267 UA_sock->msglen = strlen(UA_sock->msg);
272 break; /* error or interrupt */
273 } else if (stat == 0) { /* timeout */
274 if (strcmp(prompt, "*") == 0) {
275 tid = start_bsock_timer(UA_sock, timeout);
276 UA_sock->fsend(".messages");
277 stop_bsock_timer(tid);
283 /* @ => internal command for us */
284 if (UA_sock->msg[0] == '@') {
285 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
286 if (!do_a_command(input, UA_sock)) {
291 tid = start_bsock_timer(UA_sock, timeout);
292 if (!UA_sock->send()) { /* send command */
293 stop_bsock_timer(tid);
296 stop_bsock_timer(tid);
298 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
301 tid = start_bsock_timer(UA_sock, timeout);
302 while ((stat = UA_sock->recv()) >= 0) {
309 /* Suppress output if running in background or user hit ctl-c */
310 if (!stop && !usrbrk()) {
311 sendit(UA_sock->msg);
314 stop_bsock_timer(tid);
323 if (is_bnet_stop(UA_sock)) {
324 break; /* error or term */
325 } else if (stat == BNET_SIGNAL) {
326 if (UA_sock->msglen == BNET_SUB_PROMPT) {
329 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
335 * Call-back for reading a passphrase for an encrypted PEM file
336 * This function uses getpass(),
337 * which uses a static buffer and is NOT thread-safe.
339 static int tls_pem_callback(char *buf, int size, const void *userdata)
342 const char *prompt = (const char *)userdata;
343 # if defined(HAVE_WIN32)
345 if (win32_cgets(buf, size) == NULL) {
354 passwd = getpass(prompt);
355 bstrncpy(buf, passwd, size);
365 #define READLINE_LIBRARY 1
366 #include "readline.h"
369 /* Get the first keyword of the line */
375 char *first_space = strchr(rl_line_buffer, ' ');
377 len = first_space - rl_line_buffer;
378 ret = (char *) malloc((len + 1) * sizeof(char));
379 memcpy(ret, rl_line_buffer, len);
386 * Return the command before the current point.
387 * Set nb to the number of command to skip
390 get_previous_keyword(int current_point, int nb)
392 int i, end=-1, start, inquotes=0;
396 /* first we look for a space before the current word */
397 for (i = current_point; i >= 0; i--) {
398 if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
403 /* find the end of the command */
404 for (; i >= 0; i--) {
405 if (rl_line_buffer[i] != ' ') {
411 /* no end of string */
416 /* look for the start of the command */
417 for (start = end; start > 0; start--) {
418 if (rl_line_buffer[start] == '"') {
419 inquotes = !inquotes;
421 if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
424 current_point = start;
428 s = (char *)malloc(end - start + 2);
429 memcpy(s, rl_line_buffer + start, end - start + 1);
430 s[end - start + 1] = 0;
432 // printf("=======> %i:%i <%s>\n", start, end, s);
437 /* Simple structure that will contain the completion list */
442 static ItemList *items = NULL;
446 items = (ItemList*) malloc(sizeof(ItemList));
447 memset(items, 0, sizeof(ItemList));
450 items->list.destroy();
456 /* Match a regexp and add the result to the items list
457 * This function is recursive
459 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
463 regmatch_t pmatch[20];
468 rc = regexec(preg, what, nmatch, pmatch, 0);
471 Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n 10 20 30\n", what);
472 Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo);
473 Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo);
474 Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo);
475 Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo);
477 size = pmatch[1].rm_eo - pmatch[1].rm_so;
478 *buf = check_pool_memory_size(*buf, size + 1);
479 memcpy(*buf, what+pmatch[1].rm_so, size);
482 items->list.append(bstrdup(*buf));
483 /* We search for the next keyword in the line */
484 match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf);
488 /* fill the items list with the output of the help command */
489 void get_arguments(const char *what)
496 rc = regcomp(&preg, "(([a-z]+=)|([a-z]+)( |$))", REG_EXTENDED);
501 buf = get_pool_memory(PM_MESSAGE);
502 UA_sock->fsend(".help item=%s", what);
503 while (UA_sock->recv() > 0) {
504 strip_trailing_junk(UA_sock->msg);
505 match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
507 free_pool_memory(buf);
511 /* retreive a simple list (.pool, .client) and store it into items */
512 void get_items(const char *what)
516 UA_sock->fsend("%s", what);
517 while (UA_sock->recv() > 0) {
518 strip_trailing_junk(UA_sock->msg);
519 items->list.append(bstrdup(UA_sock->msg));
525 ITEM_ARG, /* item with simple list like .jobs */
526 ITEM_HELP /* use help item=xxx and detect all arguments */
529 /* Generator function for command completion. STATE lets us know whether
530 * to start from scratch; without any state (i.e. STATE == 0), then we
531 * start at the top of the list.
533 static char *item_generator(const char *text, int state,
534 const char *item, cpl_item_t type)
536 static int list_index, len;
539 /* If this is a new word to complete, initialize now. This includes
540 * saving the length of TEXT for efficiency, and initializing the index
557 /* Return the next name which partially matches from the command list. */
558 while (items && list_index < items->list.size())
560 name = (char *)items->list[list_index];
563 if (strncmp(name, text, len) == 0) {
564 char *ret = (char *) actuallymalloc(strlen(name)+1);
570 /* If no names matched, then return NULL. */
571 return ((char *)NULL);
574 /* gobal variables for the type and the item to search
575 * the readline API doesn' permit to pass user data.
577 static const char *cpl_item;
578 static cpl_item_t cpl_type;
580 static char *cpl_generator(const char *text, int state)
582 return item_generator(text, state, cpl_item, cpl_type);
585 /* this function is used to not use the default filename completion */
586 static char *dummy_completion_function(const char *text, int state)
591 struct cpl_keywords_t {
596 static struct cpl_keywords_t cpl_keywords[] = {
598 {"fileset=", ".fileset" },
599 {"client=", ".client" },
601 {"restore_job=",".jobs type=R" },
602 {"level=", ".level" },
603 {"storage=", ".storage" },
604 {"schedule=", ".schedule" },
605 {"volume=", ".media" },
606 {"oldvolume=", ".media" },
607 {"volstatus=", ".volstatus" },
612 {"unmark", ".lsmark" },
613 {"catalog=", ".catalogs" },
614 {"actiononpurge=", ".actiononpurge" }
616 #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
618 /* Attempt to complete on the contents of TEXT. START and END bound the
619 * region of rl_line_buffer that contains the word to complete. TEXT is
620 * the word to complete. We can use the entire contents of rl_line_buffer
621 * in case we want to do some simple parsing. Return the array of matches,
622 * or NULL if there aren't any.
624 static char **readline_completion(const char *text, int start, int end)
629 matches = (char **)NULL;
631 /* If this word is at the start of the line, then it is a command
632 * to complete. Otherwise it is the name of a file in the current
635 s = get_previous_keyword(start, 0);
636 cmd = get_first_keyword();
638 for (int i=0; i < key_size; i++) {
639 if (!strcasecmp(s, cpl_keywords[i].key)) {
640 cpl_item = cpl_keywords[i].cmd;
642 matches = rl_completion_matches(text, cpl_generator);
648 if (!found) { /* we try to get help with the first command */
650 cpl_type = ITEM_HELP;
651 /* we don't want to append " " at the end */
652 rl_completion_suppress_append=true;
653 matches = rl_completion_matches(text, cpl_generator);
656 } else { /* nothing on the line, display all commands */
657 cpl_item = ".help all";
659 matches = rl_completion_matches(text, cpl_generator);
667 static char eol = '\0';
668 static int eolcmd(FILE *input, BSOCK *UA_sock)
670 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
672 } else if (argc == 1) {
675 sendit(_("Illegal separator character.\n"));
683 * -1 error (must stop)
686 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
688 static char *line = NULL;
689 static char *next = NULL;
690 static int do_history = 0;
695 rl_catch_signals = 0; /* do it ourselves */
696 /* Here, readline does ***real*** malloc
697 * so, be we have to use the real free
699 line = readline((char *)prompt); /* cast needed for old readlines */
701 return -1; /* error return and exit */
703 strip_trailing_junk(line);
708 sendit(_("Command logic problem\n"));
711 return 0; /* No input */
715 * Split "line" into multiple commands separated by the eol character.
716 * Each part is pointed to by "next" until finally it becomes null.
721 next = strchr(command, eol);
726 if (command != line && isatty(fileno(input))) {
727 senditf("%s%s\n", prompt, command);
730 sock->msglen = pm_strcpy(&sock->msg, command);
739 actuallyfree(line); /* allocated by readline() malloc */
745 #else /* no readline, do it ourselves */
748 static bool bisatty(int fd)
758 * Returns: 1 if data available
763 wait_for_data(int fd, int sec)
765 #if defined(HAVE_WIN32)
775 FD_SET((unsigned)fd, &fdset);
776 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
777 case 0: /* timeout */
780 if (errno == EINTR || errno == EAGAIN) {
783 return -1; /* error return */
792 * Get next input command from terminal.
794 * Returns: 1 if got input
799 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
803 if (output == stdout || teeout) {
808 switch (wait_for_data(fileno(input), sec)) {
810 return 0; /* timeout */
812 return -1; /* error */
814 len = sizeof_pool_memory(sock->msg) - 1;
820 if (bisatty(fileno(input))) {
821 input_line(sock->msg, len);
825 #ifdef HAVE_WIN32 /* use special console for input on win32 */
826 if (input == stdin) {
827 if (win32_cgets(sock->msg, len) == NULL) {
833 if (fgets(sock->msg, len, input) == NULL) {
842 strip_trailing_junk(sock->msg);
843 sock->msglen = strlen(sock->msg);
847 #endif /* ! HAVE_READLINE */
850 static int console_update_history(const char *histfile)
856 * first, try to truncate the history file, and if it
857 * fails, the file is probably not present, and we
858 * can use write_history to create it
861 if (history_truncate_file(histfile, 100) == 0) {
862 ret = append_history(history_length, histfile);
864 ret = write_history(histfile);
871 static int console_init_history(const char *histfile)
877 ret = read_history(histfile);
878 /* Tell the completer that we want a complete . */
879 rl_completion_entry_function = dummy_completion_function;
880 rl_attempted_completion_function = readline_completion;
881 rl_filename_completion_desired = 0;
888 bool select_director(const char *director, DIRRES **ret_dir, CONRES **ret_cons)
890 int numcon=0, numdir=0;
895 struct sockaddr client_addr;
896 memset(&client_addr, 0, sizeof(client_addr));
903 foreach_res(dir, R_DIRECTOR) {
907 foreach_res(cons, R_CONSOLE) {
912 if (numdir == 1) { /* No choose */
913 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
916 if (director) { /* Command line choice overwrite the no choose option */
918 foreach_res(dir, R_DIRECTOR) {
919 if (bstrcmp(dir->hdr.name, director)) {
924 if (!dir) { /* Can't find Director used as argument */
925 senditf(_("Can't find %s in Director list\n"), director);
930 if (!dir) { /* prompt for director */
931 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
933 sendit(_("Available Directors:\n"));
936 foreach_res(dir, R_DIRECTOR) {
937 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name,
938 dir->address, dir->DIRport);
941 if (get_cmd(stdin, _("Select Director by entering a number: "),
944 (void)WSACleanup(); /* Cleanup Windows sockets */
947 if (!is_a_number(UA_sock->msg)) {
948 senditf(_("%s is not a number. You must enter a number between "
950 UA_sock->msg, numdir);
953 item = atoi(UA_sock->msg);
954 if (item < 0 || item > numdir) {
955 senditf(_("You must enter a number between 1 and %d\n"), numdir);
960 for (i=0; i<item; i++) {
961 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
966 /* Look for a console linked to this director */
967 for (i=0; i<numcon; i++) {
968 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
969 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
974 /* Look for the first non-linked console */
976 for (i=0; i<numcon; i++) {
977 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
978 if (cons->director == NULL)
984 /* If no console, take first one */
986 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
996 /*********************************************************************
998 * Main Bacula Console -- User Interface Program
1001 int main(int argc, char *argv[])
1004 char *director=NULL;
1005 bool list_directors=false;
1006 bool no_signals = false;
1007 bool test_config = false;
1011 setlocale(LC_ALL, "");
1012 bindtextdomain("bacula", LOCALEDIR);
1013 textdomain("bacula");
1017 my_name_is(argc, argv, "bconsole");
1018 init_msg(NULL, NULL);
1019 working_directory = "/tmp";
1020 args = get_pool_memory(PM_FNAME);
1022 while ((ch = getopt(argc, argv, "D:lbc:d:nstu:?")) != -1) {
1024 case 'D': /* Director */
1028 director = bstrdup(optarg);
1032 list_directors = true;
1036 case 'c': /* configuration file */
1037 if (configfile != NULL) {
1040 configfile = bstrdup(optarg);
1044 if (*optarg == 't') {
1045 dbg_timestamp = true;
1047 debug_level = atoi(optarg);
1048 if (debug_level <= 0) {
1054 case 'n': /* no conio */
1058 case 's': /* turn off signals */
1067 timeout = atoi(optarg);
1080 init_signals(terminate_console);
1084 #if !defined(HAVE_WIN32)
1085 /* Override Bacula default signals */
1086 signal(SIGQUIT, SIG_IGN);
1087 signal(SIGTSTP, got_sigstop);
1088 signal(SIGCONT, got_sigcontinue);
1089 signal(SIGTTIN, got_sigtin);
1090 signal(SIGTTOU, got_sigtout);
1101 if (configfile == NULL) {
1102 configfile = bstrdup(CONFIG_FILE);
1105 config = new_config_parser();
1106 parse_cons_config(config, configfile, M_ERROR_TERM);
1108 if (init_crypto() != 0) {
1109 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
1112 if (!check_resources()) {
1113 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
1120 if (list_directors) {
1122 foreach_res(dir, R_DIRECTOR) {
1123 senditf("%s\n", dir->hdr.name);
1129 terminate_console(0);
1133 memset(&jcr, 0, sizeof(jcr));
1135 (void)WSA_Init(); /* Initialize Windows sockets */
1137 start_watchdog(); /* Start socket watchdog */
1139 if(!select_director(director, &dir, &cons)) {
1143 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1146 /* Initialize Console TLS context */
1147 if (cons && (cons->tls_enable || cons->tls_require)) {
1148 /* Generate passphrase prompt */
1149 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1151 /* Initialize TLS context:
1152 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1153 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1155 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1156 cons->tls_ca_certdir, cons->tls_certfile,
1157 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1159 if (!cons->tls_ctx) {
1160 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1162 terminate_console(0);
1167 /* Initialize Director TLS context */
1168 if (dir->tls_enable || dir->tls_require) {
1169 /* Generate passphrase prompt */
1170 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1172 /* Initialize TLS context:
1173 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1174 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1175 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1176 dir->tls_ca_certdir, dir->tls_certfile,
1177 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1179 if (!dir->tls_ctx) {
1180 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1182 terminate_console(0);
1187 if (dir->heartbeat_interval) {
1188 heart_beat = dir->heartbeat_interval;
1190 heart_beat = cons->heartbeat_interval;
1194 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1195 NULL, dir->DIRport, 0);
1196 if (UA_sock == NULL) {
1197 terminate_console(0);
1200 jcr.dir_bsock = UA_sock;
1202 /* If cons==NULL, default console will be used */
1203 if (!authenticate_director(&jcr, dir, cons)) {
1204 terminate_console(0);
1208 Dmsg0(40, "Opened connection with Director daemon\n");
1210 sendit(_("Enter a period to cancel a command.\n"));
1212 /* Read/Update history file if HOME exists */
1213 POOL_MEM history_file;
1215 /* Run commands in ~/.bconsolerc if any */
1216 char *env = getenv("HOME");
1219 pm_strcpy(&UA_sock->msg, env);
1220 pm_strcat(&UA_sock->msg, "/.bconsolerc");
1221 fd = fopen(UA_sock->msg, "rb");
1223 read_and_process_input(fd, UA_sock);
1227 pm_strcpy(history_file, env);
1228 pm_strcat(history_file, "/.bconsole_history");
1229 console_init_history(history_file.c_str());
1232 read_and_process_input(stdin, UA_sock);
1235 UA_sock->signal(BNET_TERMINATE); /* send EOF */
1240 console_update_history(history_file.c_str());
1243 terminate_console(0);
1247 /* Cleanup and then exit */
1248 static void terminate_console(int sig)
1251 static bool already_here = false;
1253 if (already_here) { /* avoid recursive temination problems */
1256 already_here = true;
1258 config->free_resources();
1262 free_pool_memory(args);
1266 (void)WSACleanup(); /* Cleanup Windows sockets */
1267 lmgr_cleanup_main();
1276 * Make a quick check to see that we have all the
1279 static int check_resources()
1288 foreach_res(director, R_DIRECTOR) {
1291 /* tls_require implies tls_enable */
1292 if (director->tls_require) {
1294 director->tls_enable = true;
1296 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1301 tls_needed = director->tls_enable || director->tls_authenticate;
1303 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1304 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1305 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1306 " At least one CA certificate store is required.\n"),
1307 director->hdr.name, configfile);
1313 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1314 "Without that I don't how to speak to the Director :-(\n"), configfile);
1319 /* Loop over Consoles */
1320 foreach_res(cons, R_CONSOLE) {
1321 /* tls_require implies tls_enable */
1322 if (cons->tls_require) {
1324 cons->tls_enable = true;
1326 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1331 tls_needed = cons->tls_enable || cons->tls_authenticate;
1332 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1333 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1334 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1335 cons->hdr.name, configfile);
1346 static int versioncmd(FILE *input, BSOCK *UA_sock)
1348 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1349 HOST_OS, DISTNAME, DISTVER);
1353 /* @input <input-filename> */
1354 static int inputcmd(FILE *input, BSOCK *UA_sock)
1359 sendit(_("Too many arguments on input command.\n"));
1363 sendit(_("First argument to input command must be a filename.\n"));
1366 fd = fopen(argk[1], "rb");
1369 senditf(_("Cannot open file %s for input. ERR=%s\n"),
1370 argk[1], be.bstrerror());
1373 read_and_process_input(fd, UA_sock);
1378 /* @tee <output-filename> */
1379 /* Send output to both terminal and specified file */
1380 static int teecmd(FILE *input, BSOCK *UA_sock)
1383 return do_outputcmd(input, UA_sock);
1386 /* @output <output-filename> */
1387 /* Send output to specified "file" */
1388 static int outputcmd(FILE *input, BSOCK *UA_sock)
1391 return do_outputcmd(input, UA_sock);
1395 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1398 const char *mode = "a+b";
1401 sendit(_("Too many arguments on output/tee command.\n"));
1405 if (output != stdout) {
1415 fd = fopen(argk[1], mode);
1418 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1419 argk[1], be.bstrerror(errno));
1427 * @exec "some-command" [wait-seconds]
1429 static int execcmd(FILE *input, BSOCK *UA_sock)
1437 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1441 wait = atoi(argk[2]);
1443 bpipe = open_bpipe(argk[1], wait, "r");
1446 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1447 argk[1], be.bstrerror(errno));
1451 while (fgets(line, sizeof(line), bpipe->rfd)) {
1452 senditf("%s", line);
1454 stat = close_bpipe(bpipe);
1458 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1465 static int echocmd(FILE *input, BSOCK *UA_sock)
1467 for (int i=1; i < argc; i++) {
1468 senditf("%s ", argk[i]);
1475 static int quitcmd(FILE *input, BSOCK *UA_sock)
1481 static int helpcmd(FILE *input, BSOCK *UA_sock)
1484 for (i=0; i<comsize; i++) {
1485 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1492 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1495 sleep(atoi(argk[1]));
1501 static int timecmd(FILE *input, BSOCK *UA_sock)
1504 time_t ttime = time(NULL);
1506 (void)localtime_r(&ttime, &tm);
1507 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1513 * Send a line to the output file and or the terminal
1515 void senditf(const char *fmt,...)
1520 va_start(arg_ptr, fmt);
1521 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1526 void sendit(const char *buf)
1530 if (output == stdout || teeout) {
1533 * Here, we convert every \n into \r\n because the
1534 * terminal is in raw mode when we are using
1537 for (p=q=buf; (p=strchr(q, '\n')); ) {
1540 memcpy(obuf, q, len);
1542 memcpy(obuf+len, "\r\n", 3);
1543 q = ++p; /* point after \n */
1544 fputs(obuf, output);
1551 if (output != stdout) {