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 extern "C" void got_sigstop(int sig);
72 extern "C" void got_sigcontinue(int sig);
73 extern "C" void got_sigtout(int sig);
74 extern "C" void got_sigtin(int sig);
77 /* Static variables */
78 static char *configfile = NULL;
79 static BSOCK *UA_sock = NULL;
81 static FILE *output = stdout;
82 static bool tee = false; /* output to output and stdout */
83 static bool stop = false;
86 static char *argk[MAX_CMD_ARGS];
87 static char *argv[MAX_CMD_ARGS];
90 /* Command prototypes */
91 static int versioncmd(FILE *input, BSOCK *UA_sock);
92 static int inputcmd(FILE *input, BSOCK *UA_sock);
93 static int outputcmd(FILE *input, BSOCK *UA_sock);
94 static int teecmd(FILE *input, BSOCK *UA_sock);
95 static int quitcmd(FILE *input, BSOCK *UA_sock);
96 static int timecmd(FILE *input, BSOCK *UA_sock);
97 static int sleepcmd(FILE *input, BSOCK *UA_sock);
100 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
105 "Copyright (C) 2000-2004 Kern Sibbald and John Walker\n"
106 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
107 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
108 " -c <file> set configuration file to file\n"
109 " -dnn set debug level to nn\n"
111 " -t test - read configuration and exit\n"
112 " -? print this message.\n"
113 "\n"), HOST_OS, DISTNAME, DISTVER);
118 void got_sigstop(int sig)
124 void got_sigcontinue(int sig)
130 void got_sigtout(int sig)
132 // printf("Got tout\n");
136 void got_sigtin(int sig)
138 // printf("Got tin\n");
142 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
149 * These are the @command
151 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
152 static struct cmdstruct commands[] = {
153 { N_("input"), inputcmd, _("input from file")},
154 { N_("output"), outputcmd, _("output to file")},
155 { N_("quit"), quitcmd, _("quit")},
156 { N_("tee"), teecmd, _("output to file and terminal")},
157 { N_("sleep"), sleepcmd, _("sleep specified time")},
158 { N_("time"), timecmd, _("print current time")},
159 { N_("version"), versioncmd, _("print Console's version")},
160 { N_("exit"), quitcmd, _("exit = quit")},
161 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
163 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
165 static int do_a_command(FILE *input, BSOCK *UA_sock)
176 Dmsg1(120, "Command: %s\n", UA_sock->msg);
182 if (*cmd == '#') { /* comment */
186 for (i=0; i<comsize; i++) { /* search for command */
187 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
188 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
194 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
195 UA_sock->msglen = strlen(UA_sock->msg);
196 sendit(UA_sock->msg);
202 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
204 const char *prompt = "*";
205 bool at_prompt = false;
206 int tty_input = isatty(fileno(input));
210 if (at_prompt) { /* don't prompt multiple times */
217 stat = get_cmd(input, prompt, UA_sock, 30);
225 /* Reading input from a file */
226 int len = sizeof_pool_memory(UA_sock->msg) - 1;
230 if (fgets(UA_sock->msg, len, input) == NULL) {
233 sendit(UA_sock->msg); /* echo to terminal */
234 strip_trailing_junk(UA_sock->msg);
235 UA_sock->msglen = strlen(UA_sock->msg);
240 break; /* error or interrupt */
241 } else if (stat == 0) { /* timeout */
242 if (strcmp(prompt, "*") == 0) {
243 bnet_fsend(UA_sock, ".messages");
249 /* @ => internal command for us */
250 if (UA_sock->msg[0] == '@') {
251 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
252 if (!do_a_command(input, UA_sock)) {
257 if (!bnet_send(UA_sock)) { /* send command */
261 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
264 while ((stat = bnet_recv(UA_sock)) >= 0) {
271 /* Suppress output if running in background or user hit ctl-c */
272 if (!stop && !usrbrk()) {
273 sendit(UA_sock->msg);
284 if (is_bnet_stop(UA_sock)) {
285 break; /* error or term */
286 } else if (stat == BNET_SIGNAL) {
287 if (UA_sock->msglen == BNET_PROMPT) {
290 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
296 /*********************************************************************
298 * Main Bacula Console -- User Interface Program
301 int main(int argc, char *argv[])
303 int ch, i, ndir, item;
304 bool no_signals = false;
305 bool test_config = false;
309 my_name_is(argc, argv, "bconsole");
310 textdomain("bacula");
311 init_msg(NULL, NULL);
312 working_directory = "/tmp";
313 args = get_pool_memory(PM_FNAME);
316 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
318 case 'c': /* configuration file */
319 if (configfile != NULL) {
322 configfile = bstrdup(optarg);
326 debug_level = atoi(optarg);
327 if (debug_level <= 0) {
332 case 's': /* turn off signals */
351 init_signals(terminate_console);
354 #if !defined(HAVE_WIN32)
355 /* Override Bacula default signals */
356 signal(SIGCHLD, SIG_IGN);
357 signal(SIGQUIT, SIG_IGN);
358 signal(SIGTSTP, got_sigstop);
359 signal(SIGCONT, got_sigcontinue);
360 signal(SIGTTIN, got_sigtin);
361 signal(SIGTTOU, got_sigtout);
371 if (configfile == NULL) {
372 configfile = bstrdup(CONFIG_FILE);
375 parse_config(configfile);
379 foreach_res(dir, R_DIRECTOR) {
385 Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
386 Without that I don't how to speak to the Director :-(\n"), configfile);
390 terminate_console(0);
394 memset(&jcr, 0, sizeof(jcr));
396 (void)WSA_Init(); /* Initialize Windows sockets */
399 struct sockaddr client_addr;
400 memset(&client_addr, 0, sizeof(client_addr));
401 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
403 sendit(_("Available Directors:\n"));
406 foreach_res(dir, R_DIRECTOR) {
407 senditf( _("%d %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
411 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
412 (void)WSACleanup(); /* Cleanup Windows sockets */
415 item = atoi(UA_sock->msg);
416 if (item < 0 || item > ndir) {
417 senditf(_("You must enter a number between 1 and %d\n"), ndir);
422 for (i=0; i<item; i++) {
423 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
429 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
433 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
434 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
435 NULL, dir->DIRport, 0);
436 if (UA_sock == NULL) {
437 terminate_console(0);
440 jcr.dir_bsock = UA_sock;
443 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
445 /* If cons==NULL, default console will be used */
446 if (!authenticate_director(&jcr, dir, cons)) {
447 fprintf(stderr, "ERR=%s", UA_sock->msg);
448 terminate_console(0);
452 Dmsg0(40, "Opened connection with Director daemon\n");
454 sendit(_("Enter a period to cancel a command.\n"));
456 /* Run commands in ~/.bconsolerc if any */
457 char *env = getenv("HOME");
460 pm_strcpy(&UA_sock->msg, env);
461 pm_strcat(&UA_sock->msg, "/.bconsolerc");
462 fd = fopen(UA_sock->msg, "r");
464 read_and_process_input(fd, UA_sock);
469 read_and_process_input(stdin, UA_sock);
472 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
476 terminate_console(0);
481 /* Cleanup and then exit */
482 static void terminate_console(int sig)
485 static bool already_here = false;
487 if (already_here) { /* avoid recursive temination problems */
491 free_pool_memory(args);
493 (void)WSACleanup(); /* Cleanup Windows sockets */
501 #define READLINE_LIBRARY 1
503 #include "readline.h"
508 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
512 rl_catch_signals = 0; /* do it ourselves */
513 line = readline((char *)prompt); /* cast needed for old readlines */
518 strip_trailing_junk(line);
519 sock->msglen = pm_strcpy(&sock->msg, line);
521 add_history(sock->msg);
527 #else /* no readline, do it ourselves */
530 * Returns: 1 if data available
535 wait_for_data(int fd, int sec)
540 return 1; /* select doesn't seem to work on Win32 */
547 FD_SET((unsigned)fd, &fdset);
548 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
549 case 0: /* timeout */
552 if (errno == EINTR || errno == EAGAIN) {
555 return -1; /* error return */
563 * Get next input command from terminal.
565 * Returns: 1 if got input
570 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
574 if (output == stdout || tee) {
579 switch (wait_for_data(fileno(input), sec)) {
581 return 0; /* timeout */
583 return -1; /* error */
585 len = sizeof_pool_memory(sock->msg) - 1;
591 if (isatty(fileno(input))) {
592 input_line(sock->msg, len);
596 if (fgets(sock->msg, len, input) == NULL) {
604 strip_trailing_junk(sock->msg);
605 sock->msglen = strlen(sock->msg);
611 static int versioncmd(FILE *input, BSOCK *UA_sock)
613 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
614 HOST_OS, DISTNAME, DISTVER);
618 static int inputcmd(FILE *input, BSOCK *UA_sock)
623 sendit(_("Too many arguments on input command.\n"));
627 sendit(_("First argument to input command must be a filename.\n"));
630 fd = fopen(argk[1], "r");
632 senditf(_("Cannot open file %s for input. ERR=%s\n"),
633 argk[1], strerror(errno));
636 read_and_process_input(fd, UA_sock);
641 /* Send output to both termina and specified file */
642 static int teecmd(FILE *input, BSOCK *UA_sock)
645 return do_outputcmd(input, UA_sock);
648 /* Send output to specified "file" */
649 static int outputcmd(FILE *input, BSOCK *UA_sock)
652 return do_outputcmd(input, UA_sock);
656 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
659 const char *mode = "a+";
662 sendit(_("Too many arguments on output/tee command.\n"));
666 if (output != stdout) {
676 fd = fopen(argk[1], mode);
678 senditf(_("Cannot open file %s for output. ERR=%s\n"),
679 argk[1], strerror(errno));
686 static int quitcmd(FILE *input, BSOCK *UA_sock)
691 static int sleepcmd(FILE *input, BSOCK *UA_sock)
694 sleep(atoi(argk[1]));
700 static int timecmd(FILE *input, BSOCK *UA_sock)
703 time_t ttime = time(NULL);
705 localtime_r(&ttime, &tm);
706 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
712 * Send a line to the output file and or the terminal
714 void senditf(const char *fmt,...)
719 va_start(arg_ptr, fmt);
720 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
725 void sendit(const char *buf)
728 if (output == stdout || tee) {
731 * Here, we convert every \n into \r\n because the
732 * terminal is in raw mode when we are using
735 for (p=q=buf; (p=strchr(q, '\n')); ) {
740 q = ++p; /* point after \n */
746 if (output != stdout) {
754 if (output == stdout || tee) {