2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2010 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;
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 " -c <file> set configuration file to file\n"
130 " -d <nn> set debug level to <nn>\n"
131 " -dt print timestamp in debug output\n"
134 " -u <nn> set command execution timeout to <nn> seconds\n"
135 " -t test - read configuration and exit\n"
136 " -? print this message.\n"
137 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
142 void got_sigstop(int sig)
148 void got_sigcontinue(int sig)
154 void got_sigtout(int sig)
156 // printf("Got tout\n");
160 void got_sigtin(int sig)
162 // printf("Got tin\n");
166 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
173 * These are the @command
175 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
176 static struct cmdstruct commands[] = {
177 { N_("input"), inputcmd, _("input from file")},
178 { N_("output"), outputcmd, _("output to file")},
179 { N_("quit"), quitcmd, _("quit")},
180 { N_("tee"), teecmd, _("output to file and terminal")},
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_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
188 { N_("help"), helpcmd, _("help listing")},
190 { N_("separator"), eolcmd, _("set command separator")},
193 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
195 static int do_a_command(FILE *input, BSOCK *UA_sock)
206 Dmsg1(120, "Command: %s\n", UA_sock->msg);
212 if (*cmd == '#') { /* comment */
216 for (i=0; i<comsize; i++) { /* search for command */
217 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
218 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
224 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
225 UA_sock->msglen = strlen(UA_sock->msg);
226 sendit(UA_sock->msg);
232 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
234 const char *prompt = "*";
235 bool at_prompt = false;
236 int tty_input = isatty(fileno(input));
241 if (at_prompt) { /* don't prompt multiple times */
248 stat = get_cmd(input, prompt, UA_sock, 30);
256 /* Reading input from a file */
257 int len = sizeof_pool_memory(UA_sock->msg) - 1;
261 if (fgets(UA_sock->msg, len, input) == NULL) {
264 sendit(UA_sock->msg); /* echo to terminal */
265 strip_trailing_junk(UA_sock->msg);
266 UA_sock->msglen = strlen(UA_sock->msg);
271 break; /* error or interrupt */
272 } else if (stat == 0) { /* timeout */
273 if (strcmp(prompt, "*") == 0) {
274 tid = start_bsock_timer(UA_sock, timeout);
275 UA_sock->fsend(".messages");
276 stop_bsock_timer(tid);
282 /* @ => internal command for us */
283 if (UA_sock->msg[0] == '@') {
284 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
285 if (!do_a_command(input, UA_sock)) {
290 tid = start_bsock_timer(UA_sock, timeout);
291 if (!UA_sock->send()) { /* send command */
292 stop_bsock_timer(tid);
295 stop_bsock_timer(tid);
297 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
300 tid = start_bsock_timer(UA_sock, timeout);
301 while ((stat = UA_sock->recv()) >= 0) {
308 /* Suppress output if running in background or user hit ctl-c */
309 if (!stop && !usrbrk()) {
310 sendit(UA_sock->msg);
313 stop_bsock_timer(tid);
322 if (is_bnet_stop(UA_sock)) {
323 break; /* error or term */
324 } else if (stat == BNET_SIGNAL) {
325 if (UA_sock->msglen == BNET_SUB_PROMPT) {
328 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
334 * Call-back for reading a passphrase for an encrypted PEM file
335 * This function uses getpass(),
336 * which uses a static buffer and is NOT thread-safe.
338 static int tls_pem_callback(char *buf, int size, const void *userdata)
341 const char *prompt = (const char *)userdata;
342 # if defined(HAVE_WIN32)
344 if (win32_cgets(buf, size) == NULL) {
353 passwd = getpass(prompt);
354 bstrncpy(buf, passwd, size);
364 #define READLINE_LIBRARY 1
365 #include "readline.h"
368 /* Get the first keyword of the line */
374 char *first_space = strchr(rl_line_buffer, ' ');
376 len = first_space - rl_line_buffer;
377 ret = (char *) malloc((len + 1) * sizeof(char));
378 memcpy(ret, rl_line_buffer, len);
385 * Return the command before the current point.
386 * Set nb to the number of command to skip
389 get_previous_keyword(int current_point, int nb)
391 int i, end=-1, start, inquotes=0;
395 /* first we look for a space before the current word */
396 for (i = current_point; i >= 0; i--) {
397 if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
402 /* find the end of the command */
403 for (; i >= 0; i--) {
404 if (rl_line_buffer[i] != ' ') {
410 /* no end of string */
415 /* look for the start of the command */
416 for (start = end; start > 0; start--) {
417 if (rl_line_buffer[start] == '"') {
418 inquotes = !inquotes;
420 if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
423 current_point = start;
427 s = (char *)malloc(end - start + 2);
428 memcpy(s, rl_line_buffer + start, end - start + 1);
429 s[end - start + 1] = 0;
431 // printf("=======> %i:%i <%s>\n", start, end, s);
436 /* Simple structure that will contain the completion list */
441 static ItemList *items = NULL;
445 items = (ItemList*) malloc(sizeof(ItemList));
446 memset(items, 0, sizeof(ItemList));
449 items->list.destroy();
455 /* Match a regexp and add the result to the items list
456 * This function is recursive
458 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
462 regmatch_t pmatch[20];
467 rc = regexec(preg, what, nmatch, pmatch, 0);
470 Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n 10 20 30\n", what);
471 Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo);
472 Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo);
473 Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo);
474 Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo);
476 size = pmatch[1].rm_eo - pmatch[1].rm_so;
477 *buf = check_pool_memory_size(*buf, size + 1);
478 memcpy(*buf, what+pmatch[1].rm_so, size);
481 items->list.append(bstrdup(*buf));
482 /* We search for the next keyword in the line */
483 match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf);
487 /* fill the items list with the output of the help command */
488 void get_arguments(const char *what)
495 rc = regcomp(&preg, "(([a-z]+=)|([a-z]+)( |$))", REG_EXTENDED);
500 buf = get_pool_memory(PM_MESSAGE);
501 UA_sock->fsend(".help item=%s", what);
502 while (UA_sock->recv() > 0) {
503 strip_trailing_junk(UA_sock->msg);
504 match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
506 free_pool_memory(buf);
510 /* retreive a simple list (.pool, .client) and store it into items */
511 void get_items(const char *what)
515 UA_sock->fsend("%s", what);
516 while (UA_sock->recv() > 0) {
517 strip_trailing_junk(UA_sock->msg);
518 items->list.append(bstrdup(UA_sock->msg));
524 ITEM_ARG, /* item with simple list like .job */
525 ITEM_HELP /* use help item=xxx and detect all arguments */
528 /* Generator function for command completion. STATE lets us know whether
529 * to start from scratch; without any state (i.e. STATE == 0), then we
530 * start at the top of the list.
532 static char *item_generator(const char *text, int state,
533 const char *item, cpl_item_t type)
535 static int list_index, len;
538 /* If this is a new word to complete, initialize now. This includes
539 * saving the length of TEXT for efficiency, and initializing the index
556 /* Return the next name which partially matches from the command list. */
557 while (items && list_index < items->list.size())
559 name = (char *)items->list[list_index];
562 if (strncmp(name, text, len) == 0) {
563 char *ret = (char *) actuallymalloc(strlen(name)+1);
569 /* If no names matched, then return NULL. */
570 return ((char *)NULL);
573 /* gobal variables for the type and the item to search
574 * the readline API doesn' permit to pass user data.
576 static const char *cpl_item;
577 static cpl_item_t cpl_type;
579 static char *cpl_generator(const char *text, int state)
581 return item_generator(text, state, cpl_item, cpl_type);
584 /* this function is used to not use the default filename completion */
585 static char *dummy_completion_function(const char *text, int state)
590 struct cpl_keywords_t {
595 static struct cpl_keywords_t cpl_keywords[] = {
597 {"fileset=", ".fileset" },
598 {"client=", ".client" },
600 {"level=", ".level" },
601 {"storage=", ".storage" },
602 {"schedule=", ".schedule" },
603 {"volume=", ".media" },
604 {"oldvolume=", ".media" },
605 {"volstatus=", ".volstatus" },
610 {"unmark", ".lsmark" },
611 {"actiononpurge=", ".actiononpurge" }
613 #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
615 /* Attempt to complete on the contents of TEXT. START and END bound the
616 * region of rl_line_buffer that contains the word to complete. TEXT is
617 * the word to complete. We can use the entire contents of rl_line_buffer
618 * in case we want to do some simple parsing. Return the array of matches,
619 * or NULL if there aren't any.
621 static char **readline_completion(const char *text, int start, int end)
626 matches = (char **)NULL;
628 /* If this word is at the start of the line, then it is a command
629 * to complete. Otherwise it is the name of a file in the current
632 s = get_previous_keyword(start, 0);
633 cmd = get_first_keyword();
635 for (int i=0; i < key_size; i++) {
636 if (!strcasecmp(s, cpl_keywords[i].key)) {
637 cpl_item = cpl_keywords[i].cmd;
639 matches = rl_completion_matches(text, cpl_generator);
645 if (!found) { /* we try to get help with the first command */
647 cpl_type = ITEM_HELP;
648 /* we don't want to append " " at the end */
649 rl_completion_suppress_append=true;
650 matches = rl_completion_matches(text, cpl_generator);
653 } else { /* nothing on the line, display all commands */
654 cpl_item = ".help all";
656 matches = rl_completion_matches(text, cpl_generator);
664 static char eol = '\0';
665 static int eolcmd(FILE *input, BSOCK *UA_sock)
667 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
669 } else if (argc == 1) {
672 sendit(_("Illegal separator character.\n"));
678 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
680 static char *line = NULL;
681 static char *next = NULL;
682 static int do_history = 0;
687 rl_catch_signals = 0; /* do it ourselves */
688 /* Here, readline does ***real*** malloc
689 * so, be we have to use the real free
691 line = readline((char *)prompt); /* cast needed for old readlines */
695 strip_trailing_junk(line);
700 sendit(_("Command logic problem\n"));
707 * Split "line" into multiple commands separated by the eol character.
708 * Each part is pointed to by "next" until finally it becomes null.
713 next = strchr(command, eol);
718 if (command != line && isatty(fileno(input))) {
719 senditf("%s%s\n", prompt, command);
722 sock->msglen = pm_strcpy(&sock->msg, command);
731 actuallyfree(line); /* allocated by readline() malloc */
737 #else /* no readline, do it ourselves */
740 static bool bisatty(int fd)
750 * Returns: 1 if data available
755 wait_for_data(int fd, int sec)
757 #if defined(HAVE_WIN32)
767 FD_SET((unsigned)fd, &fdset);
768 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
769 case 0: /* timeout */
772 if (errno == EINTR || errno == EAGAIN) {
775 return -1; /* error return */
784 * Get next input command from terminal.
786 * Returns: 1 if got input
791 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
795 if (output == stdout || teeout) {
800 switch (wait_for_data(fileno(input), sec)) {
802 return 0; /* timeout */
804 return -1; /* error */
806 len = sizeof_pool_memory(sock->msg) - 1;
812 if (bisatty(fileno(input))) {
813 input_line(sock->msg, len);
817 #ifdef HAVE_WIN32 /* use special console for input on win32 */
818 if (input == stdin) {
819 if (win32_cgets(sock->msg, len) == NULL) {
825 if (fgets(sock->msg, len, input) == NULL) {
834 strip_trailing_junk(sock->msg);
835 sock->msglen = strlen(sock->msg);
839 #endif /* ! HAVE_READLINE */
842 static int console_update_history(const char *histfile)
847 /* first, try to truncate the history file, and if it
848 * fail, the file is probably not present, and we
849 * can use write_history to create it
852 if (history_truncate_file(histfile, 100) == 0) {
853 ret = append_history(history_length, histfile);
855 ret = write_history(histfile);
863 static int console_init_history(const char *histfile)
870 ret = read_history(histfile);
871 /* Tell the completer that we want a complete . */
872 rl_completion_entry_function = dummy_completion_function;
873 rl_attempted_completion_function = readline_completion;
874 rl_filename_completion_desired = 0;
881 /*********************************************************************
883 * Main Bacula Console -- User Interface Program
886 int main(int argc, char *argv[])
889 bool no_signals = false;
890 bool test_config = false;
894 setlocale(LC_ALL, "");
895 bindtextdomain("bacula", LOCALEDIR);
896 textdomain("bacula");
900 my_name_is(argc, argv, "bconsole");
901 init_msg(NULL, NULL);
902 working_directory = "/tmp";
903 args = get_pool_memory(PM_FNAME);
905 while ((ch = getopt(argc, argv, "bc:d:nstu:?")) != -1) {
907 case 'c': /* configuration file */
908 if (configfile != NULL) {
911 configfile = bstrdup(optarg);
915 if (*optarg == 't') {
916 dbg_timestamp = true;
918 debug_level = atoi(optarg);
919 if (debug_level <= 0) {
925 case 'n': /* no conio */
929 case 's': /* turn off signals */
938 timeout = atoi(optarg);
951 init_signals(terminate_console);
955 #if !defined(HAVE_WIN32)
956 /* Override Bacula default signals */
957 signal(SIGQUIT, SIG_IGN);
958 signal(SIGTSTP, got_sigstop);
959 signal(SIGCONT, got_sigcontinue);
960 signal(SIGTTIN, got_sigtin);
961 signal(SIGTTOU, got_sigtout);
972 if (configfile == NULL) {
973 configfile = bstrdup(CONFIG_FILE);
976 config = new_config_parser();
977 parse_cons_config(config, configfile, M_ERROR_TERM);
979 if (init_crypto() != 0) {
980 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
983 if (!check_resources()) {
984 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
992 terminate_console(0);
996 memset(&jcr, 0, sizeof(jcr));
998 (void)WSA_Init(); /* Initialize Windows sockets */
1000 start_watchdog(); /* Start socket watchdog */
1004 foreach_res(dir, R_DIRECTOR) {
1008 foreach_res(cons, R_CONSOLE) {
1014 struct sockaddr client_addr;
1015 memset(&client_addr, 0, sizeof(client_addr));
1016 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
1018 sendit(_("Available Directors:\n"));
1021 foreach_res(dir, R_DIRECTOR) {
1022 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
1026 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
1027 (void)WSACleanup(); /* Cleanup Windows sockets */
1030 if (!is_a_number(UA_sock->msg)) {
1031 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
1032 UA_sock->msg, numdir);
1035 item = atoi(UA_sock->msg);
1036 if (item < 0 || item > numdir) {
1037 senditf(_("You must enter a number between 1 and %d\n"), numdir);
1040 term_bsock(UA_sock);
1042 for (i=0; i<item; i++) {
1043 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
1045 /* Look for a console linked to this director */
1046 for (i=0; i<numcon; i++) {
1047 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1048 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
1053 /* Look for the first non-linked console */
1055 for (i=0; i<numcon; i++) {
1056 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1057 if (cons->director == NULL)
1064 /* If no director, take first one */
1067 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
1070 /* If no console, take first one */
1073 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
1077 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1080 /* Initialize Console TLS context */
1081 if (cons && (cons->tls_enable || cons->tls_require)) {
1082 /* Generate passphrase prompt */
1083 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1085 /* Initialize TLS context:
1086 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1087 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1089 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1090 cons->tls_ca_certdir, cons->tls_certfile,
1091 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1093 if (!cons->tls_ctx) {
1094 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1096 terminate_console(0);
1101 /* Initialize Director TLS context */
1102 if (dir->tls_enable || dir->tls_require) {
1103 /* Generate passphrase prompt */
1104 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1106 /* Initialize TLS context:
1107 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1108 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1109 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1110 dir->tls_ca_certdir, dir->tls_certfile,
1111 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1113 if (!dir->tls_ctx) {
1114 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1116 terminate_console(0);
1121 if (dir->heartbeat_interval) {
1122 heart_beat = dir->heartbeat_interval;
1124 heart_beat = cons->heartbeat_interval;
1128 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1129 NULL, dir->DIRport, 0);
1130 if (UA_sock == NULL) {
1131 terminate_console(0);
1134 jcr.dir_bsock = UA_sock;
1136 /* If cons==NULL, default console will be used */
1137 if (!authenticate_director(&jcr, dir, cons)) {
1138 terminate_console(0);
1142 Dmsg0(40, "Opened connection with Director daemon\n");
1144 sendit(_("Enter a period to cancel a command.\n"));
1146 /* Read/Update history file if HOME exists */
1147 POOL_MEM history_file;
1149 /* Run commands in ~/.bconsolerc if any */
1150 char *env = getenv("HOME");
1153 pm_strcpy(&UA_sock->msg, env);
1154 pm_strcat(&UA_sock->msg, "/.bconsolerc");
1155 fd = fopen(UA_sock->msg, "rb");
1157 read_and_process_input(fd, UA_sock);
1161 pm_strcpy(history_file, env);
1162 pm_strcat(history_file, "/.bconsole_history");
1163 console_init_history(history_file.c_str());
1166 read_and_process_input(stdin, UA_sock);
1169 UA_sock->signal(BNET_TERMINATE); /* send EOF */
1174 console_update_history(history_file.c_str());
1177 terminate_console(0);
1181 /* Cleanup and then exit */
1182 static void terminate_console(int sig)
1185 static bool already_here = false;
1187 if (already_here) { /* avoid recursive temination problems */
1190 already_here = true;
1192 config->free_resources();
1196 free_pool_memory(args);
1200 (void)WSACleanup(); /* Cleanup Windows sockets */
1201 lmgr_cleanup_main();
1210 * Make a quick check to see that we have all the
1213 static int check_resources()
1222 foreach_res(director, R_DIRECTOR) {
1225 /* tls_require implies tls_enable */
1226 if (director->tls_require) {
1228 director->tls_enable = true;
1230 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1235 tls_needed = director->tls_enable || director->tls_authenticate;
1237 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1238 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1239 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1240 " At least one CA certificate store is required.\n"),
1241 director->hdr.name, configfile);
1247 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1248 "Without that I don't how to speak to the Director :-(\n"), configfile);
1253 /* Loop over Consoles */
1254 foreach_res(cons, R_CONSOLE) {
1255 /* tls_require implies tls_enable */
1256 if (cons->tls_require) {
1258 cons->tls_enable = true;
1260 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1265 tls_needed = cons->tls_enable || cons->tls_authenticate;
1266 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1267 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1268 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1269 cons->hdr.name, configfile);
1279 static int versioncmd(FILE *input, BSOCK *UA_sock)
1281 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1282 HOST_OS, DISTNAME, DISTVER);
1286 static int inputcmd(FILE *input, BSOCK *UA_sock)
1291 sendit(_("Too many arguments on input command.\n"));
1295 sendit(_("First argument to input command must be a filename.\n"));
1298 fd = fopen(argk[1], "rb");
1301 senditf(_("Cannot open file %s for input. ERR=%s\n"),
1302 argk[1], be.bstrerror());
1305 read_and_process_input(fd, UA_sock);
1310 /* Send output to both termina and specified file */
1311 static int teecmd(FILE *input, BSOCK *UA_sock)
1314 return do_outputcmd(input, UA_sock);
1317 /* Send output to specified "file" */
1318 static int outputcmd(FILE *input, BSOCK *UA_sock)
1321 return do_outputcmd(input, UA_sock);
1325 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1328 const char *mode = "a+b";
1331 sendit(_("Too many arguments on output/tee command.\n"));
1335 if (output != stdout) {
1345 fd = fopen(argk[1], mode);
1348 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1349 argk[1], be.bstrerror(errno));
1357 * exec "some-command" [wait-seconds]
1359 static int execcmd(FILE *input, BSOCK *UA_sock)
1367 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1371 wait = atoi(argk[2]);
1373 bpipe = open_bpipe(argk[1], wait, "r");
1376 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1377 argk[1], be.bstrerror(errno));
1381 while (fgets(line, sizeof(line), bpipe->rfd)) {
1382 senditf("%s", line);
1384 stat = close_bpipe(bpipe);
1388 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1394 static int echocmd(FILE *input, BSOCK *UA_sock)
1396 for (int i=1; i < argc; i++) {
1397 senditf("%s ", argk[i]);
1403 static int quitcmd(FILE *input, BSOCK *UA_sock)
1408 static int helpcmd(FILE *input, BSOCK *UA_sock)
1411 for (i=0; i<comsize; i++) {
1412 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1418 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1421 sleep(atoi(argk[1]));
1427 static int timecmd(FILE *input, BSOCK *UA_sock)
1430 time_t ttime = time(NULL);
1432 (void)localtime_r(&ttime, &tm);
1433 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1439 * Send a line to the output file and or the terminal
1441 void senditf(const char *fmt,...)
1446 va_start(arg_ptr, fmt);
1447 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1452 void sendit(const char *buf)
1456 if (output == stdout || teeout) {
1459 * Here, we convert every \n into \r\n because the
1460 * terminal is in raw mode when we are using
1463 for (p=q=buf; (p=strchr(q, '\n')); ) {
1466 memcpy(obuf, q, len);
1468 memcpy(obuf+len, "\r\n", 3);
1469 q = ++p; /* point after \n */
1470 fputs(obuf, output);
1477 if (output != stdout) {