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, char *name);
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);
201 int len = sizeof_pool_memory(UA_sock->msg) - 1;
202 if (fgets(UA_sock->msg, len, input) == NULL || usrbrk()) {
205 sendit(UA_sock->msg); /* echo to terminal */
206 strip_trailing_junk(UA_sock->msg);
207 UA_sock->msglen = strlen(UA_sock->msg);
212 break; /* error or interrupt */
213 } else if (stat == 0) { /* timeout */
214 if (strcmp(prompt, "*") == 0) {
215 bnet_fsend(UA_sock, ".messages");
221 /* @ => internal command for us */
222 if (UA_sock->msg[0] == '@') {
223 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
224 if (!do_a_command(input, UA_sock)) {
229 if (!bnet_send(UA_sock)) { /* send command */
233 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
236 while ((stat = bnet_recv(UA_sock)) >= 0) {
243 /* Suppress output if running in background or user hit ctl-c */
244 if (!stop && !usrbrk()) {
245 sendit(UA_sock->msg);
251 if (is_bnet_stop(UA_sock)) {
252 break; /* error or term */
253 } else if (stat == BNET_SIGNAL) {
254 if (UA_sock->msglen == BNET_PROMPT) {
257 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
263 /*********************************************************************
265 * Main Bacula Console -- User Interface Program
268 int main(int argc, char *argv[])
270 int ch, i, ndir, item;
271 bool no_signals = false;
272 bool test_config = false;
276 my_name_is(argc, argv, "bconsole");
277 textdomain("bacula-console");
278 init_msg(NULL, NULL);
279 working_directory = "/tmp";
280 args = get_pool_memory(PM_FNAME);
283 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
285 case 'c': /* configuration file */
286 if (configfile != NULL) {
289 configfile = bstrdup(optarg);
293 debug_level = atoi(optarg);
294 if (debug_level <= 0) {
299 case 's': /* turn off signals */
318 init_signals(terminate_console);
321 /* Override Bacula default signals */
322 signal(SIGCHLD, SIG_IGN);
323 signal(SIGTSTP, got_sigstop);
324 signal(SIGCONT, got_sigcontinue);
325 signal(SIGTTIN, got_sigtin);
326 signal(SIGTTOU, got_sigtout);
335 if (configfile == NULL) {
336 configfile = bstrdup(CONFIG_FILE);
339 parse_config(configfile);
343 foreach_res(dir, R_DIRECTOR) {
349 Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
350 Without that I don't how to speak to the Director :-(\n"), configfile);
354 terminate_console(0);
358 memset(&jcr, 0, sizeof(jcr));
362 struct sockaddr_in client_addr;
363 memset(&client_addr, 0, sizeof(client_addr));
364 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
366 sendit(_("Available Directors:\n"));
369 foreach_res(dir, R_DIRECTOR) {
370 senditf( _("%d %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
374 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
377 item = atoi(UA_sock->msg);
378 if (item < 0 || item > ndir) {
379 senditf(_("You must enter a number between 1 and %d\n"), ndir);
384 for (i=0; i<item; i++) {
385 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
391 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
395 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
396 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
397 NULL, dir->DIRport, 0);
398 if (UA_sock == NULL) {
399 terminate_console(0);
402 jcr.dir_bsock = UA_sock;
405 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
409 con_name = cons->hdr.name;
411 con_name = "*UserAgent*";
413 if (!authenticate_director(&jcr, dir, con_name)) {
414 fprintf(stderr, "ERR=%s", UA_sock->msg);
415 terminate_console(0);
419 Dmsg0(40, "Opened connection with Director daemon\n");
421 sendit(_("Enter a period to cancel a command.\n"));
423 /* Run commands in ~/.bconsolerc if any */
424 char *env = getenv("HOME");
427 pm_strcpy(&UA_sock->msg, env);
428 pm_strcat(&UA_sock->msg, "/.bconsolerc");
429 fd = fopen(UA_sock->msg, "r");
431 read_and_process_input(fd, UA_sock);
436 read_and_process_input(stdin, UA_sock);
439 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
443 terminate_console(0);
448 /* Cleanup and then exit */
449 static void terminate_console(int sig)
451 static bool already_here = false;
453 if (already_here) { /* avoid recursive temination problems */
457 free_pool_memory(args);
466 #define READLINE_LIBRARY 1
468 #include "readline.h"
473 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
477 rl_catch_signals = 0; /* do it ourselves */
478 line = readline(prompt);
483 strip_trailing_junk(line);
484 sock->msglen = pm_strcpy(&sock->msg, line);
486 add_history(sock->msg);
492 #else /* no readline, do it ourselves */
495 * Returns: 1 if data available
500 wait_for_data(int fd, int sec)
510 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
511 case 0: /* timeout */
514 if (errno == EINTR || errno == EAGAIN) {
517 return -1; /* error return */
525 * Get next input command from terminal.
527 * Returns: 1 if got input
532 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
536 if (output == stdout || tee) {
541 switch (wait_for_data(fileno(input), sec)) {
543 return 0; /* timeout */
545 return -1; /* error */
547 len = sizeof_pool_memory(sock->msg) - 1;
553 if (isatty(fileno(input))) {
554 input_line(sock->msg, len);
558 if (fgets(sock->msg, len, input) == NULL) {
563 strip_trailing_junk(sock->msg);
564 sock->msglen = strlen(sock->msg);
570 static int versioncmd(FILE *input, BSOCK *UA_sock)
572 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
573 HOST_OS, DISTNAME, DISTVER);
577 static int inputcmd(FILE *input, BSOCK *UA_sock)
582 sendit(_("Too many arguments on input command.\n"));
586 sendit(_("First argument to input command must be a filename.\n"));
589 fd = fopen(argk[1], "r");
591 senditf(_("Cannot open file %s for input. ERR=%s\n"),
592 argk[1], strerror(errno));
595 read_and_process_input(fd, UA_sock);
600 /* Send output to both termina and specified file */
601 static int teecmd(FILE *input, BSOCK *UA_sock)
604 return do_outputcmd(input, UA_sock);
607 /* Send output to specified "file" */
608 static int outputcmd(FILE *input, BSOCK *UA_sock)
611 return do_outputcmd(input, UA_sock);
615 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
621 sendit(_("Too many arguments on output/tee command.\n"));
625 if (output != stdout) {
635 fd = fopen(argk[1], mode);
637 senditf(_("Cannot open file %s for output. ERR=%s\n"),
638 argk[1], strerror(errno));
645 static int quitcmd(FILE *input, BSOCK *UA_sock)
650 static int sleepcmd(FILE *input, BSOCK *UA_sock)
653 sleep(atoi(argk[1]));
659 static int timecmd(FILE *input, BSOCK *UA_sock)
662 time_t ttime = time(NULL);
664 localtime_r(&ttime, &tm);
665 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
671 * Send a line to the output file and or the terminal
673 void senditf(char *fmt,...)
678 va_start(arg_ptr, fmt);
679 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
684 void sendit(char *buf)
687 if (output == stdout || tee) {
690 * Here, we convert every \n into \r\n because the
691 * terminal is in raw mode when we are using
694 for (p=q=buf; (p=strchr(q, '\n')); ) {
699 q = ++p; /* point after \n */
705 if (output != stdout) {