2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2018 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"
32 #if defined(HAVE_CONIO)
35 #else /* defined(HAVE_READLINE) || "DUMB" */
38 #define con_set_zed_keys();
44 static int brkflg = 0; /* set on user break */
46 #if defined(HAVE_WIN32)
47 #define isatty(fd) (fd==0)
50 /* Exported variables */
52 //extern int rl_catch_signals;
54 /* Imported functions */
55 int authenticate_director(BSOCK *dir, DIRRES *director, CONRES *cons);
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 teein = false; /* input to output and stdout */
79 static bool stop = false;
80 static bool no_conio = false;
81 static int timeout = 0;
85 static char *argk[MAX_CMD_ARGS];
86 static char *argv[MAX_CMD_ARGS];
87 static CONFIG *config;
90 /* Command prototypes */
91 static int versioncmd(FILE *input, BSOCK *UA_sock);
92 static int inputcmd(FILE *input, BSOCK *UA_sock);
93 static int outputcmd(FILE *input, BSOCK *UA_sock);
94 static int teecmd(FILE *input, BSOCK *UA_sock);
95 static int teeallcmd(FILE *input, BSOCK *UA_sock);
96 static int quitcmd(FILE *input, BSOCK *UA_sock);
97 static int helpcmd(FILE *input, BSOCK *UA_sock);
98 static int echocmd(FILE *input, BSOCK *UA_sock);
99 static int timecmd(FILE *input, BSOCK *UA_sock);
100 static int sleepcmd(FILE *input, BSOCK *UA_sock);
101 static int execcmd(FILE *input, BSOCK *UA_sock);
102 static int putfilecmd(FILE *input, BSOCK *UA_sock);
105 static int eolcmd(FILE *input, BSOCK *UA_sock);
107 # ifndef HAVE_REGEX_H
108 # include "lib/bregex.h"
116 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
122 "\n%sVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
123 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
124 " -D <dir> select a Director\n"
125 " -l list Directors defined\n"
126 " -L list Consoles defined\n"
127 " -C <cons> select a console\n"
128 " -c <file> set configuration file to file\n"
129 " -d <nn> set debug level to <nn>\n"
130 " -dt print timestamp in debug output\n"
133 " -u <nn> set command execution timeout to <nn> seconds\n"
134 " -t test - read configuration and exit\n"
135 " -? print this message.\n"
136 "\n"), 2000, BDEMO, HOST_OS, DISTNAME, DISTVER);
141 void got_sigstop(int sig)
147 void got_sigcontinue(int sig)
153 void got_sigtout(int sig)
155 // printf("Got tout\n");
159 void got_sigtin(int sig)
161 // printf("Got tin\n");
165 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
172 * These are the @command
174 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
175 static struct cmdstruct commands[] = {
176 { N_("input"), inputcmd, _("input from file")},
177 { N_("output"), outputcmd, _("output to file")},
178 { N_("quit"), quitcmd, _("quit")},
179 { N_("tee"), teecmd, _("output to file and terminal")},
180 { N_("tall"), teeallcmd, _("output everything to file and terminal (tee all)")},
181 { N_("sleep"), sleepcmd, _("sleep specified time")},
182 { N_("time"), timecmd, _("print current time")},
183 { N_("version"), versioncmd, _("print Console's version")},
184 { N_("echo"), echocmd, _("echo command string")},
185 { N_("exec"), execcmd, _("execute an external command")},
186 { N_("exit"), quitcmd, _("exit = quit")},
187 { N_("putfile"), putfilecmd, _("send a file to the director")},
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);
232 /* When getting .api command, we can ignore some signals, so we set
235 static bool api_mode=false;
237 static bool ignore_signal(int stat, BSOCK *s)
239 /* Not in API mode */
249 /* List signal that should not stop the read loop */
250 Dmsg1(100, "Got signal %s\n", bnet_sig_to_ascii(s->msglen));
253 case BNET_CMD_FAILED: /* might want to print **ERROR** */
254 case BNET_CMD_OK: /* might want to print **OK** */
255 case BNET_MSGS_PENDING:
261 /* The signal should break the read loop */
265 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
267 const char *prompt = "*";
268 bool at_prompt = false;
269 int tty_input = isatty(fileno(input));
274 if (at_prompt) { /* don't prompt multiple times */
281 stat = get_cmd(input, prompt, UA_sock, 30);
289 /* Reading input from a file */
293 if (bfgets(UA_sock->msg, input) == NULL) {
296 sendit(UA_sock->msg); /* echo to terminal */
297 strip_trailing_junk(UA_sock->msg);
298 UA_sock->msglen = strlen(UA_sock->msg);
303 break; /* error or interrupt */
304 } else if (stat == 0) { /* timeout */
305 if (strcmp(prompt, "*") == 0) {
306 tid = start_bsock_timer(UA_sock, timeout);
307 UA_sock->fsend(".messages");
308 stop_bsock_timer(tid);
314 /* @ => internal command for us */
315 if (UA_sock->msg[0] == '@') {
316 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
317 if (!do_a_command(input, UA_sock)) {
322 tid = start_bsock_timer(UA_sock, timeout);
323 if (!UA_sock->send()) { /* send command */
324 stop_bsock_timer(tid);
327 stop_bsock_timer(tid);
329 if (strncasecmp(UA_sock->msg, ".api", 4) == 0) {
332 if (strcasecmp(UA_sock->msg, ".quit") == 0 || strcasecmp(UA_sock->msg, ".exit") == 0) {
335 tid = start_bsock_timer(UA_sock, timeout);
337 stat = UA_sock->recv();
338 if (ignore_signal(stat, UA_sock)) {
352 /* Suppress output if running in background or user hit ctl-c */
353 if (!stop && !usrbrk()) {
354 sendit(UA_sock->msg);
357 stop_bsock_timer(tid);
366 if (UA_sock->is_stop()) {
367 break; /* error or term */
368 } else if (stat == BNET_SIGNAL) {
369 if (UA_sock->msglen == BNET_SUB_PROMPT) {
372 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock->msglen));
378 * Call-back for reading a passphrase for an encrypted PEM file
379 * This function uses getpass(),
380 * which uses a static buffer and is NOT thread-safe.
382 static int tls_pem_callback(char *buf, int size, const void *userdata)
385 const char *prompt = (const char *)userdata;
386 # if defined(HAVE_WIN32)
388 if (win32_cgets(buf, size) == NULL) {
397 passwd = getpass(prompt);
398 bstrncpy(buf, passwd, size);
408 #define READLINE_LIBRARY 1
409 #include "readline.h"
412 /* Get the first keyword of the line */
418 char *first_space = strchr(rl_line_buffer, ' ');
420 len = first_space - rl_line_buffer;
421 ret = (char *) malloc((len + 1) * sizeof(char));
422 memcpy(ret, rl_line_buffer, len);
429 * Return the command before the current point.
430 * Set nb to the number of command to skip
433 get_previous_keyword(int current_point, int nb)
435 int i, end=-1, start, inquotes=0;
439 /* first we look for a space before the current word */
440 for (i = current_point; i >= 0; i--) {
441 if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
446 /* find the end of the command */
447 for (; i >= 0; i--) {
448 if (rl_line_buffer[i] != ' ') {
454 /* no end of string */
459 /* look for the start of the command */
460 for (start = end; start > 0; start--) {
461 if (rl_line_buffer[start] == '"') {
462 inquotes = !inquotes;
464 if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
467 current_point = start;
471 s = (char *)malloc(end - start + 2);
472 memcpy(s, rl_line_buffer + start, end - start + 1);
473 s[end - start + 1] = 0;
475 // printf("=======> %i:%i <%s>\n", start, end, s);
480 /* Simple structure that will contain the completion list */
485 static ItemList *items = NULL;
489 items = (ItemList*) malloc(sizeof(ItemList));
490 memset(items, 0, sizeof(ItemList));
493 items->list.destroy();
499 /* Match a regexp and add the result to the items list
500 * This function is recursive
502 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
506 regmatch_t pmatch[nmatch];
511 rc = regexec(preg, what, nmatch, pmatch, 0);
514 Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n 10 20 30\n", what);
515 Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo);
516 Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo);
517 Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo);
518 Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo);
520 size = pmatch[1].rm_eo - pmatch[1].rm_so;
521 *buf = check_pool_memory_size(*buf, size + 1);
522 memcpy(*buf, what+pmatch[1].rm_so, size);
525 items->list.append(bstrdup(*buf));
526 /* We search for the next keyword in the line */
527 match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf);
531 /* fill the items list with the output of the help command */
532 void get_arguments(const char *what)
539 rc = regcomp(&preg, "(([a-z]+=)|([a-z]+)( |$))", REG_EXTENDED);
544 buf = get_pool_memory(PM_MESSAGE);
545 UA_sock->fsend(".help item=%s", what);
546 while (UA_sock->recv() > 0) {
547 strip_trailing_junk(UA_sock->msg);
548 match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
550 free_pool_memory(buf);
554 /* retreive a simple list (.pool, .client) and store it into items */
555 void get_items(const char *what)
559 UA_sock->fsend("%s", what);
560 while (UA_sock->recv() > 0) {
561 strip_trailing_junk(UA_sock->msg);
562 items->list.append(bstrdup(UA_sock->msg));
568 ITEM_ARG, /* item with simple list like .jobs */
569 ITEM_HELP /* use help item=xxx and detect all arguments */
572 /* Generator function for command completion. STATE lets us know whether
573 * to start from scratch; without any state (i.e. STATE == 0), then we
574 * start at the top of the list.
576 static char *item_generator(const char *text, int state,
577 const char *item, cpl_item_t type)
579 static int list_index, len;
582 /* If this is a new word to complete, initialize now. This includes
583 * saving the length of TEXT for efficiency, and initializing the index
600 /* Return the next name which partially matches from the command list. */
601 while (items && list_index < items->list.size())
603 name = (char *)items->list[list_index];
606 if (strncmp(name, text, len) == 0) {
607 char *ret = (char *) actuallymalloc(strlen(name)+1);
613 /* If no names matched, then return NULL. */
614 return ((char *)NULL);
617 /* gobal variables for the type and the item to search
618 * the readline API doesn' permit to pass user data.
620 static const char *cpl_item;
621 static cpl_item_t cpl_type;
623 static char *cpl_generator(const char *text, int state)
625 return item_generator(text, state, cpl_item, cpl_type);
628 /* this function is used to not use the default filename completion */
629 static char *dummy_completion_function(const char *text, int state)
634 struct cpl_keywords_t {
639 static struct cpl_keywords_t cpl_keywords[] = {
641 {"fileset=", ".fileset" },
642 {"client=", ".client" },
644 {"restore_job=",".jobs type=R" },
645 {"level=", ".level" },
646 {"storage=", ".storage" },
647 {"schedule=", ".schedule" },
648 {"volume=", ".media" },
649 {"oldvolume=", ".media" },
650 {"volstatus=", ".volstatus" },
655 {"unmark", ".lsmark" },
656 {"catalog=", ".catalogs" },
657 {"actiononpurge=", ".actiononpurge" },
659 {"recylepool=", ".pool" },
660 {"allfrompool=",".pool" }
662 #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
664 /* Attempt to complete on the contents of TEXT. START and END bound the
665 * region of rl_line_buffer that contains the word to complete. TEXT is
666 * the word to complete. We can use the entire contents of rl_line_buffer
667 * in case we want to do some simple parsing. Return the array of matches,
668 * or NULL if there aren't any.
670 static char **readline_completion(const char *text, int start, int end)
675 matches = (char **)NULL;
677 /* If this word is at the start of the line, then it is a command
678 * to complete. Otherwise it is the name of a file in the current
681 s = get_previous_keyword(start, 0);
682 cmd = get_first_keyword();
684 for (int i=0; i < key_size; i++) {
685 if (!strcasecmp(s, cpl_keywords[i].key)) {
686 cpl_item = cpl_keywords[i].cmd;
688 matches = rl_completion_matches(text, cpl_generator);
694 if (!found) { /* we try to get help with the first command */
696 cpl_type = ITEM_HELP;
697 /* we don't want to append " " at the end */
698 rl_completion_suppress_append=true;
699 matches = rl_completion_matches(text, cpl_generator);
702 } else { /* nothing on the line, display all commands */
703 cpl_item = ".help all";
705 matches = rl_completion_matches(text, cpl_generator);
713 static char eol = '\0';
714 static int eolcmd(FILE *input, BSOCK *UA_sock)
716 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
718 } else if (argc == 1) {
721 sendit(_("Illegal separator character.\n"));
729 * -1 error (must stop)
732 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
734 static char *line = NULL;
735 static char *next = NULL;
736 static int do_history = 0;
741 rl_catch_signals = 0; /* do it ourselves */
742 /* Here, readline does ***real*** malloc
743 * so, be we have to use the real free
745 line = readline((char *)prompt); /* cast needed for old readlines */
747 return -1; /* error return and exit */
749 strip_trailing_junk(line);
754 sendit(_("Command logic problem\n"));
757 return 0; /* No input */
761 * Split "line" into multiple commands separated by the eol character.
762 * Each part is pointed to by "next" until finally it becomes null.
767 next = strchr(command, eol);
772 if (command != line && isatty(fileno(input))) {
773 senditf("%s%s\n", prompt, command);
776 /* Send the intput to the output file if needed */
777 if (teein && output != stdout) {
778 fputs(prompt, output);
779 fputs(command, output);
784 sock->msglen = pm_strcpy(&sock->msg, command);
793 actuallyfree(line); /* allocated by readline() malloc */
799 #else /* no readline, do it ourselves */
802 static bool bisatty(int fd)
812 * Returns: 1 if data available
817 wait_for_data(int fd, int sec)
819 #if defined(HAVE_WIN32)
823 switch(fd_wait_data(fd, WAIT_READ, sec, 0)) {
824 case 0: /* timeout */
827 if (errno == EINTR || errno == EAGAIN) {
830 return -1; /* error return */
839 * Get next input command from terminal.
841 * Returns: 1 if got input
846 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
850 if (output == stdout || teeout) {
855 switch (wait_for_data(fileno(input), sec)) {
857 return 0; /* timeout */
859 return -1; /* error */
861 len = sizeof_pool_memory(sock->msg) - 1;
867 if (bisatty(fileno(input))) {
868 input_line(sock->msg, len);
872 #ifdef HAVE_WIN32 /* use special console for input on win32 */
873 if (input == stdin) {
874 if (win32_cgets(sock->msg, len) == NULL) {
880 if (bfgets(sock->msg, input) == NULL) {
889 strip_trailing_junk(sock->msg);
890 sock->msglen = strlen(sock->msg);
892 /* Send input to log file if needed */
893 if (teein && output != stdout) {
894 fputs(sock->msg, output);
901 #endif /* ! HAVE_READLINE */
903 /* Routine to return true if user types break */
909 /* Clear break flag */
915 /* Interrupt caught here */
916 static void sigintcatcher(int sig)
920 terminate_console(sig);
922 signal(SIGINT, sigintcatcher);
928 signal(SIGINT, sigintcatcher);
931 static int console_update_history(const char *histfile)
937 * first, try to truncate the history file, and if it
938 * fails, the file is probably not present, and we
939 * can use write_history to create it
942 if (history_truncate_file(histfile, 100) == 0) {
943 ret = append_history(history_length, histfile);
945 ret = write_history(histfile);
952 static int console_init_history(const char *histfile)
958 ret = read_history(histfile);
959 /* Tell the completer that we want a complete . */
960 rl_completion_entry_function = dummy_completion_function;
961 rl_attempted_completion_function = readline_completion;
962 rl_filename_completion_desired = 0;
969 static bool select_director(const char *director, const char *console,
970 DIRRES **ret_dir, CONRES **ret_cons)
972 int numcon=0, numdir=0;
983 foreach_res(dir, R_DIRECTOR) {
987 foreach_res(cons, R_CONSOLE) {
992 if (numdir == 1) { /* No choose */
993 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
996 if (director) { /* Command line choice overwrite the no choose option */
998 foreach_res(dir, R_DIRECTOR) {
999 if (bstrcasecmp(dir->hdr.name, director)) {
1004 if (!dir) { /* Can't find Director used as argument */
1005 senditf(_("Can't find %s in Director list\n"), director);
1010 if (dir == NULL) { /* prompt for director */
1011 UA_sock = new_bsock();
1013 sendit(_("Available Directors:\n"));
1016 foreach_res(dir, R_DIRECTOR) {
1017 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name,
1018 dir->address, dir->DIRport);
1021 if (get_cmd(stdin, _("Select Director by entering a number: "),
1024 (void)WSACleanup(); /* Cleanup Windows sockets */
1027 if (!is_a_number(UA_sock->msg)) {
1028 senditf(_("%s is not a number. You must enter a number between "
1030 UA_sock->msg, numdir);
1033 item = atoi(UA_sock->msg);
1034 if (item < 0 || item > numdir) {
1035 senditf(_("You must enter a number between 1 and %d\n"), numdir);
1038 free_bsock(UA_sock);
1040 for (i=0; i<item; i++) {
1041 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
1046 /* Look for a console linked to this director */
1047 for (i=0; i<numcon; i++) {
1048 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1050 if (strcmp(cons->hdr.name, console) == 0) {
1053 } else if (cons->director && strcasecmp(cons->director, dir->hdr.name) == 0) {
1056 if (i == (numcon - 1)) {
1061 if (cons == NULL && console != NULL) {
1063 senditf(_("Can't find %s in Console list\n"), console);
1067 /* Look for the first non-linked console */
1069 for (i=0; i<numcon; i++) {
1070 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1071 if (cons->director == NULL) {
1074 if (i == (numcon - 1)) {
1080 /* If no console, take first one */
1082 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
1092 /*********************************************************************
1094 * Main Bacula Console -- User Interface Program
1097 int main(int argc, char *argv[])
1100 char *director = NULL;
1101 char *console = NULL;
1102 bool list_directors=false, list_consoles=false;
1103 bool no_signals = false;
1104 bool test_config = false;
1108 setlocale(LC_ALL, "");
1109 bindtextdomain("bacula", LOCALEDIR);
1110 textdomain("bacula");
1114 my_name_is(argc, argv, "bconsole");
1115 init_msg(NULL, NULL);
1116 working_directory = "/tmp";
1117 args = get_pool_memory(PM_FNAME);
1119 while ((ch = getopt(argc, argv, "D:lc:d:nstu:?C:L")) != -1) {
1121 case 'D': /* Director */
1125 director = bstrdup(optarg);
1128 case 'C': /* Console */
1132 console = bstrdup(optarg);
1135 case 'L': /* Console */
1136 list_consoles = true;
1141 list_directors = true;
1145 case 'c': /* configuration file */
1146 if (configfile != NULL) {
1149 configfile = bstrdup(optarg);
1153 if (*optarg == 't') {
1154 dbg_timestamp = true;
1156 debug_level = atoi(optarg);
1157 if (debug_level <= 0) {
1163 case 'n': /* no conio */
1167 case 's': /* turn off signals */
1176 timeout = atoi(optarg);
1189 init_signals(terminate_console);
1193 #if !defined(HAVE_WIN32)
1194 /* Override Bacula default signals */
1195 signal(SIGQUIT, SIG_IGN);
1196 signal(SIGTSTP, got_sigstop);
1197 signal(SIGCONT, got_sigcontinue);
1198 signal(SIGTTIN, got_sigtin);
1199 signal(SIGTTOU, got_sigtout);
1210 if (configfile == NULL) {
1211 configfile = bstrdup(CONFIG_FILE);
1214 config = New(CONFIG());
1215 parse_cons_config(config, configfile, M_ERROR_TERM);
1217 if (init_crypto() != 0) {
1218 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
1221 if (!check_resources()) {
1222 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
1229 if (list_directors) {
1231 foreach_res(dir, R_DIRECTOR) {
1232 senditf("%s\n", dir->hdr.name);
1237 if (list_consoles) {
1239 foreach_res(cons, R_CONSOLE) {
1240 senditf("%s\n", cons->hdr.name);
1246 terminate_console(0);
1250 memset(&jcr, 0, sizeof(jcr));
1252 (void)WSA_Init(); /* Initialize Windows sockets */
1254 start_watchdog(); /* Start socket watchdog */
1256 if (!select_director(director, console, &dir, &cons)) {
1257 terminate_console(0);
1261 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1264 /* Initialize Console TLS context */
1265 if (cons && (cons->tls_enable || cons->tls_require)) {
1266 /* Generate passphrase prompt */
1267 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1269 /* Initialize TLS context:
1270 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1271 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1273 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1274 cons->tls_ca_certdir, cons->tls_certfile,
1275 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1277 if (!cons->tls_ctx) {
1278 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1280 terminate_console(0);
1285 /* Initialize Director TLS context */
1286 if (dir->tls_enable || dir->tls_require) {
1287 /* Generate passphrase prompt */
1288 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1290 /* Initialize TLS context:
1291 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1292 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1293 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1294 dir->tls_ca_certdir, dir->tls_certfile,
1295 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1297 if (!dir->tls_ctx) {
1298 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1300 terminate_console(0);
1305 if (dir->heartbeat_interval) {
1306 heart_beat = dir->heartbeat_interval;
1308 heart_beat = cons->heartbeat_interval;
1313 UA_sock = new_bsock();
1315 if (!UA_sock->connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1316 NULL, dir->DIRport, 0)) {
1319 terminate_console(0);
1322 jcr.dir_bsock = UA_sock;
1324 /* If cons==NULL, default console will be used */
1325 if (!authenticate_director(UA_sock, dir, cons)) {
1326 terminate_console(0);
1330 Dmsg0(40, "Opened connection with Director daemon\n");
1332 sendit(_("Enter a period to cancel a command.\n"));
1334 /* Read/Update history file if HOME exists */
1335 POOL_MEM history_file;
1337 /* Run commands in ~/.bconsolerc if any */
1338 char *env = getenv("HOME");
1341 pm_strcpy(&UA_sock->msg, env);
1342 pm_strcat(&UA_sock->msg, "/.bconsolerc");
1343 fd = fopen(UA_sock->msg, "rb");
1345 read_and_process_input(fd, UA_sock);
1349 pm_strcpy(history_file, env);
1350 pm_strcat(history_file, "/.bconsole_history");
1351 console_init_history(history_file.c_str());
1354 read_and_process_input(stdin, UA_sock);
1357 UA_sock->signal(BNET_TERMINATE); /* send EOF */
1362 console_update_history(history_file.c_str());
1365 terminate_console(0);
1369 /* Cleanup and then exit */
1370 static void terminate_console(int sig)
1373 static bool already_here = false;
1375 if (already_here) { /* avoid recursive temination problems */
1378 already_here = true;
1385 free_pool_memory(args);
1386 #if defined(HAVE_CONIO)
1390 #elif defined(HAVE_READLINE)
1391 rl_cleanup_after_signal();
1392 #else /* !HAVE_CONIO && !HAVE_READLINE */
1394 (void)WSACleanup(); /* Cleanup Windows sockets */
1395 lmgr_cleanup_main();
1404 * Make a quick check to see that we have all the
1407 static int check_resources()
1416 foreach_res(director, R_DIRECTOR) {
1419 /* tls_require implies tls_enable */
1420 if (director->tls_require) {
1422 director->tls_enable = true;
1424 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1429 tls_needed = director->tls_enable || director->tls_authenticate;
1431 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1432 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1433 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1434 " At least one CA certificate store is required.\n"),
1435 director->hdr.name, configfile);
1441 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1442 "Without that I don't how to speak to the Director :-(\n"), configfile);
1447 /* Loop over Consoles */
1448 foreach_res(cons, R_CONSOLE) {
1449 /* tls_require implies tls_enable */
1450 if (cons->tls_require) {
1452 cons->tls_enable = true;
1454 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1459 tls_needed = cons->tls_enable || cons->tls_authenticate;
1460 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1461 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1462 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1463 cons->hdr.name, configfile);
1474 static int versioncmd(FILE *input, BSOCK *UA_sock)
1476 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1477 HOST_OS, DISTNAME, DISTVER);
1481 /* @input <input-filename> */
1482 static int inputcmd(FILE *input, BSOCK *UA_sock)
1487 sendit(_("Too many arguments on input command.\n"));
1491 sendit(_("First argument to input command must be a filename.\n"));
1494 fd = fopen(argk[1], "rb");
1497 senditf(_("Cannot open file %s for input. ERR=%s\n"),
1498 argk[1], be.bstrerror());
1501 read_and_process_input(fd, UA_sock);
1506 /* @tall <output-filename> */
1507 /* Send input/output to both terminal and specified file */
1508 static int teeallcmd(FILE *input, BSOCK *UA_sock)
1512 return do_outputcmd(input, UA_sock);
1515 /* @tee <output-filename> */
1516 /* Send output to both terminal and specified file */
1517 static int teecmd(FILE *input, BSOCK *UA_sock)
1521 return do_outputcmd(input, UA_sock);
1524 /* @output <output-filename> */
1525 /* Send output to specified "file" */
1526 static int outputcmd(FILE *input, BSOCK *UA_sock)
1530 return do_outputcmd(input, UA_sock);
1534 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1537 const char *mode = "a+b";
1540 sendit(_("Too many arguments on output/tee command.\n"));
1544 if (output != stdout) {
1555 fd = fopen(argk[1], mode);
1558 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1559 argk[1], be.bstrerror(errno));
1567 * @exec "some-command" [wait-seconds]
1569 static int execcmd(FILE *input, BSOCK *UA_sock)
1578 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1584 wait = atoi(argk[2]);
1588 /* handle cmd=XXXX and wait=XXXX */
1589 for (int i=1; i<argc; i++) {
1590 if (strcmp(argk[i], "cmd") == 0) {
1593 if (strcmp(argk[i], "wait") == 0) {
1594 wait = atoi(argv[i]);
1598 bpipe = open_bpipe(cmd, wait, "r");
1601 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1602 argk[1], be.bstrerror(errno));
1606 while (fgets(line, sizeof(line), bpipe->rfd)) {
1607 senditf("%s", line);
1609 stat = close_bpipe(bpipe);
1613 senditf(_("@exec error: ERR=%s\n"), be.bstrerror());
1619 static int echocmd(FILE *input, BSOCK *UA_sock)
1621 for (int i=1; i < argc; i++) {
1622 senditf("%s ", argk[i]);
1629 static int quitcmd(FILE *input, BSOCK *UA_sock)
1635 static int helpcmd(FILE *input, BSOCK *UA_sock)
1638 for (i=0; i<comsize; i++) {
1639 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1646 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1649 sleep(atoi(argk[1]));
1654 /* @putfile key /path/to/file
1656 * The Key parameter is needed to use the file on the director side.
1658 static int putfilecmd(FILE *input, BSOCK *UA_sock)
1661 const char *key = "putfile";
1666 sendit("Usage: @putfile key file\n");
1673 if (!key || !fname) {
1674 senditf("Syntax error in @putfile command\n");
1678 fp = fopen(fname, "r");
1681 senditf("Unable to open %s. ERR=%s\n", fname, be.bstrerror(errno));
1685 UA_sock->fsend(".putfile key=\"%s\"", key);
1687 /* Just read the file and send it to the director */
1689 i = fread(UA_sock->msg, 1, sizeof_pool_memory(UA_sock->msg) - 1, fp);
1691 UA_sock->msg[i] = 0;
1692 UA_sock->msglen = i;
1697 UA_sock->signal(BNET_EOD);
1700 /* Get the file name associated */
1701 while (UA_sock->recv() > 0) {
1702 senditf("%s", UA_sock->msg);
1708 static int timecmd(FILE *input, BSOCK *UA_sock)
1711 time_t ttime = time(NULL);
1713 (void)localtime_r(&ttime, &tm);
1714 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1720 * Send a line to the output file and or the terminal
1722 void senditf(const char *fmt,...)
1727 va_start(arg_ptr, fmt);
1728 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1733 void sendit(const char *buf)
1737 if (output == stdout || teeout) {
1740 * Here, we convert every \n into \r\n because the
1741 * terminal is in raw mode when we are using
1744 for (p=q=buf; (p=strchr(q, '\n')); ) {
1747 memcpy(obuf, q, len);
1749 memcpy(obuf+len, "\r\n", 3);
1750 q = ++p; /* point after \n */
1751 fputs(obuf, output);
1758 if (output != stdout) {