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 "./console.conf" /* default configuration file */
80 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
81 "Usage: console [-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, "console");
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 read_and_process_input(stdin, UA_sock);
383 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
387 terminate_console(0);
392 /* Cleanup and then exit */
393 static void terminate_console(int sig)
395 static int already_here = FALSE;
397 if (already_here) { /* avoid recursive temination problems */
401 free_pool_memory(args);
409 #define READLINE_LIBRARY 1
411 #include "readline.h"
416 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
420 rl_catch_signals = 0; /* do it ourselves */
421 line = readline(prompt);
426 strcpy(sock->msg, line);
427 strip_trailing_junk(sock->msg);
428 sock->msglen = strlen(sock->msg);
430 add_history(sock->msg);
436 #else /* no readline, do it ourselves */
439 * Returns: 1 if data available
444 wait_for_data(int fd, int sec)
454 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
455 case 0: /* timeout */
458 if (errno == EINTR || errno == EAGAIN) {
461 return -1; /* error return */
469 * Get next input command from terminal.
471 * Returns: 1 if got input
476 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
480 if (output == stdout || tee) {
481 fputs(prompt, stdout);
486 switch (wait_for_data(fileno(input), sec)) {
488 return 0; /* timeout */
490 return -1; /* error */
492 len = sizeof_pool_memory(sock->msg) - 1;
497 if (fgets(sock->msg, len, input) == NULL) {
502 strip_trailing_junk(sock->msg);
503 sock->msglen = strlen(sock->msg);
509 static int versioncmd(FILE *input, BSOCK *UA_sock)
511 sendit("Version: " VERSION " (" BDATE ") %s %s %s\n",
512 HOST_OS, DISTNAME, DISTVER);
516 static int inputcmd(FILE *input, BSOCK *UA_sock)
521 sendit(_("Too many arguments on input command.\n"));
525 sendit(_("First argument to input command must be a filename.\n"));
528 fd = fopen(argk[1], "r");
530 sendit(_("Cannot open file %s for input. ERR=%s\n"),
531 argk[1], strerror(errno));
534 read_and_process_input(fd, UA_sock);
539 static int teecmd(FILE *input, BSOCK *UA_sock)
542 return do_outputcmd(input, UA_sock);
545 static int outputcmd(FILE *input, BSOCK *UA_sock)
548 return do_outputcmd(input, UA_sock);
552 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
558 sendit(_("Too many arguments on output/tee command.\n"));
562 if (output != stdout) {
572 fd = fopen(argk[1], mode);
574 sendit(_("Cannot open file %s for output. ERR=%s\n"),
575 argk[1], strerror(errno));
582 static int quitcmd(FILE *input, BSOCK *UA_sock)
587 static int sleepcmd(FILE *input, BSOCK *UA_sock)
590 sleep(atoi(argk[1]));
596 static int timecmd(FILE *input, BSOCK *UA_sock)
599 time_t ttime = time(NULL);
601 localtime_r(&ttime, &tm);
602 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
608 static void sendit(char *fmt,...)
613 va_start(arg_ptr, fmt);
614 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);