3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
11 Copyright (C) 2000-2003 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();
45 /* Exported variables */
50 extern int rl_catch_signals;
53 /* Imported functions */
54 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
58 /* Forward referenced functions */
59 static void terminate_console(int sig);
60 int get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec);
61 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
62 void senditf(char *fmt, ...);
63 void sendit(char *buf);
65 /* Static variables */
66 static char *configfile = NULL;
67 static BSOCK *UA_sock = NULL;
69 static FILE *output = stdout;
70 static bool tee = false; /* output to output and stdout */
71 static bool stop = false;
74 static char *argk[MAX_CMD_ARGS];
75 static char *argv[MAX_CMD_ARGS];
78 /* Command prototypes */
79 static int versioncmd(FILE *input, BSOCK *UA_sock);
80 static int inputcmd(FILE *input, BSOCK *UA_sock);
81 static int outputcmd(FILE *input, BSOCK *UA_sock);
82 static int teecmd(FILE *input, BSOCK *UA_sock);
83 static int quitcmd(FILE *input, BSOCK *UA_sock);
84 static int timecmd(FILE *input, BSOCK *UA_sock);
85 static int sleepcmd(FILE *input, BSOCK *UA_sock);
88 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
93 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
94 "Usage: bconsole [-s] [-c config_file] [-d debug_level] [config_file]\n"
95 " -c <file> set configuration file to file\n"
96 " -dnn set debug level to nn\n"
98 " -t test - read configuration and exit\n"
99 " -? print this message.\n"
100 "\n"), HOST_OS, DISTNAME, DISTVER);
103 void got_sigstop(int sig)
108 void got_sigcontinue(int sig)
113 void got_sigtout(int sig)
115 // printf("Got tout\n");
118 void got_sigtin(int sig)
120 // printf("Got tin\n");
123 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
130 * These are the @command
132 struct cmdstruct { char *key; int (*func)(FILE *input, BSOCK *UA_sock); char *help; };
133 static struct cmdstruct commands[] = {
134 { N_("input"), inputcmd, _("input from file")},
135 { N_("output"), outputcmd, _("output to file")},
136 { N_("quit"), quitcmd, _("quit")},
137 { N_("tee"), teecmd, _("output to file and terminal")},
138 { N_("sleep"), sleepcmd, _("sleep specified time")},
139 { N_("time"), timecmd, _("print current time")},
140 { N_("version"), versioncmd, _("print Console's version")},
141 { N_("exit"), quitcmd, _("exit = quit")},
142 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
144 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
146 static int do_a_command(FILE *input, BSOCK *UA_sock)
157 Dmsg1(120, "Command: %s\n", UA_sock->msg);
163 if (*cmd == '#') { /* comment */
167 for (i=0; i<comsize; i++) { /* search for command */
168 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
169 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
175 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
176 UA_sock->msglen = strlen(UA_sock->msg);
177 sendit(UA_sock->msg);
183 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
186 bool at_prompt = false;
187 int tty_input = isatty(fileno(input));
191 if (at_prompt) { /* don't prompt multiple times */
198 stat = get_cmd(input, prompt, UA_sock, 30);
206 /* Reading input from a file */
207 int len = sizeof_pool_memory(UA_sock->msg) - 1;
211 if (fgets(UA_sock->msg, len, input) == NULL) {
214 sendit(UA_sock->msg); /* echo to terminal */
215 strip_trailing_junk(UA_sock->msg);
216 UA_sock->msglen = strlen(UA_sock->msg);
221 break; /* error or interrupt */
222 } else if (stat == 0) { /* timeout */
223 if (strcmp(prompt, "*") == 0) {
224 bnet_fsend(UA_sock, ".messages");
230 /* @ => internal command for us */
231 if (UA_sock->msg[0] == '@') {
232 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
233 if (!do_a_command(input, UA_sock)) {
238 if (!bnet_send(UA_sock)) { /* send command */
242 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
245 while ((stat = bnet_recv(UA_sock)) >= 0) {
252 /* Suppress output if running in background or user hit ctl-c */
253 if (!stop && !usrbrk()) {
254 sendit(UA_sock->msg);
265 if (is_bnet_stop(UA_sock)) {
266 break; /* error or term */
267 } else if (stat == BNET_SIGNAL) {
268 if (UA_sock->msglen == BNET_PROMPT) {
271 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
277 /*********************************************************************
279 * Main Bacula Console -- User Interface Program
282 int main(int argc, char *argv[])
284 int ch, i, ndir, item;
285 bool no_signals = false;
286 bool test_config = false;
290 my_name_is(argc, argv, "bconsole");
291 textdomain("bacula-console");
292 init_msg(NULL, NULL);
293 working_directory = "/tmp";
294 args = get_pool_memory(PM_FNAME);
297 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
299 case 'c': /* configuration file */
300 if (configfile != NULL) {
303 configfile = bstrdup(optarg);
307 debug_level = atoi(optarg);
308 if (debug_level <= 0) {
313 case 's': /* turn off signals */
332 init_signals(terminate_console);
335 /* Override Bacula default signals */
336 signal(SIGCHLD, SIG_IGN);
337 signal(SIGQUIT, SIG_IGN);
338 signal(SIGTSTP, got_sigstop);
339 signal(SIGCONT, got_sigcontinue);
340 signal(SIGTTIN, got_sigtin);
341 signal(SIGTTOU, got_sigtout);
350 if (configfile == NULL) {
351 configfile = bstrdup(CONFIG_FILE);
354 parse_config(configfile);
358 foreach_res(dir, R_DIRECTOR) {
364 Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
365 Without that I don't how to speak to the Director :-(\n"), configfile);
369 terminate_console(0);
373 memset(&jcr, 0, sizeof(jcr));
377 struct sockaddr_in client_addr;
378 memset(&client_addr, 0, sizeof(client_addr));
379 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
381 sendit(_("Available Directors:\n"));
384 foreach_res(dir, R_DIRECTOR) {
385 senditf( _("%d %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
389 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
392 item = atoi(UA_sock->msg);
393 if (item < 0 || item > ndir) {
394 senditf(_("You must enter a number between 1 and %d\n"), ndir);
399 for (i=0; i<item; i++) {
400 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
406 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
410 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
411 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
412 NULL, dir->DIRport, 0);
413 if (UA_sock == NULL) {
414 terminate_console(0);
417 jcr.dir_bsock = UA_sock;
420 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
422 /* If cons==NULL, default console will be used */
423 if (!authenticate_director(&jcr, dir, cons)) {
424 fprintf(stderr, "ERR=%s", UA_sock->msg);
425 terminate_console(0);
429 Dmsg0(40, "Opened connection with Director daemon\n");
431 sendit(_("Enter a period to cancel a command.\n"));
433 /* Run commands in ~/.bconsolerc if any */
434 char *env = getenv("HOME");
437 pm_strcpy(&UA_sock->msg, env);
438 pm_strcat(&UA_sock->msg, "/.bconsolerc");
439 fd = fopen(UA_sock->msg, "r");
441 read_and_process_input(fd, UA_sock);
446 read_and_process_input(stdin, UA_sock);
449 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
453 terminate_console(0);
458 /* Cleanup and then exit */
459 static void terminate_console(int sig)
461 static bool already_here = false;
463 if (already_here) { /* avoid recursive temination problems */
467 free_pool_memory(args);
476 #define READLINE_LIBRARY 1
478 #include "readline.h"
483 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
487 rl_catch_signals = 0; /* do it ourselves */
488 line = readline(prompt);
493 strip_trailing_junk(line);
494 sock->msglen = pm_strcpy(&sock->msg, line);
496 add_history(sock->msg);
502 #else /* no readline, do it ourselves */
505 * Returns: 1 if data available
510 wait_for_data(int fd, int sec)
520 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
521 case 0: /* timeout */
524 if (errno == EINTR || errno == EAGAIN) {
527 return -1; /* error return */
535 * Get next input command from terminal.
537 * Returns: 1 if got input
542 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
546 if (output == stdout || tee) {
551 switch (wait_for_data(fileno(input), sec)) {
553 return 0; /* timeout */
555 return -1; /* error */
557 len = sizeof_pool_memory(sock->msg) - 1;
563 if (isatty(fileno(input))) {
564 input_line(sock->msg, len);
568 if (fgets(sock->msg, len, input) == NULL) {
576 strip_trailing_junk(sock->msg);
577 sock->msglen = strlen(sock->msg);
583 static int versioncmd(FILE *input, BSOCK *UA_sock)
585 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
586 HOST_OS, DISTNAME, DISTVER);
590 static int inputcmd(FILE *input, BSOCK *UA_sock)
595 sendit(_("Too many arguments on input command.\n"));
599 sendit(_("First argument to input command must be a filename.\n"));
602 fd = fopen(argk[1], "r");
604 senditf(_("Cannot open file %s for input. ERR=%s\n"),
605 argk[1], strerror(errno));
608 read_and_process_input(fd, UA_sock);
613 /* Send output to both termina and specified file */
614 static int teecmd(FILE *input, BSOCK *UA_sock)
617 return do_outputcmd(input, UA_sock);
620 /* Send output to specified "file" */
621 static int outputcmd(FILE *input, BSOCK *UA_sock)
624 return do_outputcmd(input, UA_sock);
628 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
634 sendit(_("Too many arguments on output/tee command.\n"));
638 if (output != stdout) {
648 fd = fopen(argk[1], mode);
650 senditf(_("Cannot open file %s for output. ERR=%s\n"),
651 argk[1], strerror(errno));
658 static int quitcmd(FILE *input, BSOCK *UA_sock)
663 static int sleepcmd(FILE *input, BSOCK *UA_sock)
666 sleep(atoi(argk[1]));
672 static int timecmd(FILE *input, BSOCK *UA_sock)
675 time_t ttime = time(NULL);
677 localtime_r(&ttime, &tm);
678 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
684 * Send a line to the output file and or the terminal
686 void senditf(char *fmt,...)
691 va_start(arg_ptr, fmt);
692 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
697 void sendit(char *buf)
700 if (output == stdout || tee) {
703 * Here, we convert every \n into \r\n because the
704 * terminal is in raw mode when we are using
707 for (p=q=buf; (p=strchr(q, '\n')); ) {
712 q = ++p; /* point after \n */
718 if (output != stdout) {