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"
34 /* Imported functions */
35 int authenticate_director(JCR *jcr, DIRRES *director);
38 /* Exported variables */
44 extern int rl_catch_signals;
47 /* Forward referenced functions */
48 static void terminate_console(int sig);
49 int get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec);
50 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
51 static void sendit(char *fmt, ...);
53 /* Static variables */
54 static char *configfile = NULL;
55 static BSOCK *UA_sock = NULL;
57 static FILE *output = stdout;
58 int tee = 0; /* output to output and stdout */
59 static int stop = FALSE;
62 static char *argk[MAX_CMD_ARGS];
63 static char *argv[MAX_CMD_ARGS];
65 /* Command prototypes */
66 static int versioncmd(FILE *input, BSOCK *UA_sock);
67 static int inputcmd(FILE *input, BSOCK *UA_sock);
68 static int outputcmd(FILE *input, BSOCK *UA_sock);
69 static int teecmd(FILE *input, BSOCK *UA_sock);
70 static int quitcmd(FILE *input, BSOCK *UA_sock);
71 static int timecmd(FILE *input, BSOCK *UA_sock);
72 static int sleepcmd(FILE *input, BSOCK *UA_sock);
75 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
80 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
81 "Usage: bconsole [-s] [-c config_file] [-d debug_level] [config_file]\n"
82 " -c <file> set configuration file to file\n"
83 " -dnn set debug level to nn\n"
85 " -t test - read configuration and exit\n"
86 " -? print this message.\n"
87 "\n"), HOST_OS, DISTNAME, DISTVER);
93 void got_stop(int sig)
98 void got_continue(int sig)
103 void got_tout(int sig)
105 // printf("Got tout\n");
108 void got_tin(int sig)
110 // printf("Got tin\n");
113 struct cmdstruct { char *key; int (*func)(FILE *input, BSOCK *UA_sock); char *help; };
114 static struct cmdstruct commands[] = {
115 { N_("input"), inputcmd, _("input from file")},
116 { N_("output"), outputcmd, _("output to file")},
117 { N_("quit"), quitcmd, _("quit")},
118 { N_("tee"), teecmd, _("output to file and terminal")},
119 { N_("sleep"), sleepcmd, _("sleep specified time")},
120 { N_("time"), timecmd, _("print current time")},
121 { N_("version"), versioncmd, _("print Console's version")},
122 { N_("exit"), quitcmd, _("exit = quit")},
124 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
126 static int do_a_command(FILE *input, BSOCK *UA_sock)
137 Dmsg1(120, "Command: %s\n", UA_sock->msg);
143 if (*cmd == '#') { /* comment */
147 for (i=0; i<comsize; i++) { /* search for command */
148 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
149 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
155 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
156 UA_sock->msglen = strlen(UA_sock->msg);
157 fputs(UA_sock->msg, output);
164 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
167 int at_prompt = FALSE;
168 int tty_input = isatty(fileno(input));
172 if (at_prompt) { /* don't prompt multiple times */
179 stat = get_cmd(input, prompt, UA_sock, 30);
181 int len = sizeof_pool_memory(UA_sock->msg) - 1;
182 if (fgets(UA_sock->msg, len, input) == NULL) {
185 sendit("%s", UA_sock->msg); /* echo to terminal */
186 strip_trailing_junk(UA_sock->msg);
187 UA_sock->msglen = strlen(UA_sock->msg);
193 } else if (stat == 0) { /* timeout */
194 if (strcmp(prompt, "*") == 0) {
195 bnet_fsend(UA_sock, ".messages");
201 /* @ => internal command for us */
202 if (UA_sock->msg[0] == '@') {
203 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
204 if (!do_a_command(input, UA_sock)) {
209 if (!bnet_send(UA_sock)) { /* send command */
213 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
216 while ((stat = bnet_recv(UA_sock)) >= 0) {
224 sendit("%s", UA_sock->msg);
230 if (is_bnet_stop(UA_sock)) {
231 break; /* error or term */
232 } else if (stat == BNET_SIGNAL) {
233 if (UA_sock->msglen == BNET_PROMPT) {
236 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
242 /*********************************************************************
244 * Main Bacula Console -- User Interface Program
247 int main(int argc, char *argv[])
249 int ch, i, ndir, item;
250 int no_signals = FALSE;
251 int test_config = FALSE;
255 my_name_is(argc, argv, "bconsole");
256 textdomain("bacula-console");
257 init_msg(NULL, NULL);
258 working_directory = "/tmp";
259 args = get_pool_memory(PM_FNAME);
261 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
263 case 'c': /* configuration file */
264 if (configfile != NULL) {
267 configfile = bstrdup(optarg);
271 debug_level = atoi(optarg);
272 if (debug_level <= 0) {
277 case 's': /* turn off signals */
295 init_signals(terminate_console);
297 signal(SIGCHLD, SIG_IGN);
298 signal(SIGTSTP, got_stop);
299 signal(SIGCONT, got_continue);
300 signal(SIGTTIN, got_tin);
301 signal(SIGTTOU, got_tout);
307 if (configfile == NULL) {
308 configfile = bstrdup(CONFIG_FILE);
311 parse_config(configfile);
315 for (dir=NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
320 Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
321 Without that I don't how to speak to the Director :-(\n"), configfile);
325 terminate_console(0);
329 memset(&jcr, 0, sizeof(jcr));
332 UA_sock = init_bsock(NULL, 0, "", "", 0);
334 sendit(_("Available Directors:\n"));
337 for (dir = NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
338 fprintf(output, _("%d %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
342 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
345 item = atoi(UA_sock->msg);
346 if (item < 0 || item > ndir) {
347 sendit(_("You must enter a number between 1 and %d\n"), ndir);
352 for (i=0; i<item; i++) {
353 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
359 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
364 sendit(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
365 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
366 NULL, dir->DIRport, 0);
367 if (UA_sock == NULL) {
368 terminate_console(0);
371 jcr.dir_bsock = UA_sock;
372 if (!authenticate_director(&jcr, dir)) {
373 fprintf(stderr, "ERR=%s", UA_sock->msg);
374 terminate_console(0);
378 Dmsg0(40, "Opened connection with Director daemon\n");
380 sendit(_("Enter a period to cancel a command.\n"));
382 char *env = getenv("HOME");
385 pm_strcpy(&UA_sock->msg, env);
386 pm_strcat(&UA_sock->msg, "/.bconsolerc");
387 fd = fopen(UA_sock->msg, "r");
389 read_and_process_input(fd, UA_sock);
394 read_and_process_input(stdin, UA_sock);
397 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
401 terminate_console(0);
406 /* Cleanup and then exit */
407 static void terminate_console(int sig)
409 static int already_here = FALSE;
411 if (already_here) { /* avoid recursive temination problems */
415 free_pool_memory(args);
423 #define READLINE_LIBRARY 1
425 #include "readline.h"
430 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
434 rl_catch_signals = 0; /* do it ourselves */
435 line = readline(prompt);
440 strip_trailing_junk(line);
441 sock->msglen = pm_strcpy(&sock->msg, line);
443 add_history(sock->msg);
449 #else /* no readline, do it ourselves */
452 * Returns: 1 if data available
457 wait_for_data(int fd, int sec)
467 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
468 case 0: /* timeout */
471 if (errno == EINTR || errno == EAGAIN) {
474 return -1; /* error return */
482 * Get next input command from terminal.
484 * Returns: 1 if got input
489 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
493 if (output == stdout || tee) {
494 fputs(prompt, stdout);
499 switch (wait_for_data(fileno(input), sec)) {
501 return 0; /* timeout */
503 return -1; /* error */
505 len = sizeof_pool_memory(sock->msg) - 1;
510 if (fgets(sock->msg, len, input) == NULL) {
515 strip_trailing_junk(sock->msg);
516 sock->msglen = strlen(sock->msg);
522 static int versioncmd(FILE *input, BSOCK *UA_sock)
524 sendit("Version: " VERSION " (" BDATE ") %s %s %s\n",
525 HOST_OS, DISTNAME, DISTVER);
529 static int inputcmd(FILE *input, BSOCK *UA_sock)
534 sendit(_("Too many arguments on input command.\n"));
538 sendit(_("First argument to input command must be a filename.\n"));
541 fd = fopen(argk[1], "r");
543 sendit(_("Cannot open file %s for input. ERR=%s\n"),
544 argk[1], strerror(errno));
547 read_and_process_input(fd, UA_sock);
552 static int teecmd(FILE *input, BSOCK *UA_sock)
555 return do_outputcmd(input, UA_sock);
558 static int outputcmd(FILE *input, BSOCK *UA_sock)
561 return do_outputcmd(input, UA_sock);
565 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
571 sendit(_("Too many arguments on output/tee command.\n"));
575 if (output != stdout) {
585 fd = fopen(argk[1], mode);
587 sendit(_("Cannot open file %s for output. ERR=%s\n"),
588 argk[1], strerror(errno));
595 static int quitcmd(FILE *input, BSOCK *UA_sock)
600 static int sleepcmd(FILE *input, BSOCK *UA_sock)
603 sleep(atoi(argk[1]));
609 static int timecmd(FILE *input, BSOCK *UA_sock)
612 time_t ttime = time(NULL);
614 localtime_r(&ttime, &tm);
615 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
621 static void sendit(char *fmt,...)
626 va_start(arg_ptr, fmt);
627 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);