3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
11 Copyright (C) 2000-2004 Kern Sibbald and John Walker
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2.1 of the License, or (at your option) any later version.
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the Free
25 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
31 #include "console_conf.h"
39 #define con_set_zed_keys();
47 #define isatty(fd) (fd==0)
48 DWORD g_platform_id = VER_PLATFORM_WIN32_WINDOWS;
51 /* Exported variables */
56 extern int rl_catch_signals;
59 /* Imported functions */
60 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
64 /* Forward referenced functions */
65 static void terminate_console(int sig);
66 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
67 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
68 void senditf(const char *fmt, ...);
69 void sendit(const char *buf);
71 /* Static variables */
72 static char *configfile = NULL;
73 static BSOCK *UA_sock = NULL;
75 static FILE *output = stdout;
76 static bool tee = false; /* output to output and stdout */
77 static bool stop = false;
80 static char *argk[MAX_CMD_ARGS];
81 static char *argv[MAX_CMD_ARGS];
84 /* Command prototypes */
85 static int versioncmd(FILE *input, BSOCK *UA_sock);
86 static int inputcmd(FILE *input, BSOCK *UA_sock);
87 static int outputcmd(FILE *input, BSOCK *UA_sock);
88 static int teecmd(FILE *input, BSOCK *UA_sock);
89 static int quitcmd(FILE *input, BSOCK *UA_sock);
90 static int timecmd(FILE *input, BSOCK *UA_sock);
91 static int sleepcmd(FILE *input, BSOCK *UA_sock);
94 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
99 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
100 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
101 " -c <file> set configuration file to file\n"
102 " -dnn set debug level to nn\n"
104 " -t test - read configuration and exit\n"
105 " -? print this message.\n"
106 "\n"), HOST_OS, DISTNAME, DISTVER);
109 void got_sigstop(int sig)
114 void got_sigcontinue(int sig)
119 void got_sigtout(int sig)
121 // printf("Got tout\n");
124 void got_sigtin(int sig)
126 // printf("Got tin\n");
129 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
136 * These are the @command
138 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
139 static struct cmdstruct commands[] = {
140 { N_("input"), inputcmd, _("input from file")},
141 { N_("output"), outputcmd, _("output to file")},
142 { N_("quit"), quitcmd, _("quit")},
143 { N_("tee"), teecmd, _("output to file and terminal")},
144 { N_("sleep"), sleepcmd, _("sleep specified time")},
145 { N_("time"), timecmd, _("print current time")},
146 { N_("version"), versioncmd, _("print Console's version")},
147 { N_("exit"), quitcmd, _("exit = quit")},
148 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
150 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
152 static int do_a_command(FILE *input, BSOCK *UA_sock)
163 Dmsg1(120, "Command: %s\n", UA_sock->msg);
169 if (*cmd == '#') { /* comment */
173 for (i=0; i<comsize; i++) { /* search for command */
174 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
175 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
181 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
182 UA_sock->msglen = strlen(UA_sock->msg);
183 sendit(UA_sock->msg);
189 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
191 const char *prompt = "*";
192 bool at_prompt = false;
193 int tty_input = isatty(fileno(input));
197 if (at_prompt) { /* don't prompt multiple times */
204 stat = get_cmd(input, prompt, UA_sock, 30);
212 /* Reading input from a file */
213 int len = sizeof_pool_memory(UA_sock->msg) - 1;
217 if (fgets(UA_sock->msg, len, input) == NULL) {
220 sendit(UA_sock->msg); /* echo to terminal */
221 strip_trailing_junk(UA_sock->msg);
222 UA_sock->msglen = strlen(UA_sock->msg);
227 break; /* error or interrupt */
228 } else if (stat == 0) { /* timeout */
229 if (strcmp(prompt, "*") == 0) {
230 bnet_fsend(UA_sock, ".messages");
236 /* @ => internal command for us */
237 if (UA_sock->msg[0] == '@') {
238 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
239 if (!do_a_command(input, UA_sock)) {
244 if (!bnet_send(UA_sock)) { /* send command */
248 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
251 while ((stat = bnet_recv(UA_sock)) >= 0) {
258 /* Suppress output if running in background or user hit ctl-c */
259 if (!stop && !usrbrk()) {
260 sendit(UA_sock->msg);
271 if (is_bnet_stop(UA_sock)) {
272 break; /* error or term */
273 } else if (stat == BNET_SIGNAL) {
274 if (UA_sock->msglen == BNET_PROMPT) {
277 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
283 /*********************************************************************
285 * Main Bacula Console -- User Interface Program
288 int main(int argc, char *argv[])
290 int ch, i, ndir, item;
291 bool no_signals = false;
292 bool test_config = false;
296 my_name_is(argc, argv, "bconsole");
297 textdomain("bacula");
298 init_msg(NULL, NULL);
299 working_directory = "/tmp";
300 args = get_pool_memory(PM_FNAME);
303 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
305 case 'c': /* configuration file */
306 if (configfile != NULL) {
309 configfile = bstrdup(optarg);
313 debug_level = atoi(optarg);
314 if (debug_level <= 0) {
319 case 's': /* turn off signals */
338 init_signals(terminate_console);
341 #if !defined(HAVE_WIN32)
342 /* Override Bacula default signals */
343 signal(SIGCHLD, SIG_IGN);
344 signal(SIGQUIT, SIG_IGN);
345 signal(SIGTSTP, got_sigstop);
346 signal(SIGCONT, got_sigcontinue);
347 signal(SIGTTIN, got_sigtin);
348 signal(SIGTTOU, got_sigtout);
358 if (configfile == NULL) {
359 configfile = bstrdup(CONFIG_FILE);
362 parse_config(configfile);
366 foreach_res(dir, R_DIRECTOR) {
372 Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
373 Without that I don't how to speak to the Director :-(\n"), configfile);
377 terminate_console(0);
381 memset(&jcr, 0, sizeof(jcr));
383 (void)WSA_Init(); /* Initialize Windows sockets */
386 struct sockaddr_in client_addr;
387 memset(&client_addr, 0, sizeof(client_addr));
388 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
390 sendit(_("Available Directors:\n"));
393 foreach_res(dir, R_DIRECTOR) {
394 senditf( _("%d %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
398 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
399 (void)WSACleanup(); /* Cleanup Windows sockets */
402 item = atoi(UA_sock->msg);
403 if (item < 0 || item > ndir) {
404 senditf(_("You must enter a number between 1 and %d\n"), ndir);
409 for (i=0; i<item; i++) {
410 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
416 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
420 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
421 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
422 NULL, dir->DIRport, 0);
423 if (UA_sock == NULL) {
424 terminate_console(0);
427 jcr.dir_bsock = UA_sock;
430 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
432 /* If cons==NULL, default console will be used */
433 if (!authenticate_director(&jcr, dir, cons)) {
434 fprintf(stderr, "ERR=%s", UA_sock->msg);
435 terminate_console(0);
439 Dmsg0(40, "Opened connection with Director daemon\n");
441 sendit(_("Enter a period to cancel a command.\n"));
443 /* Run commands in ~/.bconsolerc if any */
444 char *env = getenv("HOME");
447 pm_strcpy(&UA_sock->msg, env);
448 pm_strcat(&UA_sock->msg, "/.bconsolerc");
449 fd = fopen(UA_sock->msg, "r");
451 read_and_process_input(fd, UA_sock);
456 read_and_process_input(stdin, UA_sock);
459 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
463 terminate_console(0);
468 /* Cleanup and then exit */
469 static void terminate_console(int sig)
472 static bool already_here = false;
474 if (already_here) { /* avoid recursive temination problems */
478 free_pool_memory(args);
480 (void)WSACleanup(); /* Cleanup Windows sockets */
488 #define READLINE_LIBRARY 1
490 #include "readline.h"
495 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
499 rl_catch_signals = 0; /* do it ourselves */
500 line = readline(prompt);
505 strip_trailing_junk(line);
506 sock->msglen = pm_strcpy(&sock->msg, line);
508 add_history(sock->msg);
514 #else /* no readline, do it ourselves */
517 * Returns: 1 if data available
522 wait_for_data(int fd, int sec)
527 return 1; /* select doesn't seem to work on Win32 */
535 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
536 case 0: /* timeout */
539 if (errno == EINTR || errno == EAGAIN) {
542 return -1; /* error return */
550 * Get next input command from terminal.
552 * Returns: 1 if got input
557 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
561 if (output == stdout || tee) {
566 switch (wait_for_data(fileno(input), sec)) {
568 return 0; /* timeout */
570 return -1; /* error */
572 len = sizeof_pool_memory(sock->msg) - 1;
578 if (isatty(fileno(input))) {
579 input_line(sock->msg, len);
583 if (fgets(sock->msg, len, input) == NULL) {
591 strip_trailing_junk(sock->msg);
592 sock->msglen = strlen(sock->msg);
598 static int versioncmd(FILE *input, BSOCK *UA_sock)
600 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
601 HOST_OS, DISTNAME, DISTVER);
605 static int inputcmd(FILE *input, BSOCK *UA_sock)
610 sendit(_("Too many arguments on input command.\n"));
614 sendit(_("First argument to input command must be a filename.\n"));
617 fd = fopen(argk[1], "r");
619 senditf(_("Cannot open file %s for input. ERR=%s\n"),
620 argk[1], strerror(errno));
623 read_and_process_input(fd, UA_sock);
628 /* Send output to both termina and specified file */
629 static int teecmd(FILE *input, BSOCK *UA_sock)
632 return do_outputcmd(input, UA_sock);
635 /* Send output to specified "file" */
636 static int outputcmd(FILE *input, BSOCK *UA_sock)
639 return do_outputcmd(input, UA_sock);
643 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
646 const char *mode = "a+";
649 sendit(_("Too many arguments on output/tee command.\n"));
653 if (output != stdout) {
663 fd = fopen(argk[1], mode);
665 senditf(_("Cannot open file %s for output. ERR=%s\n"),
666 argk[1], strerror(errno));
673 static int quitcmd(FILE *input, BSOCK *UA_sock)
678 static int sleepcmd(FILE *input, BSOCK *UA_sock)
681 sleep(atoi(argk[1]));
687 static int timecmd(FILE *input, BSOCK *UA_sock)
690 time_t ttime = time(NULL);
692 localtime_r(&ttime, &tm);
693 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
699 * Send a line to the output file and or the terminal
701 void senditf(const char *fmt,...)
706 va_start(arg_ptr, fmt);
707 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
712 void sendit(const char *buf)
715 if (output == stdout || tee) {
718 * Here, we convert every \n into \r\n because the
719 * terminal is in raw mode when we are using
722 for (p=q=buf; (p=strchr(q, '\n')); ) {
727 q = ++p; /* point after \n */
733 if (output != stdout) {
741 if (output == stdout || tee) {