2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
18 * Bacula Console interface to the Director
20 * Kern Sibbald, September MM
25 #include "console_conf.h"
35 #define con_set_zed_keys();
41 #if defined(HAVE_WIN32)
42 #define isatty(fd) (fd==0)
45 /* Exported variables */
47 //extern int rl_catch_signals;
49 /* Imported functions */
50 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
51 extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
53 /* Forward referenced functions */
54 static void terminate_console(int sig);
55 static int check_resources();
56 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
57 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
58 void senditf(const char *fmt, ...);
59 void sendit(const char *buf);
61 extern "C" void got_sigstop(int sig);
62 extern "C" void got_sigcontinue(int sig);
63 extern "C" void got_sigtout(int sig);
64 extern "C" void got_sigtin(int sig);
67 /* Static variables */
68 static char *configfile = NULL;
69 static BSOCK *UA_sock = NULL;
70 static DIRRES *dir = NULL;
71 static CONRES *cons = NULL;
72 static FILE *output = stdout;
73 static bool teeout = false; /* output to output and stdout */
74 static bool stop = false;
75 static bool no_conio = false;
76 static int timeout = 0;
80 static char *argk[MAX_CMD_ARGS];
81 static char *argv[MAX_CMD_ARGS];
82 static CONFIG *config;
85 /* Command prototypes */
86 static int versioncmd(FILE *input, BSOCK *UA_sock);
87 static int inputcmd(FILE *input, BSOCK *UA_sock);
88 static int outputcmd(FILE *input, BSOCK *UA_sock);
89 static int teecmd(FILE *input, BSOCK *UA_sock);
90 static int quitcmd(FILE *input, BSOCK *UA_sock);
91 static int helpcmd(FILE *input, BSOCK *UA_sock);
92 static int echocmd(FILE *input, BSOCK *UA_sock);
93 static int timecmd(FILE *input, BSOCK *UA_sock);
94 static int sleepcmd(FILE *input, BSOCK *UA_sock);
95 static int execcmd(FILE *input, BSOCK *UA_sock);
97 static int eolcmd(FILE *input, BSOCK *UA_sock);
100 # include "lib/bregex.h"
108 #define CONFIG_FILE "bconsole.conf" /* default configuration file */
114 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
115 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
116 " -D <dir> select a Director\n"
117 " -l list Directors defined\n"
118 " -c <file> set configuration file to file\n"
119 " -d <nn> set debug level to <nn>\n"
120 " -dt print timestamp in debug output\n"
123 " -u <nn> set command execution timeout to <nn> seconds\n"
124 " -t test - read configuration and exit\n"
125 " -? print this message.\n"
126 "\n"), 2000, HOST_OS, DISTNAME, DISTVER);
131 void got_sigstop(int sig)
137 void got_sigcontinue(int sig)
143 void got_sigtout(int sig)
145 // printf("Got tout\n");
149 void got_sigtin(int sig)
151 // printf("Got tin\n");
155 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
162 * These are the @command
164 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
165 static struct cmdstruct commands[] = {
166 { N_("input"), inputcmd, _("input from file")},
167 { N_("output"), outputcmd, _("output to file")},
168 { N_("quit"), quitcmd, _("quit")},
169 { N_("tee"), teecmd, _("output to file and terminal")},
170 { N_("sleep"), sleepcmd, _("sleep specified time")},
171 { N_("time"), timecmd, _("print current time")},
172 { N_("version"), versioncmd, _("print Console's version")},
173 { N_("echo"), echocmd, _("echo command string")},
174 { N_("exec"), execcmd, _("execute an external command")},
175 { N_("exit"), quitcmd, _("exit = quit")},
176 { N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
177 { N_("help"), helpcmd, _("help listing")},
179 { N_("separator"), eolcmd, _("set command separator")},
182 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
184 static int do_a_command(FILE *input, BSOCK *UA_sock)
195 Dmsg1(120, "Command: %s\n", UA_sock->msg);
201 if (*cmd == '#') { /* comment */
205 for (i=0; i<comsize; i++) { /* search for command */
206 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
207 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
213 pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
214 UA_sock->msglen = strlen(UA_sock->msg);
215 sendit(UA_sock->msg);
221 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
223 const char *prompt = "*";
224 bool at_prompt = false;
225 int tty_input = isatty(fileno(input));
230 if (at_prompt) { /* don't prompt multiple times */
237 stat = get_cmd(input, prompt, UA_sock, 30);
245 /* Reading input from a file */
246 int len = sizeof_pool_memory(UA_sock->msg) - 1;
250 if (fgets(UA_sock->msg, len, input) == NULL) {
253 sendit(UA_sock->msg); /* echo to terminal */
254 strip_trailing_junk(UA_sock->msg);
255 UA_sock->msglen = strlen(UA_sock->msg);
260 break; /* error or interrupt */
261 } else if (stat == 0) { /* timeout */
262 if (strcmp(prompt, "*") == 0) {
263 tid = start_bsock_timer(UA_sock, timeout);
264 UA_sock->fsend(".messages");
265 stop_bsock_timer(tid);
271 /* @ => internal command for us */
272 if (UA_sock->msg[0] == '@') {
273 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
274 if (!do_a_command(input, UA_sock)) {
279 tid = start_bsock_timer(UA_sock, timeout);
280 if (!UA_sock->send()) { /* send command */
281 stop_bsock_timer(tid);
284 stop_bsock_timer(tid);
286 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
289 tid = start_bsock_timer(UA_sock, timeout);
290 while ((stat = UA_sock->recv()) >= 0) {
297 /* Suppress output if running in background or user hit ctl-c */
298 if (!stop && !usrbrk()) {
299 sendit(UA_sock->msg);
302 stop_bsock_timer(tid);
311 if (UA_sock->is_stop()) {
312 break; /* error or term */
313 } else if (stat == BNET_SIGNAL) {
314 if (UA_sock->msglen == BNET_SUB_PROMPT) {
317 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
323 * Call-back for reading a passphrase for an encrypted PEM file
324 * This function uses getpass(),
325 * which uses a static buffer and is NOT thread-safe.
327 static int tls_pem_callback(char *buf, int size, const void *userdata)
330 const char *prompt = (const char *)userdata;
331 # if defined(HAVE_WIN32)
333 if (win32_cgets(buf, size) == NULL) {
342 passwd = getpass(prompt);
343 bstrncpy(buf, passwd, size);
353 #define READLINE_LIBRARY 1
354 #include "readline.h"
357 /* Get the first keyword of the line */
363 char *first_space = strchr(rl_line_buffer, ' ');
365 len = first_space - rl_line_buffer;
366 ret = (char *) malloc((len + 1) * sizeof(char));
367 memcpy(ret, rl_line_buffer, len);
374 * Return the command before the current point.
375 * Set nb to the number of command to skip
378 get_previous_keyword(int current_point, int nb)
380 int i, end=-1, start, inquotes=0;
384 /* first we look for a space before the current word */
385 for (i = current_point; i >= 0; i--) {
386 if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
391 /* find the end of the command */
392 for (; i >= 0; i--) {
393 if (rl_line_buffer[i] != ' ') {
399 /* no end of string */
404 /* look for the start of the command */
405 for (start = end; start > 0; start--) {
406 if (rl_line_buffer[start] == '"') {
407 inquotes = !inquotes;
409 if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
412 current_point = start;
416 s = (char *)malloc(end - start + 2);
417 memcpy(s, rl_line_buffer + start, end - start + 1);
418 s[end - start + 1] = 0;
420 // printf("=======> %i:%i <%s>\n", start, end, s);
425 /* Simple structure that will contain the completion list */
430 static ItemList *items = NULL;
434 items = (ItemList*) malloc(sizeof(ItemList));
435 memset(items, 0, sizeof(ItemList));
438 items->list.destroy();
444 /* Match a regexp and add the result to the items list
445 * This function is recursive
447 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
451 regmatch_t pmatch[20];
456 rc = regexec(preg, what, nmatch, pmatch, 0);
459 Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n 10 20 30\n", what);
460 Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo);
461 Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo);
462 Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo);
463 Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo);
465 size = pmatch[1].rm_eo - pmatch[1].rm_so;
466 *buf = check_pool_memory_size(*buf, size + 1);
467 memcpy(*buf, what+pmatch[1].rm_so, size);
470 items->list.append(bstrdup(*buf));
471 /* We search for the next keyword in the line */
472 match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf);
476 /* fill the items list with the output of the help command */
477 void get_arguments(const char *what)
484 rc = regcomp(&preg, "(([a-z]+=)|([a-z]+)( |$))", REG_EXTENDED);
489 buf = get_pool_memory(PM_MESSAGE);
490 UA_sock->fsend(".help item=%s", what);
491 while (UA_sock->recv() > 0) {
492 strip_trailing_junk(UA_sock->msg);
493 match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
495 free_pool_memory(buf);
499 /* retreive a simple list (.pool, .client) and store it into items */
500 void get_items(const char *what)
504 UA_sock->fsend("%s", what);
505 while (UA_sock->recv() > 0) {
506 strip_trailing_junk(UA_sock->msg);
507 items->list.append(bstrdup(UA_sock->msg));
513 ITEM_ARG, /* item with simple list like .jobs */
514 ITEM_HELP /* use help item=xxx and detect all arguments */
517 /* Generator function for command completion. STATE lets us know whether
518 * to start from scratch; without any state (i.e. STATE == 0), then we
519 * start at the top of the list.
521 static char *item_generator(const char *text, int state,
522 const char *item, cpl_item_t type)
524 static int list_index, len;
527 /* If this is a new word to complete, initialize now. This includes
528 * saving the length of TEXT for efficiency, and initializing the index
545 /* Return the next name which partially matches from the command list. */
546 while (items && list_index < items->list.size())
548 name = (char *)items->list[list_index];
551 if (strncmp(name, text, len) == 0) {
552 char *ret = (char *) actuallymalloc(strlen(name)+1);
558 /* If no names matched, then return NULL. */
559 return ((char *)NULL);
562 /* gobal variables for the type and the item to search
563 * the readline API doesn' permit to pass user data.
565 static const char *cpl_item;
566 static cpl_item_t cpl_type;
568 static char *cpl_generator(const char *text, int state)
570 return item_generator(text, state, cpl_item, cpl_type);
573 /* this function is used to not use the default filename completion */
574 static char *dummy_completion_function(const char *text, int state)
579 struct cpl_keywords_t {
584 static struct cpl_keywords_t cpl_keywords[] = {
586 {"fileset=", ".fileset" },
587 {"client=", ".client" },
589 {"restore_job=",".jobs type=R" },
590 {"level=", ".level" },
591 {"storage=", ".storage" },
592 {"schedule=", ".schedule" },
593 {"volume=", ".media" },
594 {"oldvolume=", ".media" },
595 {"volstatus=", ".volstatus" },
600 {"unmark", ".lsmark" },
601 {"catalog=", ".catalogs" },
602 {"actiononpurge=", ".actiononpurge" }
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
623 s = get_previous_keyword(start, 0);
624 cmd = get_first_keyword();
626 for (int i=0; i < key_size; i++) {
627 if (!strcasecmp(s, cpl_keywords[i].key)) {
628 cpl_item = cpl_keywords[i].cmd;
630 matches = rl_completion_matches(text, cpl_generator);
636 if (!found) { /* we try to get help with the first command */
638 cpl_type = ITEM_HELP;
639 /* we don't want to append " " at the end */
640 rl_completion_suppress_append=true;
641 matches = rl_completion_matches(text, cpl_generator);
644 } else { /* nothing on the line, display all commands */
645 cpl_item = ".help all";
647 matches = rl_completion_matches(text, cpl_generator);
655 static char eol = '\0';
656 static int eolcmd(FILE *input, BSOCK *UA_sock)
658 if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
660 } else if (argc == 1) {
663 sendit(_("Illegal separator character.\n"));
671 * -1 error (must stop)
674 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
676 static char *line = NULL;
677 static char *next = NULL;
678 static int do_history = 0;
683 rl_catch_signals = 0; /* do it ourselves */
684 /* Here, readline does ***real*** malloc
685 * so, be we have to use the real free
687 line = readline((char *)prompt); /* cast needed for old readlines */
689 return -1; /* error return and exit */
691 strip_trailing_junk(line);
696 sendit(_("Command logic problem\n"));
699 return 0; /* No input */
703 * Split "line" into multiple commands separated by the eol character.
704 * Each part is pointed to by "next" until finally it becomes null.
709 next = strchr(command, eol);
714 if (command != line && isatty(fileno(input))) {
715 senditf("%s%s\n", prompt, command);
718 sock->msglen = pm_strcpy(&sock->msg, command);
727 actuallyfree(line); /* allocated by readline() malloc */
733 #else /* no readline, do it ourselves */
736 static bool bisatty(int fd)
746 * Returns: 1 if data available
751 wait_for_data(int fd, int sec)
753 #if defined(HAVE_WIN32)
763 FD_SET((unsigned)fd, &fdset);
764 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
765 case 0: /* timeout */
768 if (errno == EINTR || errno == EAGAIN) {
771 return -1; /* error return */
780 * Get next input command from terminal.
782 * Returns: 1 if got input
787 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
791 if (output == stdout || teeout) {
796 switch (wait_for_data(fileno(input), sec)) {
798 return 0; /* timeout */
800 return -1; /* error */
802 len = sizeof_pool_memory(sock->msg) - 1;
808 if (bisatty(fileno(input))) {
809 input_line(sock->msg, len);
813 #ifdef HAVE_WIN32 /* use special console for input on win32 */
814 if (input == stdin) {
815 if (win32_cgets(sock->msg, len) == NULL) {
821 if (bfgets(sock->msg, input) == NULL) {
830 strip_trailing_junk(sock->msg);
831 sock->msglen = strlen(sock->msg);
835 #endif /* ! HAVE_READLINE */
838 static int console_update_history(const char *histfile)
844 * first, try to truncate the history file, and if it
845 * fails, the file is probably not present, and we
846 * can use write_history to create it
849 if (history_truncate_file(histfile, 100) == 0) {
850 ret = append_history(history_length, histfile);
852 ret = write_history(histfile);
859 static int console_init_history(const char *histfile)
865 ret = read_history(histfile);
866 /* Tell the completer that we want a complete . */
867 rl_completion_entry_function = dummy_completion_function;
868 rl_attempted_completion_function = readline_completion;
869 rl_filename_completion_desired = 0;
876 bool select_director(const char *director, DIRRES **ret_dir, CONRES **ret_cons)
878 int numcon=0, numdir=0;
889 foreach_res(dir, R_DIRECTOR) {
893 foreach_res(cons, R_CONSOLE) {
898 if (numdir == 1) { /* No choose */
899 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
902 if (director) { /* Command line choice overwrite the no choose option */
904 foreach_res(dir, R_DIRECTOR) {
905 if (bstrcasecmp(dir->hdr.name, director)) {
910 if (!dir) { /* Can't find Director used as argument */
911 senditf(_("Can't find %s in Director list\n"), director);
916 if (dir == NULL) { /* prompt for director */
917 UA_sock = new_bsock();
919 sendit(_("Available Directors:\n"));
922 foreach_res(dir, R_DIRECTOR) {
923 senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name,
924 dir->address, dir->DIRport);
927 if (get_cmd(stdin, _("Select Director by entering a number: "),
930 (void)WSACleanup(); /* Cleanup Windows sockets */
933 if (!is_a_number(UA_sock->msg)) {
934 senditf(_("%s is not a number. You must enter a number between "
936 UA_sock->msg, numdir);
939 item = atoi(UA_sock->msg);
940 if (item < 0 || item > numdir) {
941 senditf(_("You must enter a number between 1 and %d\n"), numdir);
946 for (i=0; i<item; i++) {
947 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
952 /* Look for a console linked to this director */
953 for (i=0; i<numcon; i++) {
954 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
955 if (cons->director && strcasecmp(cons->director, dir->hdr.name) == 0) {
958 if (i == (numcon - 1)) {
963 /* Look for the first non-linked console */
965 for (i=0; i<numcon; i++) {
966 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
967 if (cons->director == NULL) {
970 if (i == (numcon - 1)) {
976 /* If no console, take first one */
978 cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
988 /*********************************************************************
990 * Main Bacula Console -- User Interface Program
993 int main(int argc, char *argv[])
996 char *director = NULL;
997 bool list_directors=false;
998 bool no_signals = false;
999 bool test_config = false;
1003 setlocale(LC_ALL, "");
1004 bindtextdomain("bacula", LOCALEDIR);
1005 textdomain("bacula");
1009 my_name_is(argc, argv, "bconsole");
1010 init_msg(NULL, NULL);
1011 working_directory = "/tmp";
1012 args = get_pool_memory(PM_FNAME);
1014 while ((ch = getopt(argc, argv, "D:lbc:d:nstu:?")) != -1) {
1016 case 'D': /* Director */
1020 director = bstrdup(optarg);
1024 list_directors = true;
1028 case 'c': /* configuration file */
1029 if (configfile != NULL) {
1032 configfile = bstrdup(optarg);
1036 if (*optarg == 't') {
1037 dbg_timestamp = true;
1039 debug_level = atoi(optarg);
1040 if (debug_level <= 0) {
1046 case 'n': /* no conio */
1050 case 's': /* turn off signals */
1059 timeout = atoi(optarg);
1072 init_signals(terminate_console);
1076 #if !defined(HAVE_WIN32)
1077 /* Override Bacula default signals */
1078 signal(SIGQUIT, SIG_IGN);
1079 signal(SIGTSTP, got_sigstop);
1080 signal(SIGCONT, got_sigcontinue);
1081 signal(SIGTTIN, got_sigtin);
1082 signal(SIGTTOU, got_sigtout);
1093 if (configfile == NULL) {
1094 configfile = bstrdup(CONFIG_FILE);
1097 config = new_config_parser();
1098 parse_cons_config(config, configfile, M_ERROR_TERM);
1100 if (init_crypto() != 0) {
1101 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
1104 if (!check_resources()) {
1105 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
1112 if (list_directors) {
1114 foreach_res(dir, R_DIRECTOR) {
1115 senditf("%s\n", dir->hdr.name);
1121 terminate_console(0);
1125 memset(&jcr, 0, sizeof(jcr));
1127 (void)WSA_Init(); /* Initialize Windows sockets */
1129 start_watchdog(); /* Start socket watchdog */
1131 if (!select_director(director, &dir, &cons)) {
1132 terminate_console(0);
1136 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1139 /* Initialize Console TLS context */
1140 if (cons && (cons->tls_enable || cons->tls_require)) {
1141 /* Generate passphrase prompt */
1142 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1144 /* Initialize TLS context:
1145 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1146 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1148 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1149 cons->tls_ca_certdir, cons->tls_certfile,
1150 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1152 if (!cons->tls_ctx) {
1153 senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1155 terminate_console(0);
1160 /* Initialize Director TLS context */
1161 if (dir->tls_enable || dir->tls_require) {
1162 /* Generate passphrase prompt */
1163 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1165 /* Initialize TLS context:
1166 * Args: CA certfile, CA certdir, Certfile, Keyfile,
1167 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1168 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1169 dir->tls_ca_certdir, dir->tls_certfile,
1170 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1172 if (!dir->tls_ctx) {
1173 senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1175 terminate_console(0);
1180 if (dir->heartbeat_interval) {
1181 heart_beat = dir->heartbeat_interval;
1183 heart_beat = cons->heartbeat_interval;
1188 UA_sock = new_bsock();
1190 if (!UA_sock->connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1191 NULL, dir->DIRport, 0)) {
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);
1341 static int versioncmd(FILE *input, BSOCK *UA_sock)
1343 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1344 HOST_OS, DISTNAME, DISTVER);
1348 /* @input <input-filename> */
1349 static int inputcmd(FILE *input, BSOCK *UA_sock)
1354 sendit(_("Too many arguments on input command.\n"));
1358 sendit(_("First argument to input command must be a filename.\n"));
1361 fd = fopen(argk[1], "rb");
1364 senditf(_("Cannot open file %s for input. ERR=%s\n"),
1365 argk[1], be.bstrerror());
1368 read_and_process_input(fd, UA_sock);
1373 /* @tee <output-filename> */
1374 /* Send output to both terminal and specified file */
1375 static int teecmd(FILE *input, BSOCK *UA_sock)
1378 return do_outputcmd(input, UA_sock);
1381 /* @output <output-filename> */
1382 /* Send output to specified "file" */
1383 static int outputcmd(FILE *input, BSOCK *UA_sock)
1386 return do_outputcmd(input, UA_sock);
1390 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1393 const char *mode = "a+b";
1396 sendit(_("Too many arguments on output/tee command.\n"));
1400 if (output != stdout) {
1410 fd = fopen(argk[1], mode);
1413 senditf(_("Cannot open file %s for output. ERR=%s\n"),
1414 argk[1], be.bstrerror(errno));
1422 * @exec "some-command" [wait-seconds]
1424 static int execcmd(FILE *input, BSOCK *UA_sock)
1432 sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1436 wait = atoi(argk[2]);
1438 bpipe = open_bpipe(argk[1], wait, "r");
1441 senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1442 argk[1], be.bstrerror(errno));
1446 while (fgets(line, sizeof(line), bpipe->rfd)) {
1447 senditf("%s", line);
1449 stat = close_bpipe(bpipe);
1453 senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
1460 static int echocmd(FILE *input, BSOCK *UA_sock)
1462 for (int i=1; i < argc; i++) {
1463 senditf("%s ", argk[i]);
1470 static int quitcmd(FILE *input, BSOCK *UA_sock)
1476 static int helpcmd(FILE *input, BSOCK *UA_sock)
1479 for (i=0; i<comsize; i++) {
1480 senditf(" %-10s %s\n", commands[i].key, commands[i].help);
1487 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1490 sleep(atoi(argk[1]));
1496 static int timecmd(FILE *input, BSOCK *UA_sock)
1499 time_t ttime = time(NULL);
1501 (void)localtime_r(&ttime, &tm);
1502 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1508 * Send a line to the output file and or the terminal
1510 void senditf(const char *fmt,...)
1515 va_start(arg_ptr, fmt);
1516 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1521 void sendit(const char *buf)
1525 if (output == stdout || teeout) {
1528 * Here, we convert every \n into \r\n because the
1529 * terminal is in raw mode when we are using
1532 for (p=q=buf; (p=strchr(q, '\n')); ) {
1535 memcpy(obuf, q, len);
1537 memcpy(obuf+len, "\r\n", 3);
1538 q = ++p; /* point after \n */
1539 fputs(obuf, output);
1546 if (output != stdout) {