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;
94 static char *argk[MAX_CMD_ARGS];
95 static char *argv[MAX_CMD_ARGS];
96 static CONFIG *config;
99 /* Command prototypes */
100 static int versioncmd(FILE *input, BSOCK *UA_sock);
101 static int inputcmd(FILE *input, BSOCK *UA_sock);
102 static int outputcmd(FILE *input, BSOCK *UA_sock);
103 static int teecmd(FILE *input, BSOCK *UA_sock);
104 static int quitcmd(FILE *input, BSOCK *UA_sock);
105 static int helpcmd(FILE *input, BSOCK *UA_sock);
106 static int echocmd(FILE *input, BSOCK *UA_sock);
107 static int timecmd(FILE *input, BSOCK *UA_sock);
108 static int sleepcmd(FILE *input, BSOCK *UA_sock);
109 static int execcmd(FILE *input, BSOCK *UA_sock);
111 static int eolcmd(FILE *input, BSOCK *UA_sock);
113 # ifndef HAVE_REGEX_H
114 # include "lib/bregex.h"
122 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
128 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
129 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
130 " -c <file> set configuration file to file\n"
131 " -d <nn> set debug level to <nn>\n"
132 " -dt print timestamp in debug output\n"
135 " -u <nn> set command execution timeout to <nn> seconds\n"
136 " -t test - read configuration and exit\n"
137 " -? print this message.\n"
138 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
143 void got_sigstop(int sig)
149 void got_sigcontinue(int sig)
155 void got_sigtout(int sig)
157 // printf("Got tout\n");
161 void got_sigtin(int sig)
163 // printf("Got tin\n");
167 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
174 * These are the @command
176 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
177 static struct cmdstruct commands[] = {
178 { N_("input"), inputcmd, _("input from file")},
179 { N_("output"), outputcmd, _("output to file")},
180 { N_("quit"), quitcmd, _("quit")},
181 { N_("tee"), teecmd, _("output to file and terminal")},
182 { N_("sleep"), sleepcmd, _("sleep specified time")},
183 { N_("time"), timecmd, _("print current time")},
184 { N_("version"), versioncmd, _("print Console's version")},
185 { N_("echo"), echocmd, _("echo command string")},
186 { N_("exec"), execcmd, _("execute an external command")},
187 { N_("exit"), quitcmd, _("exit = quit")},
188 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
189 { N_("help"), helpcmd, _("help listing")},
191 { N_("separator"), eolcmd, _("set command separator")},
194 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
196 static int do_a_command(FILE *input, BSOCK *UA_sock)
207 Dmsg1(120, "Command: %s\n", UA_sock->msg);
213 if (*cmd == '#') { /* comment */
217 for (i=0; i<comsize; i++) { /* search for command */
218 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
219 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
225 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
226 UA_sock->msglen = strlen(UA_sock->msg);
227 sendit(UA_sock->msg);
233 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
235 const char *prompt = "*";
236 bool at_prompt = false;
237 int tty_input = isatty(fileno(input));
242 if (at_prompt) { /* don't prompt multiple times */
249 stat = get_cmd(input, prompt, UA_sock, 30);
257 /* Reading input from a file */
258 int len = sizeof_pool_memory(UA_sock->msg) - 1;
262 if (fgets(UA_sock->msg, len, input) == NULL) {
265 sendit(UA_sock->msg); /* echo to terminal */
266 strip_trailing_junk(UA_sock->msg);
267 UA_sock->msglen = strlen(UA_sock->msg);
272 break; /* error or interrupt */
273 } else if (stat == 0) { /* timeout */
274 if (strcmp(prompt, "*") == 0) {
275 tid = start_bsock_timer(UA_sock, timeout);
276 bnet_fsend(UA_sock, ".messages");
277 stop_bsock_timer(tid);
283 /* @ => internal command for us */
284 if (UA_sock->msg[0] == '@') {
285 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
286 if (!do_a_command(input, UA_sock)) {
291 tid = start_bsock_timer(UA_sock, timeout);
292 if (!bnet_send(UA_sock)) { /* send command */
293 stop_bsock_timer(tid);
296 stop_bsock_timer(tid);
298 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
301 tid = start_bsock_timer(UA_sock, timeout);
302 while ((stat = bnet_recv(UA_sock)) >= 0) {
309 /* Suppress output if running in background or user hit ctl-c */
310 if (!stop && !usrbrk()) {
311 sendit(UA_sock->msg);
314 stop_bsock_timer(tid);
323 if (is_bnet_stop(UA_sock)) {
324 break; /* error or term */
325 } else if (stat == BNET_SIGNAL) {
326 if (UA_sock->msglen == BNET_PROMPT) {
329 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
335 * Call-back for reading a passphrase for an encrypted PEM file
336 * This function uses getpass(),
337 * which uses a static buffer and is NOT thread-safe.
339 static int tls_pem_callback(char *buf, int size, const void *userdata)
342 const char *prompt = (const char *)userdata;
343 # if defined(HAVE_WIN32)
345 if (win32_cgets(buf, size) == NULL) {
354 passwd = getpass(prompt);
355 bstrncpy(buf, passwd, size);
365 #define READLINE_LIBRARY 1
366 #include "readline.h"
369 /* Get the first keyword of the line */
375 char *first_space = strchr(rl_line_buffer, ' ');
377 len = first_space - rl_line_buffer;
378 ret = (char *) malloc((len + 1) * sizeof(char));
379 memcpy(ret, rl_line_buffer, len);
386 * Return the command before the current point.
387 * Set nb to the number of command to skip
390 get_previous_keyword(int current_point, int nb)
392 int i, end=-1, start, inquotes=0;
396 /* first we look for a space before the current word */
397 for (i = current_point; i >= 0; i--) {
398 if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
403 /* find the end of the command */
404 for (; i >= 0; i--) {
405 if (rl_line_buffer[i] != ' ') {
411 /* no end of string */
416 /* look for the start of the command */
417 for (start = end; start > 0; start--) {
418 if (rl_line_buffer[start] == '"') {
419 inquotes = !inquotes;
421 if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
424 current_point = start;
428 s = (char *)malloc(end - start + 2);
429 memcpy(s, rl_line_buffer + start, end - start + 1);
430 s[end - start + 1] = 0;
432 // printf("=======> %i:%i <%s>\n", start, end, s);
437 /* Simple structure that will contain the completion list */
442 static ItemList *items = NULL;
446 items = (ItemList*) malloc(sizeof(ItemList));
447 memset(items, 0, sizeof(ItemList));
450 items->list.destroy();
456 /* Match a regexp and add the result to the items list
457 * This function is recursive
459 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
463 regmatch_t pmatch[20];
468 rc = regexec(preg, what, nmatch, pmatch, 0);
470 size = pmatch[0].rm_eo - pmatch[0].rm_so;
472 *buf = check_pool_memory_size(*buf, size + 1);
473 memcpy(*buf, what+pmatch[0].rm_so, size);
476 items->list.append(bstrdup(*buf));
477 /* We search for the next keyword in the line */
478 match_kw(preg, what + pmatch[0].rm_eo, len - pmatch[0].rm_eo, buf);
482 /* fill the items list with the output of the help command */
483 void get_arguments(const char *what)
491 "([a-z]+=|done|select|all|listing|jobs|stats|slots)",
497 buf = get_pool_memory(PM_MESSAGE);
498 UA_sock->fsend(".help item=%s", what);
499 while (UA_sock->recv() > 0) {
500 strip_trailing_junk(UA_sock->msg);
501 match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
503 free_pool_memory(buf);
507 /* retreive a simple list (.pool, .client) and store it into items */
508 void get_items(const char *what)
512 UA_sock->fsend("%s", what);
513 while (UA_sock->recv() > 0) {
514 strip_trailing_junk(UA_sock->msg);
515 items->list.append(bstrdup(UA_sock->msg));
521 ITEM_ARG, /* item with simple list like .job */
522 ITEM_HELP /* use help item=xxx and detect all arguments */
525 /* Generator function for command completion. STATE lets us know whether
526 * to start from scratch; without any state (i.e. STATE == 0), then we
527 * start at the top of the list.
529 static char *item_generator(const char *text, int state,
530 const char *item, cpl_item_t type)
532 static int list_index, len;
535 /* If this is a new word to complete, initialize now. This includes
536 * saving the length of TEXT for efficiency, and initializing the index
553 /* Return the next name which partially matches from the command list. */
554 while (items && list_index < items->list.size())
556 name = (char *)items->list[list_index];
559 if (strncmp(name, text, len) == 0) {
560 char *ret = (char *) actuallymalloc(strlen(name)+1);
566 /* If no names matched, then return NULL. */
567 return ((char *)NULL);
570 /* gobal variables for the type and the item to search
571 * the readline API doesn' permit to pass user data.
573 static const char *cpl_item;
574 static cpl_item_t cpl_type;
576 static char *cpl_generator(const char *text, int state)
578 return item_generator(text, state, cpl_item, cpl_type);
581 /* this function is used to not use the default filename completion */
582 static char *dummy_completion_function(const char *text, int state)
587 struct cpl_keywords_t {
592 static struct cpl_keywords_t cpl_keywords[] = {
594 {"fileset=", ".fileset" },
595 {"client=", ".client" },
597 {"level=", ".level" },
598 {"storage=", ".storage" },
599 {"schedule=", ".schedule" },
600 {"volume=", ".media" },
601 {"oldvolume=", ".media" },
602 {"volstatus=", ".volstatus" }
604 #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
606 /* Attempt to complete on the contents of TEXT. START and END bound the
607 * region of rl_line_buffer that contains the word to complete. TEXT is
608 * the word to complete. We can use the entire contents of rl_line_buffer
609 * in case we want to do some simple parsing. Return the array of matches,
610 * or NULL if there aren't any.
612 static char **readline_completion(const char *text, int start, int end)
617 matches = (char **)NULL;
619 /* If this word is at the start of the line, then it is a command
620 to complete. Otherwise it is the name of a file in the current
622 s = get_previous_keyword(start, 0);
623 cmd = get_first_keyword();
625 for (int i=0; i < key_size; i++) {
626 if (!strcasecmp(s, cpl_keywords[i].key)) {
627 cpl_item = cpl_keywords[i].cmd;
629 matches = rl_completion_matches(text, cpl_generator);
635 if (!found) { /* we try to get help with the first command */
637 cpl_type = ITEM_HELP;
638 /* we don't want to append " " at the end */
639 rl_completion_suppress_append=true;
640 matches = rl_completion_matches(text, cpl_generator);
643 } else { /* nothing on the line, display all commands */
644 cpl_item = ".help all";
646 matches = rl_completion_matches(text, cpl_generator);
654 static char eol = '\0';
655 static int eolcmd(FILE *input, BSOCK *UA_sock)
657 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
659 } else if (argc == 1) {
662 sendit(_("Illegal separator character.\n"));
668 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
670 static char *line = NULL;
671 static char *next = NULL;
672 static int do_history = 0;
677 rl_catch_signals = 0; /* do it ourselves */
678 /* Here, readline does ***real*** malloc
679 * so, be we have to use the real free
681 line = readline((char *)prompt); /* cast needed for old readlines */
685 strip_trailing_junk(line);
690 sendit(_("Command logic problem\n"));
697 * Split "line" into multiple commands separated by the eol character.
698 * Each part is pointed to by "next" until finally it becomes null.
703 next = strchr(command, eol);
708 if (command != line && isatty(fileno(input))) {
709 senditf("%s%s\n", prompt, command);
712 sock->msglen = pm_strcpy(&sock->msg, command);
721 actuallyfree(line); /* allocated by readline() malloc */
727 #else /* no readline, do it ourselves */
730 static bool bisatty(int fd)
740 * Returns: 1 if data available
745 wait_for_data(int fd, int sec)
747 #if defined(HAVE_WIN32)
757 FD_SET((unsigned)fd, &fdset);
758 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
759 case 0: /* timeout */
762 if (errno == EINTR || errno == EAGAIN) {
765 return -1; /* error return */
774 * Get next input command from terminal.
776 * Returns: 1 if got input
781 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
785 if (output == stdout || teeout) {
790 switch (wait_for_data(fileno(input), sec)) {
792 return 0; /* timeout */
794 return -1; /* error */
796 len = sizeof_pool_memory(sock->msg) - 1;
802 if (bisatty(fileno(input))) {
803 input_line(sock->msg, len);
807 #ifdef HAVE_WIN32 /* use special console for input on win32 */
808 if (input == stdin) {
809 if (win32_cgets(sock->msg, len) == NULL) {
815 if (fgets(sock->msg, len, input) == NULL) {
824 strip_trailing_junk(sock->msg);
825 sock->msglen = strlen(sock->msg);
829 #endif /* ! HAVE_READLINE */
832 static int console_update_history(const char *histfile)
837 /* first, try to truncate the history file, and if it
838 * fail, the file is probably not present, and we
839 * can use write_history to create it
842 if (history_truncate_file(histfile, 100) == 0) {
843 ret = append_history(history_length, histfile);
845 ret = write_history(histfile);
853 static int console_init_history(const char *histfile)
860 ret = read_history(histfile);
861 /* Tell the completer that we want a complete . */
862 rl_completion_entry_function = dummy_completion_function;
863 rl_attempted_completion_function = readline_completion;
864 rl_filename_completion_desired = 0;
870 /*********************************************************************
872 * Main Bacula Console -- User Interface Program
875 int main(int argc, char *argv[])
878 bool no_signals = false;
879 bool test_config = false;
883 setlocale(LC_ALL, "");
884 bindtextdomain("bacula", LOCALEDIR);
885 textdomain("bacula");
889 my_name_is(argc, argv, "bconsole");
890 init_msg(NULL, NULL);
891 working_directory = "/tmp";
892 args = get_pool_memory(PM_FNAME);
894 while ((ch = getopt(argc, argv, "bc:d:nstu:?")) != -1) {
896 case 'c': /* configuration file */
897 if (configfile != NULL) {
900 configfile = bstrdup(optarg);
904 if (*optarg == 't') {
905 dbg_timestamp = true;
907 debug_level = atoi(optarg);
908 if (debug_level <= 0) {
914 case 'n': /* no conio */
918 case 's': /* turn off signals */
927 timeout = atoi(optarg);
940 init_signals(terminate_console);
944 #if !defined(HAVE_WIN32)
945 /* Override Bacula default signals */
946 signal(SIGQUIT, SIG_IGN);
947 signal(SIGTSTP, got_sigstop);
948 signal(SIGCONT, got_sigcontinue);
949 signal(SIGTTIN, got_sigtin);
950 signal(SIGTTOU, got_sigtout);
961 if (configfile == NULL) {
962 configfile = bstrdup(CONFIG_FILE);
965 config = new_config_parser();
966 parse_cons_config(config, configfile, M_ERROR_TERM);
968 if (init_crypto() != 0) {
969 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
972 if (!check_resources()) {
973 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
981 terminate_console(0);
985 memset(&jcr, 0, sizeof(jcr));
987 (void)WSA_Init(); /* Initialize Windows sockets */
989 start_watchdog(); /* Start socket watchdog */
993 foreach_res(dir, R_DIRECTOR) {
997 foreach_res(cons, R_CONSOLE) {
1003 struct sockaddr client_addr;
1004 memset(&client_addr, 0, sizeof(client_addr));
1005 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
1007 sendit(_("Available Directors:\n"));
1010 foreach_res(dir, R_DIRECTOR) {
1011 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
1015 if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
1016 (void)WSACleanup(); /* Cleanup Windows sockets */
1019 if (!is_a_number(UA_sock->msg)) {
1020 senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
1021 UA_sock->msg, numdir);
1024 item = atoi(UA_sock->msg);
1025 if (item < 0 || item > numdir) {
1026 senditf(_("You must enter a number between 1 and %d\n"), numdir);
1029 term_bsock(UA_sock);
1031 for (i=0; i<item; i++) {
1032 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
1034 /* Look for a console linked to this director */
1035 for (i=0; i<numcon; i++) {
1036 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1037 if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
1042 /* Look for the first non-linked console */
1044 for (i=0; i<numcon; i++) {
1045 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1046 if (cons->director == NULL)
1053 /* If no director, take first one */
1056 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
1059 /* If no console, take first one */
1062 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
1066 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1069 /* Initialize Console TLS context */
1070 if (cons && (cons->tls_enable || cons->tls_require)) {
1071 /* Generate passphrase prompt */
1072 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1074 /* Initialize TLS context:
1075 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1076 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1078 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1079 cons->tls_ca_certdir, cons->tls_certfile,
1080 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1082 if (!cons->tls_ctx) {
1083 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1085 terminate_console(0);
1090 /* Initialize Director TLS context */
1091 if (dir->tls_enable || dir->tls_require) {
1092 /* Generate passphrase prompt */
1093 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1095 /* Initialize TLS context:
1096 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1097 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1098 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1099 dir->tls_ca_certdir, dir->tls_certfile,
1100 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1102 if (!dir->tls_ctx) {
1103 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1105 terminate_console(0);
1110 if (dir->heartbeat_interval) {
1111 heart_beat = dir->heartbeat_interval;
1113 heart_beat = cons->heartbeat_interval;
1117 UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1118 NULL, dir->DIRport, 0);
1119 if (UA_sock == NULL) {
1120 terminate_console(0);
1123 jcr.dir_bsock = UA_sock;
1125 /* If cons==NULL, default console will be used */
1126 if (!authenticate_director(&jcr, dir, cons)) {
1127 terminate_console(0);
1131 Dmsg0(40, "Opened connection with Director daemon\n");
1133 sendit(_("Enter a period to cancel a command.\n"));
1135 /* Read/Update history file if HOME exists */
1136 POOL_MEM history_file;
1138 /* Run commands in ~/.bconsolerc if any */
1139 char *env = getenv("HOME");
1142 pm_strcpy(&UA_sock->msg, env);
1143 pm_strcat(&UA_sock->msg, "/.bconsolerc");
1144 fd = fopen(UA_sock->msg, "rb");
1146 read_and_process_input(fd, UA_sock);
1150 pm_strcpy(history_file, env);
1151 pm_strcat(history_file, "/.bconsole_history");
1152 console_init_history(history_file.c_str());
1155 read_and_process_input(stdin, UA_sock);
1158 UA_sock->signal(BNET_TERMINATE); /* send EOF */
1163 console_update_history(history_file.c_str());
1166 terminate_console(0);
1170 /* Cleanup and then exit */
1171 static void terminate_console(int sig)
1174 static bool already_here = false;
1176 if (already_here) { /* avoid recursive temination problems */
1179 already_here = true;
1181 config->free_resources();
1185 free_pool_memory(args);
1189 (void)WSACleanup(); /* Cleanup Windows sockets */
1190 lmgr_cleanup_main();
1199 * Make a quick check to see that we have all the
1202 static int check_resources()
1211 foreach_res(director, R_DIRECTOR) {
1214 /* tls_require implies tls_enable */
1215 if (director->tls_require) {
1217 director->tls_enable = true;
1219 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1224 tls_needed = director->tls_enable || director->tls_authenticate;
1226 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1227 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1228 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1229 " At least one CA certificate store is required.\n"),
1230 director->hdr.name, configfile);
1236 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1237 "Without that I don't how to speak to the Director :-(\n"), configfile);
1242 /* Loop over Consoles */
1243 foreach_res(cons, R_CONSOLE) {
1244 /* tls_require implies tls_enable */
1245 if (cons->tls_require) {
1247 cons->tls_enable = true;
1249 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1254 tls_needed = cons->tls_enable || cons->tls_authenticate;
1255 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1256 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1257 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1258 cons->hdr.name, configfile);
1268 static int versioncmd(FILE *input, BSOCK *UA_sock)
1270 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1271 HOST_OS, DISTNAME, DISTVER);
1275 static int inputcmd(FILE *input, BSOCK *UA_sock)
1280 sendit(_("Too many arguments on input command.\n"));
1284 sendit(_("First argument to input command must be a filename.\n"));
1287 fd = fopen(argk[1], "rb");
1290 senditf(_("Cannot open file %s for input. ERR=%s\n"),
1291 argk[1], be.bstrerror());
1294 read_and_process_input(fd, UA_sock);
1299 /* Send output to both termina and specified file */
1300 static int teecmd(FILE *input, BSOCK *UA_sock)
1303 return do_outputcmd(input, UA_sock);
1306 /* Send output to specified "file" */
1307 static int outputcmd(FILE *input, BSOCK *UA_sock)
1310 return do_outputcmd(input, UA_sock);
1314 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1317 const char *mode = "a+b";
1320 sendit(_("Too many arguments on output/tee command.\n"));
1324 if (output != stdout) {
1334 fd = fopen(argk[1], mode);
1337 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1338 argk[1], be.bstrerror(errno));
1346 * exec "some-command" [wait-seconds]
1348 static int execcmd(FILE *input, BSOCK *UA_sock)
1356 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1360 wait = atoi(argk[2]);
1362 bpipe = open_bpipe(argk[1], wait, "r");
1365 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1366 argk[1], be.bstrerror(errno));
1370 while (fgets(line, sizeof(line), bpipe->rfd)) {
1371 senditf("%s", line);
1373 stat = close_bpipe(bpipe);
1377 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1383 static int echocmd(FILE *input, BSOCK *UA_sock)
1385 for (int i=1; i < argc; i++) {
1386 senditf("%s ", argk[i]);
1392 static int quitcmd(FILE *input, BSOCK *UA_sock)
1397 static int helpcmd(FILE *input, BSOCK *UA_sock)
1400 for (i=0; i<comsize; i++) {
1401 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1407 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1410 sleep(atoi(argk[1]));
1416 static int timecmd(FILE *input, BSOCK *UA_sock)
1419 time_t ttime = time(NULL);
1421 (void)localtime_r(&ttime, &tm);
1422 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1428 * Send a line to the output file and or the terminal
1430 void senditf(const char *fmt,...)
1435 va_start(arg_ptr, fmt);
1436 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1441 void sendit(const char *buf)
1445 if (output == stdout || teeout) {
1448 * Here, we convert every \n into \r\n because the
1449 * terminal is in raw mode when we are using
1452 for (p=q=buf; (p=strchr(q, '\n')); ) {
1455 memcpy(obuf, q, len);
1457 memcpy(obuf+len, "\r\n", 3);
1458 q = ++p; /* point after \n */
1459 fputs(obuf, output);
1466 if (output != stdout) {