2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Bacula Console interface to the Director
24 * Kern Sibbald, September MM
29 #include "console_conf.h"
39 #define con_set_zed_keys();
45 #if defined(HAVE_WIN32)
46 #define isatty(fd) (fd==0)
49 /* Exported variables */
51 //extern int rl_catch_signals;
53 /* Imported functions */
54 int authenticate_director(BSOCK *dir, DIRRES *director, CONRES *cons);
55 extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
57 /* Forward referenced functions */
58 static void terminate_console(int sig);
59 static int check_resources();
60 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
61 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
62 void senditf(const char *fmt, ...);
63 void sendit(const char *buf);
65 extern "C" void got_sigstop(int sig);
66 extern "C" void got_sigcontinue(int sig);
67 extern "C" void got_sigtout(int sig);
68 extern "C" void got_sigtin(int sig);
71 /* Static variables */
72 static char *configfile = NULL;
73 static BSOCK *UA_sock = NULL;
74 static DIRRES *dir = NULL;
75 static CONRES *cons = NULL;
76 static FILE *output = stdout;
77 static bool teeout = false; /* output 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 quitcmd(FILE *input, BSOCK *UA_sock);
95 static int helpcmd(FILE *input, BSOCK *UA_sock);
96 static int echocmd(FILE *input, BSOCK *UA_sock);
97 static int timecmd(FILE *input, BSOCK *UA_sock);
98 static int sleepcmd(FILE *input, BSOCK *UA_sock);
99 static int execcmd(FILE *input, BSOCK *UA_sock);
102 static int eolcmd(FILE *input, BSOCK *UA_sock);
104 # ifndef HAVE_REGEX_H
105 # include "lib/bregex.h"
113 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
119 "\n%sVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
120 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
121 " -D <dir> select a Director\n"
122 " -l list Directors defined\n"
123 " -c <file> set configuration file to file\n"
124 " -d <nn> set debug level to <nn>\n"
125 " -dt print timestamp in debug output\n"
128 " -u <nn> set command execution timeout to <nn> seconds\n"
129 " -t test - read configuration and exit\n"
130 " -? print this message.\n"
131 "\n"), 2000, "", HOST_OS, DISTNAME, DISTVER);
136 void got_sigstop(int sig)
142 void got_sigcontinue(int sig)
148 void got_sigtout(int sig)
150 // printf("Got tout\n");
154 void got_sigtin(int sig)
156 // printf("Got tin\n");
160 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
167 * These are the @command
169 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
170 static struct cmdstruct commands[] = {
171 { N_("input"), inputcmd, _("input from file")},
172 { N_("output"), outputcmd, _("output to file")},
173 { N_("quit"), quitcmd, _("quit")},
174 { N_("tee"), teecmd, _("output to file and terminal")},
175 { N_("sleep"), sleepcmd, _("sleep specified time")},
176 { N_("time"), timecmd, _("print current time")},
177 { N_("version"), versioncmd, _("print Console's version")},
178 { N_("echo"), echocmd, _("echo command string")},
179 { N_("exec"), execcmd, _("execute an external command")},
180 { N_("exit"), quitcmd, _("exit = quit")},
181 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
182 { N_("help"), helpcmd, _("help listing")},
184 { N_("separator"), eolcmd, _("set command separator")},
187 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
189 static int do_a_command(FILE *input, BSOCK *UA_sock)
200 Dmsg1(120, "Command: %s\n", UA_sock->msg);
206 if (*cmd == '#') { /* comment */
210 for (i=0; i<comsize; i++) { /* search for command */
211 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
212 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
218 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
219 UA_sock->msglen = strlen(UA_sock->msg);
220 sendit(UA_sock->msg);
225 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
227 const char *prompt = "*";
228 bool at_prompt = false;
229 int tty_input = isatty(fileno(input));
234 if (at_prompt) { /* don't prompt multiple times */
241 stat = get_cmd(input, prompt, UA_sock, 30);
249 /* Reading input from a file */
253 if (bfgets(UA_sock->msg, input) == NULL) {
256 sendit(UA_sock->msg); /* echo to terminal */
257 strip_trailing_junk(UA_sock->msg);
258 UA_sock->msglen = strlen(UA_sock->msg);
263 break; /* error or interrupt */
264 } else if (stat == 0) { /* timeout */
265 if (strcmp(prompt, "*") == 0) {
266 tid = start_bsock_timer(UA_sock, timeout);
267 UA_sock->fsend(".messages");
268 stop_bsock_timer(tid);
274 /* @ => internal command for us */
275 if (UA_sock->msg[0] == '@') {
276 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
277 if (!do_a_command(input, UA_sock)) {
282 tid = start_bsock_timer(UA_sock, timeout);
283 if (!UA_sock->send()) { /* send command */
284 stop_bsock_timer(tid);
287 stop_bsock_timer(tid);
289 if (strcasecmp(UA_sock->msg, ".quit") == 0 || strcasecmp(UA_sock->msg, ".exit") == 0) {
292 tid = start_bsock_timer(UA_sock, timeout);
294 stat = UA_sock->recv();
306 /* Suppress output if running in background or user hit ctl-c */
307 if (!stop && !usrbrk()) {
308 sendit(UA_sock->msg);
311 stop_bsock_timer(tid);
320 if (UA_sock->is_stop()) {
321 break; /* error or term */
322 } else if (stat == BNET_SIGNAL) {
323 if (UA_sock->msglen == BNET_SUB_PROMPT) {
326 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock->msglen));
332 * Call-back for reading a passphrase for an encrypted PEM file
333 * This function uses getpass(),
334 * which uses a static buffer and is NOT thread-safe.
336 static int tls_pem_callback(char *buf, int size, const void *userdata)
339 const char *prompt = (const char *)userdata;
340 # if defined(HAVE_WIN32)
342 if (win32_cgets(buf, size) == NULL) {
351 passwd = getpass(prompt);
352 bstrncpy(buf, passwd, size);
362 #define READLINE_LIBRARY 1
363 #include "readline.h"
366 /* Get the first keyword of the line */
372 char *first_space = strchr(rl_line_buffer, ' ');
374 len = first_space - rl_line_buffer;
375 ret = (char *) malloc((len + 1) * sizeof(char));
376 memcpy(ret, rl_line_buffer, len);
383 * Return the command before the current point.
384 * Set nb to the number of command to skip
387 get_previous_keyword(int current_point, int nb)
389 int i, end=-1, start, inquotes=0;
393 /* first we look for a space before the current word */
394 for (i = current_point; i >= 0; i--) {
395 if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
400 /* find the end of the command */
401 for (; i >= 0; i--) {
402 if (rl_line_buffer[i] != ' ') {
408 /* no end of string */
413 /* look for the start of the command */
414 for (start = end; start > 0; start--) {
415 if (rl_line_buffer[start] == '"') {
416 inquotes = !inquotes;
418 if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
421 current_point = start;
425 s = (char *)malloc(end - start + 2);
426 memcpy(s, rl_line_buffer + start, end - start + 1);
427 s[end - start + 1] = 0;
429 // printf("=======> %i:%i <%s>\n", start, end, s);
434 /* Simple structure that will contain the completion list */
439 static ItemList *items = NULL;
443 items = (ItemList*) malloc(sizeof(ItemList));
444 memset(items, 0, sizeof(ItemList));
447 items->list.destroy();
453 /* Match a regexp and add the result to the items list
454 * This function is recursive
456 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
460 regmatch_t pmatch[nmatch];
465 rc = regexec(preg, what, nmatch, pmatch, 0);
468 Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n 10 20 30\n", what);
469 Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo);
470 Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo);
471 Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo);
472 Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo);
474 size = pmatch[1].rm_eo - pmatch[1].rm_so;
475 *buf = check_pool_memory_size(*buf, size + 1);
476 memcpy(*buf, what+pmatch[1].rm_so, size);
479 items->list.append(bstrdup(*buf));
480 /* We search for the next keyword in the line */
481 match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf);
485 /* fill the items list with the output of the help command */
486 void get_arguments(const char *what)
493 rc = regcomp(&preg, "(([a-z]+=)|([a-z]+)( |$))", REG_EXTENDED);
498 buf = get_pool_memory(PM_MESSAGE);
499 UA_sock->fsend(".help item=%s", what);
500 while (UA_sock->recv() > 0) {
501 strip_trailing_junk(UA_sock->msg);
502 match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
504 free_pool_memory(buf);
508 /* retreive a simple list (.pool, .client) and store it into items */
509 void get_items(const char *what)
513 UA_sock->fsend("%s", what);
514 while (UA_sock->recv() > 0) {
515 strip_trailing_junk(UA_sock->msg);
516 items->list.append(bstrdup(UA_sock->msg));
522 ITEM_ARG, /* item with simple list like .jobs */
523 ITEM_HELP /* use help item=xxx and detect all arguments */
526 /* Generator function for command completion. STATE lets us know whether
527 * to start from scratch; without any state (i.e. STATE == 0), then we
528 * start at the top of the list.
530 static char *item_generator(const char *text, int state,
531 const char *item, cpl_item_t type)
533 static int list_index, len;
536 /* If this is a new word to complete, initialize now. This includes
537 * saving the length of TEXT for efficiency, and initializing the index
554 /* Return the next name which partially matches from the command list. */
555 while (items && list_index < items->list.size())
557 name = (char *)items->list[list_index];
560 if (strncmp(name, text, len) == 0) {
561 char *ret = (char *) actuallymalloc(strlen(name)+1);
567 /* If no names matched, then return NULL. */
568 return ((char *)NULL);
571 /* gobal variables for the type and the item to search
572 * the readline API doesn' permit to pass user data.
574 static const char *cpl_item;
575 static cpl_item_t cpl_type;
577 static char *cpl_generator(const char *text, int state)
579 return item_generator(text, state, cpl_item, cpl_type);
582 /* this function is used to not use the default filename completion */
583 static char *dummy_completion_function(const char *text, int state)
588 struct cpl_keywords_t {
593 static struct cpl_keywords_t cpl_keywords[] = {
595 {"fileset=", ".fileset" },
596 {"client=", ".client" },
598 {"restore_job=",".jobs type=R" },
599 {"level=", ".level" },
600 {"storage=", ".storage" },
601 {"schedule=", ".schedule" },
602 {"volume=", ".media" },
603 {"oldvolume=", ".media" },
604 {"volstatus=", ".volstatus" },
609 {"unmark", ".lsmark" },
610 {"catalog=", ".catalogs" },
611 {"actiononpurge=", ".actiononpurge" },
613 {"recylepool=", ".pool" },
614 {"allfrompool=",".pool" }
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 (bfgets(sock->msg, 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 static bool select_director(const char *director, DIRRES **ret_dir, CONRES **ret_cons)
890 int numcon=0, numdir=0;
901 foreach_res(dir, R_DIRECTOR) {
905 foreach_res(cons, R_CONSOLE) {
910 if (numdir == 1) { /* No choose */
911 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
914 if (director) { /* Command line choice overwrite the no choose option */
916 foreach_res(dir, R_DIRECTOR) {
917 if (bstrcasecmp(dir->hdr.name, director)) {
922 if (!dir) { /* Can't find Director used as argument */
923 senditf(_("Can't find %s in Director list\n"), director);
928 if (dir == NULL) { /* prompt for director */
929 UA_sock = new_bsock();
931 sendit(_("Available Directors:\n"));
934 foreach_res(dir, R_DIRECTOR) {
935 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name,
936 dir->address, dir->DIRport);
939 if (get_cmd(stdin, _("Select Director by entering a number: "),
942 (void)WSACleanup(); /* Cleanup Windows sockets */
945 if (!is_a_number(UA_sock->msg)) {
946 senditf(_("%s is not a number. You must enter a number between "
948 UA_sock->msg, numdir);
951 item = atoi(UA_sock->msg);
952 if (item < 0 || item > numdir) {
953 senditf(_("You must enter a number between 1 and %d\n"), numdir);
958 for (i=0; i<item; i++) {
959 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
964 /* Look for a console linked to this director */
965 for (i=0; i<numcon; i++) {
966 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
967 if (cons->director && strcasecmp(cons->director, dir->hdr.name) == 0) {
970 if (i == (numcon - 1)) {
975 /* Look for the first non-linked console */
977 for (i=0; i<numcon; i++) {
978 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
979 if (cons->director == NULL) {
982 if (i == (numcon - 1)) {
988 /* If no console, take first one */
990 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
1000 /*********************************************************************
1002 * Main Bacula Console -- User Interface Program
1005 int main(int argc, char *argv[])
1008 char *director = NULL;
1009 bool list_directors=false;
1010 bool no_signals = false;
1011 bool test_config = false;
1015 setlocale(LC_ALL, "");
1016 bindtextdomain("bacula", LOCALEDIR);
1017 textdomain("bacula");
1021 my_name_is(argc, argv, "bconsole");
1022 init_msg(NULL, NULL);
1023 working_directory = "/tmp";
1024 args = get_pool_memory(PM_FNAME);
1026 while ((ch = getopt(argc, argv, "D:lc:d:nstu:?")) != -1) {
1028 case 'D': /* Director */
1032 director = bstrdup(optarg);
1036 list_directors = true;
1040 case 'c': /* configuration file */
1041 if (configfile != NULL) {
1044 configfile = bstrdup(optarg);
1048 if (*optarg == 't') {
1049 dbg_timestamp = true;
1051 debug_level = atoi(optarg);
1052 if (debug_level <= 0) {
1058 case 'n': /* no conio */
1062 case 's': /* turn off signals */
1071 timeout = atoi(optarg);
1084 init_signals(terminate_console);
1088 #if !defined(HAVE_WIN32)
1089 /* Override Bacula default signals */
1090 signal(SIGQUIT, SIG_IGN);
1091 signal(SIGTSTP, got_sigstop);
1092 signal(SIGCONT, got_sigcontinue);
1093 signal(SIGTTIN, got_sigtin);
1094 signal(SIGTTOU, got_sigtout);
1105 if (configfile == NULL) {
1106 configfile = bstrdup(CONFIG_FILE);
1109 config = new_config_parser();
1110 parse_cons_config(config, configfile, M_ERROR_TERM);
1112 if (init_crypto() != 0) {
1113 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
1116 if (!check_resources()) {
1117 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
1124 if (list_directors) {
1126 foreach_res(dir, R_DIRECTOR) {
1127 senditf("%s\n", dir->hdr.name);
1133 terminate_console(0);
1137 memset(&jcr, 0, sizeof(jcr));
1139 (void)WSA_Init(); /* Initialize Windows sockets */
1141 start_watchdog(); /* Start socket watchdog */
1143 if (!select_director(director, &dir, &cons)) {
1144 terminate_console(0);
1148 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1151 /* Initialize Console TLS context */
1152 if (cons && (cons->tls_enable || cons->tls_require)) {
1153 /* Generate passphrase prompt */
1154 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1156 /* Initialize TLS context:
1157 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1158 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1160 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1161 cons->tls_ca_certdir, cons->tls_certfile,
1162 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1164 if (!cons->tls_ctx) {
1165 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1167 terminate_console(0);
1172 /* Initialize Director TLS context */
1173 if (dir->tls_enable || dir->tls_require) {
1174 /* Generate passphrase prompt */
1175 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1177 /* Initialize TLS context:
1178 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1179 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1180 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1181 dir->tls_ca_certdir, dir->tls_certfile,
1182 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1184 if (!dir->tls_ctx) {
1185 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1187 terminate_console(0);
1192 if (dir->heartbeat_interval) {
1193 heart_beat = dir->heartbeat_interval;
1195 heart_beat = cons->heartbeat_interval;
1200 UA_sock = new_bsock();
1202 if (!UA_sock->connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1203 NULL, dir->DIRport, 0)) {
1206 terminate_console(0);
1209 jcr.dir_bsock = UA_sock;
1211 /* If cons==NULL, default console will be used */
1212 if (!authenticate_director(UA_sock, dir, cons)) {
1213 terminate_console(0);
1217 Dmsg0(40, "Opened connection with Director daemon\n");
1219 sendit(_("Enter a period to cancel a command.\n"));
1221 /* Read/Update history file if HOME exists */
1222 POOL_MEM history_file;
1224 /* Run commands in ~/.bconsolerc if any */
1225 char *env = getenv("HOME");
1228 pm_strcpy(&UA_sock->msg, env);
1229 pm_strcat(&UA_sock->msg, "/.bconsolerc");
1230 fd = fopen(UA_sock->msg, "rb");
1232 read_and_process_input(fd, UA_sock);
1236 pm_strcpy(history_file, env);
1237 pm_strcat(history_file, "/.bconsole_history");
1238 console_init_history(history_file.c_str());
1241 read_and_process_input(stdin, UA_sock);
1244 UA_sock->signal(BNET_TERMINATE); /* send EOF */
1249 console_update_history(history_file.c_str());
1252 terminate_console(0);
1256 /* Cleanup and then exit */
1257 static void terminate_console(int sig)
1260 static bool already_here = false;
1262 if (already_here) { /* avoid recursive temination problems */
1265 already_here = true;
1267 config->free_resources();
1271 free_pool_memory(args);
1275 (void)WSACleanup(); /* Cleanup Windows sockets */
1276 lmgr_cleanup_main();
1285 * Make a quick check to see that we have all the
1288 static int check_resources()
1297 foreach_res(director, R_DIRECTOR) {
1300 /* tls_require implies tls_enable */
1301 if (director->tls_require) {
1303 director->tls_enable = true;
1305 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1310 tls_needed = director->tls_enable || director->tls_authenticate;
1312 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1313 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1314 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1315 " At least one CA certificate store is required.\n"),
1316 director->hdr.name, configfile);
1322 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1323 "Without that I don't how to speak to the Director :-(\n"), configfile);
1328 /* Loop over Consoles */
1329 foreach_res(cons, R_CONSOLE) {
1330 /* tls_require implies tls_enable */
1331 if (cons->tls_require) {
1333 cons->tls_enable = true;
1335 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1340 tls_needed = cons->tls_enable || cons->tls_authenticate;
1341 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1342 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1343 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1344 cons->hdr.name, configfile);
1355 static int versioncmd(FILE *input, BSOCK *UA_sock)
1357 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1358 HOST_OS, DISTNAME, DISTVER);
1362 /* @input <input-filename> */
1363 static int inputcmd(FILE *input, BSOCK *UA_sock)
1368 sendit(_("Too many arguments on input command.\n"));
1372 sendit(_("First argument to input command must be a filename.\n"));
1375 fd = fopen(argk[1], "rb");
1378 senditf(_("Cannot open file %s for input. ERR=%s\n"),
1379 argk[1], be.bstrerror());
1382 read_and_process_input(fd, UA_sock);
1387 /* @tee <output-filename> */
1388 /* Send output to both terminal and specified file */
1389 static int teecmd(FILE *input, BSOCK *UA_sock)
1392 return do_outputcmd(input, UA_sock);
1395 /* @output <output-filename> */
1396 /* Send output to specified "file" */
1397 static int outputcmd(FILE *input, BSOCK *UA_sock)
1400 return do_outputcmd(input, UA_sock);
1404 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1407 const char *mode = "a+b";
1410 sendit(_("Too many arguments on output/tee command.\n"));
1414 if (output != stdout) {
1424 fd = fopen(argk[1], mode);
1427 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1428 argk[1], be.bstrerror(errno));
1436 * @exec "some-command" [wait-seconds]
1438 static int execcmd(FILE *input, BSOCK *UA_sock)
1446 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1450 wait = atoi(argk[2]);
1452 bpipe = open_bpipe(argk[1], wait, "r");
1455 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1456 argk[1], be.bstrerror(errno));
1460 while (fgets(line, sizeof(line), bpipe->rfd)) {
1461 senditf("%s", line);
1463 stat = close_bpipe(bpipe);
1467 senditf(_("@exec error: ERR=%s\n"), be.bstrerror());
1473 static int echocmd(FILE *input, BSOCK *UA_sock)
1475 for (int i=1; i < argc; i++) {
1476 senditf("%s ", argk[i]);
1483 static int quitcmd(FILE *input, BSOCK *UA_sock)
1489 static int helpcmd(FILE *input, BSOCK *UA_sock)
1492 for (i=0; i<comsize; i++) {
1493 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1500 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1503 sleep(atoi(argk[1]));
1509 static int timecmd(FILE *input, BSOCK *UA_sock)
1512 time_t ttime = time(NULL);
1514 (void)localtime_r(&ttime, &tm);
1515 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1521 * Send a line to the output file and or the terminal
1523 void senditf(const char *fmt,...)
1528 va_start(arg_ptr, fmt);
1529 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1534 void sendit(const char *buf)
1538 if (output == stdout || teeout) {
1541 * Here, we convert every \n into \r\n because the
1542 * terminal is in raw mode when we are using
1545 for (p=q=buf; (p=strchr(q, '\n')); ) {
1548 memcpy(obuf, q, len);
1550 memcpy(obuf+len, "\r\n", 3);
1551 q = ++p; /* point after \n */
1552 fputs(obuf, output);
1559 if (output != stdout) {