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 */
195 bnet_fsend(UA_sock, ".messages");
199 /* @ => internal command for us */
200 if (UA_sock->msg[0] == '@') {
201 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
202 if (!do_a_command(input, UA_sock)) {
207 if (!bnet_send(UA_sock)) { /* send command */
211 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
214 while ((stat = bnet_recv(UA_sock)) >= 0) {
222 sendit("%s", UA_sock->msg);
228 if (is_bnet_stop(UA_sock)) {
229 break; /* error or term */
230 } else if (stat == BNET_SIGNAL) {
231 if (UA_sock->msglen == BNET_PROMPT) {
234 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
240 /*********************************************************************
242 * Main Bacula Console -- User Interface Program
245 int main(int argc, char *argv[])
247 int ch, i, ndir, item;
248 int no_signals = FALSE;
249 int test_config = FALSE;
253 my_name_is(argc, argv, "console");
254 textdomain("bacula-console");
255 init_msg(NULL, NULL);
256 working_directory = "/tmp";
257 args = get_pool_memory(PM_FNAME);
259 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
261 case 'c': /* configuration file */
262 if (configfile != NULL) {
265 configfile = bstrdup(optarg);
269 debug_level = atoi(optarg);
270 if (debug_level <= 0) {
275 case 's': /* turn off signals */
293 init_signals(terminate_console);
295 signal(SIGCHLD, SIG_IGN);
296 signal(SIGTSTP, got_stop);
297 signal(SIGCONT, got_continue);
298 signal(SIGTTIN, got_tin);
299 signal(SIGTTOU, got_tout);
305 if (configfile == NULL) {
306 configfile = bstrdup(CONFIG_FILE);
309 parse_config(configfile);
313 for (dir=NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
318 Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
319 Without that I don't how to speak to the Director :-(\n"), configfile);
323 terminate_console(0);
327 memset(&jcr, 0, sizeof(jcr));
330 UA_sock = init_bsock(NULL, 0, "", "", 0);
332 sendit(_("Available Directors:\n"));
335 for (dir = NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
336 fprintf(output, _("%d %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
340 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
343 item = atoi(UA_sock->msg);
344 if (item < 0 || item > ndir) {
345 sendit(_("You must enter a number between 1 and %d\n"), ndir);
350 for (i=0; i<item; i++) {
351 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
357 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
362 sendit(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
363 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
364 NULL, dir->DIRport, 0);
365 if (UA_sock == NULL) {
366 terminate_console(0);
369 jcr.dir_bsock = UA_sock;
370 if (!authenticate_director(&jcr, dir)) {
371 fprintf(stderr, "ERR=%s", UA_sock->msg);
372 terminate_console(0);
376 Dmsg0(40, "Opened connection with Director daemon\n");
378 read_and_process_input(stdin, UA_sock);
381 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
385 terminate_console(0);
390 /* Cleanup and then exit */
391 static void terminate_console(int sig)
393 static int already_here = FALSE;
395 if (already_here) { /* avoid recursive temination problems */
399 free_pool_memory(args);
407 #define READLINE_LIBRARY 1
409 #include "readline.h"
414 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
418 rl_catch_signals = 0; /* do it ourselves */
419 line = readline(prompt);
424 strcpy(sock->msg, line);
425 strip_trailing_junk(sock->msg);
426 sock->msglen = strlen(sock->msg);
428 add_history(sock->msg);
434 #else /* no readline, do it ourselves */
437 * Returns: 1 if data available
442 wait_for_data(int fd, int sec)
452 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
453 case 0: /* timeout */
456 if (errno == EINTR || errno == EAGAIN) {
459 return -1; /* error return */
467 * Get next input command from terminal.
469 * Returns: 1 if got input
474 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
478 if (output == stdout || tee) {
479 fputs(prompt, stdout);
484 switch (wait_for_data(fileno(input), sec)) {
486 return 0; /* timeout */
488 return -1; /* error */
490 len = sizeof_pool_memory(sock->msg) - 1;
495 if (fgets(sock->msg, len, input) == NULL) {
500 strip_trailing_junk(sock->msg);
501 sock->msglen = strlen(sock->msg);
507 static int versioncmd(FILE *input, BSOCK *UA_sock)
509 sendit("Version: " VERSION " (" BDATE ") %s %s %s\n",
510 HOST_OS, DISTNAME, DISTVER);
514 static int inputcmd(FILE *input, BSOCK *UA_sock)
519 sendit(_("Too many arguments.\n"));
523 sendit(_("First argument must be a filename.\n"));
526 fd = fopen(argk[1], "r");
528 sendit(_("Cannot open file. ERR=%s\n"), strerror(errno));
531 read_and_process_input(fd, UA_sock);
536 static int teecmd(FILE *input, BSOCK *UA_sock)
539 return do_outputcmd(input, UA_sock);
542 static int outputcmd(FILE *input, BSOCK *UA_sock)
545 return do_outputcmd(input, UA_sock);
549 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
555 sendit(_("Too many arguments.\n"));
559 if (output != stdout) {
569 fd = fopen(argk[1], mode);
571 sendit(_("Cannot open file. ERR=%s\n"), strerror(errno));
578 static int quitcmd(FILE *input, BSOCK *UA_sock)
583 static int sleepcmd(FILE *input, BSOCK *UA_sock)
586 sleep(atoi(argk[1]));
592 static int timecmd(FILE *input, BSOCK *UA_sock)
595 time_t ttime = time(NULL);
597 localtime_r(&ttime, &tm);
598 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
604 static void sendit(char *fmt,...)
609 va_start(arg_ptr, fmt);
610 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);