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 sendit(_("Enter a period to cancel a command.\n"));
382 read_and_process_input(stdin, UA_sock);
385 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
389 terminate_console(0);
394 /* Cleanup and then exit */
395 static void terminate_console(int sig)
397 static int already_here = FALSE;
399 if (already_here) { /* avoid recursive temination problems */
403 free_pool_memory(args);
411 #define READLINE_LIBRARY 1
413 #include "readline.h"
418 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
422 rl_catch_signals = 0; /* do it ourselves */
423 line = readline(prompt);
428 strip_trailing_junk(line);
429 sock->msglen = pm_strcpy(&sock->msg, line);
431 add_history(sock->msg);
437 #else /* no readline, do it ourselves */
440 * Returns: 1 if data available
445 wait_for_data(int fd, int sec)
455 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
456 case 0: /* timeout */
459 if (errno == EINTR || errno == EAGAIN) {
462 return -1; /* error return */
470 * Get next input command from terminal.
472 * Returns: 1 if got input
477 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
481 if (output == stdout || tee) {
482 fputs(prompt, stdout);
487 switch (wait_for_data(fileno(input), sec)) {
489 return 0; /* timeout */
491 return -1; /* error */
493 len = sizeof_pool_memory(sock->msg) - 1;
498 if (fgets(sock->msg, len, input) == NULL) {
503 strip_trailing_junk(sock->msg);
504 sock->msglen = strlen(sock->msg);
510 static int versioncmd(FILE *input, BSOCK *UA_sock)
512 sendit("Version: " VERSION " (" BDATE ") %s %s %s\n",
513 HOST_OS, DISTNAME, DISTVER);
517 static int inputcmd(FILE *input, BSOCK *UA_sock)
522 sendit(_("Too many arguments on input command.\n"));
526 sendit(_("First argument to input command must be a filename.\n"));
529 fd = fopen(argk[1], "r");
531 sendit(_("Cannot open file %s for input. ERR=%s\n"),
532 argk[1], strerror(errno));
535 read_and_process_input(fd, UA_sock);
540 static int teecmd(FILE *input, BSOCK *UA_sock)
543 return do_outputcmd(input, UA_sock);
546 static int outputcmd(FILE *input, BSOCK *UA_sock)
549 return do_outputcmd(input, UA_sock);
553 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
559 sendit(_("Too many arguments on output/tee command.\n"));
563 if (output != stdout) {
573 fd = fopen(argk[1], mode);
575 sendit(_("Cannot open file %s for output. ERR=%s\n"),
576 argk[1], strerror(errno));
583 static int quitcmd(FILE *input, BSOCK *UA_sock)
588 static int sleepcmd(FILE *input, BSOCK *UA_sock)
591 sleep(atoi(argk[1]));
597 static int timecmd(FILE *input, BSOCK *UA_sock)
600 time_t ttime = time(NULL);
602 localtime_r(&ttime, &tm);
603 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
609 static void sendit(char *fmt,...)
614 va_start(arg_ptr, fmt);
615 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);