2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of 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
38 #include "console_conf.h"
48 #define con_set_zed_keys();
54 #if defined(HAVE_WIN32)
55 #define isatty(fd) (fd==0)
58 /* Exported variables */
60 //extern int rl_catch_signals;
62 /* Imported functions */
63 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
64 extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
66 /* Forward referenced functions */
67 static void terminate_console(int sig);
68 static int check_resources();
69 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
70 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
71 void senditf(const char *fmt, ...);
72 void sendit(const char *buf);
74 extern "C" void got_sigstop(int sig);
75 extern "C" void got_sigcontinue(int sig);
76 extern "C" void got_sigtout(int sig);
77 extern "C" void got_sigtin(int sig);
80 /* Static variables */
81 static char *configfile = NULL;
82 static BSOCK *UA_sock = NULL;
83 static DIRRES *dir = NULL;
84 static CONRES *cons = NULL;
85 static FILE *output = stdout;
86 static bool teeout = false; /* output to output and stdout */
87 static bool stop = false;
88 static bool no_conio = false;
89 static int timeout = 0;
93 static char *argk[MAX_CMD_ARGS];
94 static char *argv[MAX_CMD_ARGS];
95 static CONFIG *config;
98 /* Command prototypes */
99 static int versioncmd(FILE *input, BSOCK *UA_sock);
100 static int inputcmd(FILE *input, BSOCK *UA_sock);
101 static int outputcmd(FILE *input, BSOCK *UA_sock);
102 static int teecmd(FILE *input, BSOCK *UA_sock);
103 static int quitcmd(FILE *input, BSOCK *UA_sock);
104 static int helpcmd(FILE *input, BSOCK *UA_sock);
105 static int echocmd(FILE *input, BSOCK *UA_sock);
106 static int timecmd(FILE *input, BSOCK *UA_sock);
107 static int sleepcmd(FILE *input, BSOCK *UA_sock);
108 static int execcmd(FILE *input, BSOCK *UA_sock);
110 static int eolcmd(FILE *input, BSOCK *UA_sock);
112 # ifndef HAVE_REGEX_H
113 # include "lib/bregex.h"
121 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
127 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
128 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
129 " -D <dir> select a Director\n"
130 " -l list Directors defined\n"
131 " -c <file> set configuration file to file\n"
132 " -d <nn> set debug level to <nn>\n"
133 " -dt print timestamp in debug output\n"
136 " -u <nn> set command execution timeout to <nn> seconds\n"
137 " -t test - read configuration and exit\n"
138 " -? print this message.\n"
139 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
144 void got_sigstop(int sig)
150 void got_sigcontinue(int sig)
156 void got_sigtout(int sig)
158 // printf("Got tout\n");
162 void got_sigtin(int sig)
164 // printf("Got tin\n");
168 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
175 * These are the @command
177 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
178 static struct cmdstruct commands[] = {
179 { N_("input"), inputcmd, _("input from file")},
180 { N_("output"), outputcmd, _("output to file")},
181 { N_("quit"), quitcmd, _("quit")},
182 { N_("tee"), teecmd, _("output to file and terminal")},
183 { N_("sleep"), sleepcmd, _("sleep specified time")},
184 { N_("time"), timecmd, _("print current time")},
185 { N_("version"), versioncmd, _("print Console's version")},
186 { N_("echo"), echocmd, _("echo command string")},
187 { N_("exec"), execcmd, _("execute an external command")},
188 { N_("exit"), quitcmd, _("exit = quit")},
189 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
190 { N_("help"), helpcmd, _("help listing")},
192 { N_("separator"), eolcmd, _("set command separator")},
195 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
197 static int do_a_command(FILE *input, BSOCK *UA_sock)
208 Dmsg1(120, "Command: %s\n", UA_sock->msg);
214 if (*cmd == '#') { /* comment */
218 for (i=0; i<comsize; i++) { /* search for command */
219 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
220 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
226 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
227 UA_sock->msglen = strlen(UA_sock->msg);
228 sendit(UA_sock->msg);
234 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
236 const char *prompt = "*";
237 bool at_prompt = false;
238 int tty_input = isatty(fileno(input));
243 if (at_prompt) { /* don't prompt multiple times */
250 stat = get_cmd(input, prompt, UA_sock, 30);
258 /* Reading input from a file */
259 int len = sizeof_pool_memory(UA_sock->msg) - 1;
263 if (fgets(UA_sock->msg, len, input) == NULL) {
266 sendit(UA_sock->msg); /* echo to terminal */
267 strip_trailing_junk(UA_sock->msg);
268 UA_sock->msglen = strlen(UA_sock->msg);
273 break; /* error or interrupt */
274 } else if (stat == 0) { /* timeout */
275 if (strcmp(prompt, "*") == 0) {
276 tid = start_bsock_timer(UA_sock, timeout);
277 UA_sock->fsend(".messages");
278 stop_bsock_timer(tid);
284 /* @ => internal command for us */
285 if (UA_sock->msg[0] == '@') {
286 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
287 if (!do_a_command(input, UA_sock)) {
292 tid = start_bsock_timer(UA_sock, timeout);
293 if (!UA_sock->send()) { /* send command */
294 stop_bsock_timer(tid);
297 stop_bsock_timer(tid);
299 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
302 tid = start_bsock_timer(UA_sock, timeout);
303 while ((stat = UA_sock->recv()) >= 0) {
310 /* Suppress output if running in background or user hit ctl-c */
311 if (!stop && !usrbrk()) {
312 sendit(UA_sock->msg);
315 stop_bsock_timer(tid);
324 if (is_bnet_stop(UA_sock)) {
325 break; /* error or term */
326 } else if (stat == BNET_SIGNAL) {
327 if (UA_sock->msglen == BNET_PROMPT) {
330 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
336 * Call-back for reading a passphrase for an encrypted PEM file
337 * This function uses getpass(),
338 * which uses a static buffer and is NOT thread-safe.
340 static int tls_pem_callback(char *buf, int size, const void *userdata)
343 const char *prompt = (const char *)userdata;
344 # if defined(HAVE_WIN32)
346 if (win32_cgets(buf, size) == NULL) {
355 passwd = getpass(prompt);
356 bstrncpy(buf, passwd, size);
366 #define READLINE_LIBRARY 1
367 #include "readline.h"
370 /* Get the first keyword of the line */
376 char *first_space = strchr(rl_line_buffer, ' ');
378 len = first_space - rl_line_buffer;
379 ret = (char *) malloc((len + 1) * sizeof(char));
380 memcpy(ret, rl_line_buffer, len);
387 * Return the command before the current point.
388 * Set nb to the number of command to skip
391 get_previous_keyword(int current_point, int nb)
393 int i, end=-1, start, inquotes=0;
397 /* first we look for a space before the current word */
398 for (i = current_point; i >= 0; i--) {
399 if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
404 /* find the end of the command */
405 for (; i >= 0; i--) {
406 if (rl_line_buffer[i] != ' ') {
412 /* no end of string */
417 /* look for the start of the command */
418 for (start = end; start > 0; start--) {
419 if (rl_line_buffer[start] == '"') {
420 inquotes = !inquotes;
422 if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
425 current_point = start;
429 s = (char *)malloc(end - start + 2);
430 memcpy(s, rl_line_buffer + start, end - start + 1);
431 s[end - start + 1] = 0;
433 // printf("=======> %i:%i <%s>\n", start, end, s);
438 /* Simple structure that will contain the completion list */
443 static ItemList *items = NULL;
447 items = (ItemList*) malloc(sizeof(ItemList));
448 memset(items, 0, sizeof(ItemList));
451 items->list.destroy();
457 /* Match a regexp and add the result to the items list
458 * This function is recursive
460 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
464 regmatch_t pmatch[20];
469 rc = regexec(preg, what, nmatch, pmatch, 0);
472 Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n 10 20 30\n", what);
473 Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo);
474 Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo);
475 Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo);
476 Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo);
478 size = pmatch[1].rm_eo - pmatch[1].rm_so;
479 *buf = check_pool_memory_size(*buf, size + 1);
480 memcpy(*buf, what+pmatch[1].rm_so, size);
483 items->list.append(bstrdup(*buf));
484 /* We search for the next keyword in the line */
485 match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf);
489 /* fill the items list with the output of the help command */
490 void get_arguments(const char *what)
497 rc = regcomp(&preg, "(([a-z]+=)|([a-z]+)( |$))", REG_EXTENDED);
502 buf = get_pool_memory(PM_MESSAGE);
503 UA_sock->fsend(".help item=%s", what);
504 while (UA_sock->recv() > 0) {
505 strip_trailing_junk(UA_sock->msg);
506 match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
508 free_pool_memory(buf);
512 /* retreive a simple list (.pool, .client) and store it into items */
513 void get_items(const char *what)
517 UA_sock->fsend("%s", what);
518 while (UA_sock->recv() > 0) {
519 strip_trailing_junk(UA_sock->msg);
520 items->list.append(bstrdup(UA_sock->msg));
526 ITEM_ARG, /* item with simple list like .job */
527 ITEM_HELP /* use help item=xxx and detect all arguments */
530 /* Generator function for command completion. STATE lets us know whether
531 * to start from scratch; without any state (i.e. STATE == 0), then we
532 * start at the top of the list.
534 static char *item_generator(const char *text, int state,
535 const char *item, cpl_item_t type)
537 static int list_index, len;
540 /* If this is a new word to complete, initialize now. This includes
541 * saving the length of TEXT for efficiency, and initializing the index
558 /* Return the next name which partially matches from the command list. */
559 while (items && list_index < items->list.size())
561 name = (char *)items->list[list_index];
564 if (strncmp(name, text, len) == 0) {
565 char *ret = (char *) actuallymalloc(strlen(name)+1);
571 /* If no names matched, then return NULL. */
572 return ((char *)NULL);
575 /* gobal variables for the type and the item to search
576 * the readline API doesn' permit to pass user data.
578 static const char *cpl_item;
579 static cpl_item_t cpl_type;
581 static char *cpl_generator(const char *text, int state)
583 return item_generator(text, state, cpl_item, cpl_type);
586 /* this function is used to not use the default filename completion */
587 static char *dummy_completion_function(const char *text, int state)
592 struct cpl_keywords_t {
597 static struct cpl_keywords_t cpl_keywords[] = {
599 {"fileset=", ".fileset" },
600 {"client=", ".client" },
602 {"level=", ".level" },
603 {"storage=", ".storage" },
604 {"schedule=", ".schedule" },
605 {"volume=", ".media" },
606 {"oldvolume=", ".media" },
607 {"volstatus=", ".volstatus" },
612 {"unmark", ".lsmark" },
613 {"actiononpurge=", ".actiononpurge" }
615 #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
617 /* Attempt to complete on the contents of TEXT. START and END bound the
618 * region of rl_line_buffer that contains the word to complete. TEXT is
619 * the word to complete. We can use the entire contents of rl_line_buffer
620 * in case we want to do some simple parsing. Return the array of matches,
621 * or NULL if there aren't any.
623 static char **readline_completion(const char *text, int start, int end)
628 matches = (char **)NULL;
630 /* If this word is at the start of the line, then it is a command
631 * to complete. Otherwise it is the name of a file in the current
634 s = get_previous_keyword(start, 0);
635 cmd = get_first_keyword();
637 for (int i=0; i < key_size; i++) {
638 if (!strcasecmp(s, cpl_keywords[i].key)) {
639 cpl_item = cpl_keywords[i].cmd;
641 matches = rl_completion_matches(text, cpl_generator);
647 if (!found) { /* we try to get help with the first command */
649 cpl_type = ITEM_HELP;
650 /* we don't want to append " " at the end */
651 rl_completion_suppress_append=true;
652 matches = rl_completion_matches(text, cpl_generator);
655 } else { /* nothing on the line, display all commands */
656 cpl_item = ".help all";
658 matches = rl_completion_matches(text, cpl_generator);
666 static char eol = '\0';
667 static int eolcmd(FILE *input, BSOCK *UA_sock)
669 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
671 } else if (argc == 1) {
674 sendit(_("Illegal separator character.\n"));
680 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
682 static char *line = NULL;
683 static char *next = NULL;
684 static int do_history = 0;
689 rl_catch_signals = 0; /* do it ourselves */
690 /* Here, readline does ***real*** malloc
691 * so, be we have to use the real free
693 line = readline((char *)prompt); /* cast needed for old readlines */
697 strip_trailing_junk(line);
702 sendit(_("Command logic problem\n"));
709 * Split "line" into multiple commands separated by the eol character.
710 * Each part is pointed to by "next" until finally it becomes null.
715 next = strchr(command, eol);
720 if (command != line && isatty(fileno(input))) {
721 senditf("%s%s\n", prompt, command);
724 sock->msglen = pm_strcpy(&sock->msg, command);
733 actuallyfree(line); /* allocated by readline() malloc */
739 #else /* no readline, do it ourselves */
742 static bool bisatty(int fd)
752 * Returns: 1 if data available
757 wait_for_data(int fd, int sec)
759 #if defined(HAVE_WIN32)
769 FD_SET((unsigned)fd, &fdset);
770 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
771 case 0: /* timeout */
774 if (errno == EINTR || errno == EAGAIN) {
777 return -1; /* error return */
786 * Get next input command from terminal.
788 * Returns: 1 if got input
793 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
797 if (output == stdout || teeout) {
802 switch (wait_for_data(fileno(input), sec)) {
804 return 0; /* timeout */
806 return -1; /* error */
808 len = sizeof_pool_memory(sock->msg) - 1;
814 if (bisatty(fileno(input))) {
815 input_line(sock->msg, len);
819 #ifdef HAVE_WIN32 /* use special console for input on win32 */
820 if (input == stdin) {
821 if (win32_cgets(sock->msg, len) == NULL) {
827 if (fgets(sock->msg, len, input) == NULL) {
836 strip_trailing_junk(sock->msg);
837 sock->msglen = strlen(sock->msg);
841 #endif /* ! HAVE_READLINE */
844 static int console_update_history(const char *histfile)
849 /* first, try to truncate the history file, and if it
850 * fail, the file is probably not present, and we
851 * can use write_history to create it
854 if (history_truncate_file(histfile, 100) == 0) {
855 ret = append_history(history_length, histfile);
857 ret = write_history(histfile);
865 static int console_init_history(const char *histfile)
872 ret = read_history(histfile);
873 /* Tell the completer that we want a complete . */
874 rl_completion_entry_function = dummy_completion_function;
875 rl_attempted_completion_function = readline_completion;
876 rl_filename_completion_desired = 0;
883 bool select_director(const char *director, DIRRES **ret_dir, CONRES **ret_cons)
885 int numcon=0, numdir=0;
890 struct sockaddr client_addr;
891 memset(&client_addr, 0, sizeof(client_addr));
898 foreach_res(dir, R_DIRECTOR) {
902 foreach_res(cons, R_CONSOLE) {
907 if (numdir == 1) { /* No choose */
908 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
911 if (director) { /* Command line choice overwrite the no choose option */
913 foreach_res(dir, R_DIRECTOR) {
914 if (bstrcmp(dir->hdr.name, director)) {
919 if (!dir) { /* Can't find Director used as argument */
920 senditf(_("Can't find %s in Director list\n"), director);
925 if (!dir) { /* prompt for director */
926 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
928 sendit(_("Available Directors:\n"));
931 foreach_res(dir, R_DIRECTOR) {
932 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name,
933 dir->address, dir->DIRport);
936 if (get_cmd(stdin, _("Select Director by entering a number: "),
939 (void)WSACleanup(); /* Cleanup Windows sockets */
942 if (!is_a_number(UA_sock->msg)) {
943 senditf(_("%s is not a number. You must enter a number between "
945 UA_sock->msg, numdir);
948 item = atoi(UA_sock->msg);
949 if (item < 0 || item > numdir) {
950 senditf(_("You must enter a number between 1 and %d\n"), numdir);
955 for (i=0; i<item; i++) {
956 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
961 /* Look for a console linked to this director */
962 for (i=0; i<numcon; i++) {
963 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
964 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
969 /* Look for the first non-linked console */
971 for (i=0; i<numcon; i++) {
972 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
973 if (cons->director == NULL)
979 /* If no console, take first one */
981 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
991 /*********************************************************************
993 * Main Bacula Console -- User Interface Program
996 int main(int argc, char *argv[])
1000 bool list_directors=false;
1001 bool no_signals = false;
1002 bool test_config = false;
1006 setlocale(LC_ALL, "");
1007 bindtextdomain("bacula", LOCALEDIR);
1008 textdomain("bacula");
1012 my_name_is(argc, argv, "bconsole");
1013 init_msg(NULL, NULL);
1014 working_directory = "/tmp";
1015 args = get_pool_memory(PM_FNAME);
1017 while ((ch = getopt(argc, argv, "D:lbc:d:nstu:?")) != -1) {
1019 case 'D': /* Director */
1023 director = bstrdup(optarg);
1027 list_directors = true;
1031 case 'c': /* configuration file */
1032 if (configfile != NULL) {
1035 configfile = bstrdup(optarg);
1039 if (*optarg == 't') {
1040 dbg_timestamp = true;
1042 debug_level = atoi(optarg);
1043 if (debug_level <= 0) {
1049 case 'n': /* no conio */
1053 case 's': /* turn off signals */
1062 timeout = atoi(optarg);
1075 init_signals(terminate_console);
1079 #if !defined(HAVE_WIN32)
1080 /* Override Bacula default signals */
1081 signal(SIGQUIT, SIG_IGN);
1082 signal(SIGTSTP, got_sigstop);
1083 signal(SIGCONT, got_sigcontinue);
1084 signal(SIGTTIN, got_sigtin);
1085 signal(SIGTTOU, got_sigtout);
1096 if (configfile == NULL) {
1097 configfile = bstrdup(CONFIG_FILE);
1100 config = new_config_parser();
1101 parse_cons_config(config, configfile, M_ERROR_TERM);
1103 if (init_crypto() != 0) {
1104 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
1107 if (!check_resources()) {
1108 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
1115 if (list_directors) {
1117 foreach_res(dir, R_DIRECTOR) {
1118 senditf("%s\n", dir->hdr.name);
1124 terminate_console(0);
1128 memset(&jcr, 0, sizeof(jcr));
1130 (void)WSA_Init(); /* Initialize Windows sockets */
1132 start_watchdog(); /* Start socket watchdog */
1134 if(!select_director(director, &dir, &cons)) {
1138 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1141 /* Initialize Console TLS context */
1142 if (cons && (cons->tls_enable || cons->tls_require)) {
1143 /* Generate passphrase prompt */
1144 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1146 /* Initialize TLS context:
1147 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1148 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1150 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1151 cons->tls_ca_certdir, cons->tls_certfile,
1152 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1154 if (!cons->tls_ctx) {
1155 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1157 terminate_console(0);
1162 /* Initialize Director TLS context */
1163 if (dir->tls_enable || dir->tls_require) {
1164 /* Generate passphrase prompt */
1165 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1167 /* Initialize TLS context:
1168 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1169 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1170 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1171 dir->tls_ca_certdir, dir->tls_certfile,
1172 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1174 if (!dir->tls_ctx) {
1175 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1177 terminate_console(0);
1182 if (dir->heartbeat_interval) {
1183 heart_beat = dir->heartbeat_interval;
1185 heart_beat = cons->heartbeat_interval;
1189 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1190 NULL, dir->DIRport, 0);
1191 if (UA_sock == NULL) {
1192 terminate_console(0);
1195 jcr.dir_bsock = UA_sock;
1197 /* If cons==NULL, default console will be used */
1198 if (!authenticate_director(&jcr, dir, cons)) {
1199 terminate_console(0);
1203 Dmsg0(40, "Opened connection with Director daemon\n");
1205 sendit(_("Enter a period to cancel a command.\n"));
1207 /* Read/Update history file if HOME exists */
1208 POOL_MEM history_file;
1210 /* Run commands in ~/.bconsolerc if any */
1211 char *env = getenv("HOME");
1214 pm_strcpy(&UA_sock->msg, env);
1215 pm_strcat(&UA_sock->msg, "/.bconsolerc");
1216 fd = fopen(UA_sock->msg, "rb");
1218 read_and_process_input(fd, UA_sock);
1222 pm_strcpy(history_file, env);
1223 pm_strcat(history_file, "/.bconsole_history");
1224 console_init_history(history_file.c_str());
1227 read_and_process_input(stdin, UA_sock);
1230 UA_sock->signal(BNET_TERMINATE); /* send EOF */
1235 console_update_history(history_file.c_str());
1238 terminate_console(0);
1242 /* Cleanup and then exit */
1243 static void terminate_console(int sig)
1246 static bool already_here = false;
1248 if (already_here) { /* avoid recursive temination problems */
1251 already_here = true;
1253 config->free_resources();
1257 free_pool_memory(args);
1261 (void)WSACleanup(); /* Cleanup Windows sockets */
1262 lmgr_cleanup_main();
1271 * Make a quick check to see that we have all the
1274 static int check_resources()
1283 foreach_res(director, R_DIRECTOR) {
1286 /* tls_require implies tls_enable */
1287 if (director->tls_require) {
1289 director->tls_enable = true;
1291 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1296 tls_needed = director->tls_enable || director->tls_authenticate;
1298 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1299 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1300 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1301 " At least one CA certificate store is required.\n"),
1302 director->hdr.name, configfile);
1308 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1309 "Without that I don't how to speak to the Director :-(\n"), configfile);
1314 /* Loop over Consoles */
1315 foreach_res(cons, R_CONSOLE) {
1316 /* tls_require implies tls_enable */
1317 if (cons->tls_require) {
1319 cons->tls_enable = true;
1321 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1326 tls_needed = cons->tls_enable || cons->tls_authenticate;
1327 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1328 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1329 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1330 cons->hdr.name, configfile);
1340 static int versioncmd(FILE *input, BSOCK *UA_sock)
1342 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1343 HOST_OS, DISTNAME, DISTVER);
1347 static int inputcmd(FILE *input, BSOCK *UA_sock)
1352 sendit(_("Too many arguments on input command.\n"));
1356 sendit(_("First argument to input command must be a filename.\n"));
1359 fd = fopen(argk[1], "rb");
1362 senditf(_("Cannot open file %s for input. ERR=%s\n"),
1363 argk[1], be.bstrerror());
1366 read_and_process_input(fd, UA_sock);
1371 /* Send output to both termina and specified file */
1372 static int teecmd(FILE *input, BSOCK *UA_sock)
1375 return do_outputcmd(input, UA_sock);
1378 /* Send output to specified "file" */
1379 static int outputcmd(FILE *input, BSOCK *UA_sock)
1382 return do_outputcmd(input, UA_sock);
1386 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1389 const char *mode = "a+b";
1392 sendit(_("Too many arguments on output/tee command.\n"));
1396 if (output != stdout) {
1406 fd = fopen(argk[1], mode);
1409 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1410 argk[1], be.bstrerror(errno));
1418 * exec "some-command" [wait-seconds]
1420 static int execcmd(FILE *input, BSOCK *UA_sock)
1428 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1432 wait = atoi(argk[2]);
1434 bpipe = open_bpipe(argk[1], wait, "r");
1437 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1438 argk[1], be.bstrerror(errno));
1442 while (fgets(line, sizeof(line), bpipe->rfd)) {
1443 senditf("%s", line);
1445 stat = close_bpipe(bpipe);
1449 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1455 static int echocmd(FILE *input, BSOCK *UA_sock)
1457 for (int i=1; i < argc; i++) {
1458 senditf("%s ", argk[i]);
1464 static int quitcmd(FILE *input, BSOCK *UA_sock)
1469 static int helpcmd(FILE *input, BSOCK *UA_sock)
1472 for (i=0; i<comsize; i++) {
1473 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1479 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1482 sleep(atoi(argk[1]));
1488 static int timecmd(FILE *input, BSOCK *UA_sock)
1491 time_t ttime = time(NULL);
1493 (void)localtime_r(&ttime, &tm);
1494 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1500 * Send a line to the output file and or the terminal
1502 void senditf(const char *fmt,...)
1507 va_start(arg_ptr, fmt);
1508 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1513 void sendit(const char *buf)
1517 if (output == stdout || teeout) {
1520 * Here, we convert every \n into \r\n because the
1521 * terminal is in raw mode when we are using
1524 for (p=q=buf; (p=strchr(q, '\n')); ) {
1527 memcpy(obuf, q, len);
1529 memcpy(obuf+len, "\r\n", 3);
1530 q = ++p; /* point after \n */
1531 fputs(obuf, output);
1538 if (output != stdout) {