3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
11 Copyright (C) 2000, 2001 Kern Sibbald and John Walker
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License
15 as published by the Free Software Foundation; either version 2
16 of the License, or (at your option) any later version.
18 This program 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
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include "console_conf.h"
32 /* Imported functions */
33 int authenticate_director(JCR *jcr, DIRRES *director);
36 /* Exported variables */
42 extern int rl_catch_signals;
45 /* Forward referenced functions */
46 static void terminate_console(int sig);
47 int get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec);
48 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
49 static void sendit(char *fmt, ...);
51 /* Static variables */
52 static char *configfile = NULL;
53 static BSOCK *UA_sock = NULL;
55 static FILE *output = stdout;
56 int tee = 0; /* output to output and stdout */
57 static int stop = FALSE;
60 static char *argk[MAX_CMD_ARGS];
61 static char *argv[MAX_CMD_ARGS];
63 /* Command prototypes */
64 static int versioncmd(FILE *input, BSOCK *UA_sock);
65 static int inputcmd(FILE *input, BSOCK *UA_sock);
66 static int outputcmd(FILE *input, BSOCK *UA_sock);
67 static int teecmd(FILE *input, BSOCK *UA_sock);
68 static int quitcmd(FILE *input, BSOCK *UA_sock);
69 static int timecmd(FILE *input, BSOCK *UA_sock);
70 static int sleepcmd(FILE *input, BSOCK *UA_sock);
73 #define CONFIG_FILE "./console.conf" /* default configuration file */
78 "\nVersion: " VERSION " (" BDATE ")\n\n"
79 "Usage: console [-s] [-c config_file] [-d debug_level] [config_file]\n"
80 " -c <file> set configuration file to file\n"
81 " -dnn set debug level to nn\n"
83 " -t test - read configuration and exit\n"
84 " -? print this message.\n"
91 void got_stop(int sig)
96 void got_continue(int sig)
101 void got_tout(int sig)
103 // printf("Got tout\n");
106 void got_tin(int sig)
108 // printf("Got tin\n");
111 struct cmdstruct { char *key; int (*func)(FILE *input, BSOCK *UA_sock); char *help; };
112 static struct cmdstruct commands[] = {
113 { N_("input"), inputcmd, _("input from file")},
114 { N_("output"), outputcmd, _("output to file")},
115 { N_("quit"), quitcmd, _("quit")},
116 { N_("tee"), teecmd, _("output to file and terminal")},
117 { N_("sleep"), sleepcmd, _("sleep specified time")},
118 { N_("time"), timecmd, _("print current time")},
119 { N_("version"), versioncmd, _("print Console's version")},
120 { N_("exit"), quitcmd, _("exit = quit")},
122 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
124 static int do_a_command(FILE *input, BSOCK *UA_sock)
135 Dmsg1(120, "Command: %s\n", UA_sock->msg);
141 if (*cmd == '#') { /* comment */
145 for (i=0; i<comsize; i++) { /* search for command */
146 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
147 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
153 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
154 UA_sock->msglen = strlen(UA_sock->msg);
155 fputs(UA_sock->msg, output);
162 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
165 int at_prompt = FALSE;
166 int tty_input = isatty(fileno(input));
170 if (at_prompt) { /* don't prompt multiple times */
177 stat = get_cmd(input, prompt, UA_sock, 30);
179 int len = sizeof_pool_memory(UA_sock->msg) - 1;
180 if (fgets(UA_sock->msg, len, input) == NULL) {
183 strip_trailing_junk(UA_sock->msg);
184 UA_sock->msglen = strlen(UA_sock->msg);
190 } else if (stat == 0) { /* timeout */
191 bnet_fsend(UA_sock, ".messages");
194 /* @ => internal command for us */
195 if (UA_sock->msg[0] == '@') {
196 parse_command_args(UA_sock->msg, args, &argc, argk, argv);
197 if (!do_a_command(input, UA_sock)) {
202 if (!bnet_send(UA_sock)) { /* send command */
206 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
209 while ((stat = bnet_recv(UA_sock)) >= 0) {
217 sendit("%s", UA_sock->msg);
223 if (is_bnet_stop(UA_sock)) {
224 break; /* error or term */
225 } else if (stat == BNET_SIGNAL) {
226 if (UA_sock->msglen == BNET_PROMPT) {
229 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
235 /*********************************************************************
237 * Main Bacula Console -- User Interface Program
240 int main(int argc, char *argv[])
242 int ch, i, ndir, item;
243 int no_signals = FALSE;
244 int test_config = FALSE;
248 my_name_is(argc, argv, "console");
249 init_msg(NULL, NULL);
250 working_directory = "/tmp";
251 args = get_pool_memory(PM_FNAME);
253 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
255 case 'c': /* configuration file */
256 if (configfile != NULL) {
259 configfile = bstrdup(optarg);
263 debug_level = atoi(optarg);
264 if (debug_level <= 0) {
269 case 's': /* turn off signals */
287 init_signals(terminate_console);
289 signal(SIGCHLD, SIG_IGN);
290 signal(SIGTSTP, got_stop);
291 signal(SIGCONT, got_continue);
292 signal(SIGTTIN, got_tin);
293 signal(SIGTTOU, got_tout);
299 if (configfile == NULL) {
300 configfile = bstrdup(CONFIG_FILE);
303 parse_config(configfile);
307 for (dir=NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
312 Emsg1(M_ERROR_TERM, 0, "No Director resource defined in %s\n\
313 Without that I don't how to speak to the Director :-(\n", configfile);
317 terminate_console(0);
321 memset(&jcr, 0, sizeof(jcr));
324 UA_sock = init_bsock(NULL, 0, "", "", 0);
326 sendit("Available Directors:\n");
329 for (dir = NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
330 fprintf(output, "%d %s at %s:%d\n", 1+ndir++, dir->hdr.name, dir->address,
334 if (get_cmd(stdin, "Select Director: ", UA_sock, 600) < 0) {
337 item = atoi(UA_sock->msg);
338 if (item < 0 || item > ndir) {
339 sendit("You must enter a number between 1 and %d\n", ndir);
344 for (i=0; i<item; i++) {
345 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
351 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
356 sendit("Connecting to Director %s:%d\n", dir->address,dir->DIRport);
357 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
358 NULL, dir->DIRport, 0);
359 if (UA_sock == NULL) {
360 terminate_console(0);
363 jcr.dir_bsock = UA_sock;
364 if (!authenticate_director(&jcr, dir)) {
365 fprintf(stderr, "ERR=%s", UA_sock->msg);
366 terminate_console(0);
370 Dmsg0(40, "Opened connection with Director daemon\n");
372 read_and_process_input(stdin, UA_sock);
375 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
379 terminate_console(0);
384 /* Cleanup and then exit */
385 static void terminate_console(int sig)
387 static int already_here = FALSE;
389 if (already_here) { /* avoid recursive temination problems */
393 free_pool_memory(args);
399 #include "readline/readline.h"
400 #include "readline/history.h"
404 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
408 rl_catch_signals = 0; /* do it ourselves */
409 line = readline(prompt);
414 strcpy(sock->msg, line);
415 strip_trailing_junk(sock->msg);
416 sock->msglen = strlen(sock->msg);
418 add_history(sock->msg);
424 #else /* no readline, do it ourselves */
427 * Returns: 1 if data available
432 wait_for_data(int fd, int sec)
442 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
443 case 0: /* timeout */
446 if (errno == EINTR || errno == EAGAIN) {
449 return -1; /* error return */
457 * Get next input command from terminal.
459 * Returns: 1 if got input
464 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
468 if (output == stdout || tee) {
469 fputs(prompt, stdout);
474 switch (wait_for_data(fileno(input), sec)) {
476 return 0; /* timeout */
478 return -1; /* error */
480 len = sizeof_pool_memory(sock->msg) - 1;
484 if (fgets(sock->msg, len, input) == NULL) {
489 strip_trailing_junk(sock->msg);
490 sock->msglen = strlen(sock->msg);
496 static int versioncmd(FILE *input, BSOCK *UA_sock)
498 sendit("Version: " VERSION " (" BDATE ")\n");
502 static int inputcmd(FILE *input, BSOCK *UA_sock)
507 sendit("Too many arguments.\n");
511 sendit("First argument must be a filename.\n");
514 fd = fopen(argk[1], "r");
516 sendit("Cannot open file. ERR=%s\n", strerror(errno));
519 read_and_process_input(fd, UA_sock);
524 static int teecmd(FILE *input, BSOCK *UA_sock)
527 return do_outputcmd(input, UA_sock);
530 static int outputcmd(FILE *input, BSOCK *UA_sock)
533 return do_outputcmd(input, UA_sock);
537 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
543 sendit("Too many arguments.\n");
547 if (output != stdout) {
557 fd = fopen(argk[1], mode);
559 sendit("Cannot open file. ERR=%s\n", strerror(errno));
566 static int quitcmd(FILE *input, BSOCK *UA_sock)
571 static int sleepcmd(FILE *input, BSOCK *UA_sock)
574 sleep(atoi(argk[1]));
580 static int timecmd(FILE *input, BSOCK *UA_sock)
583 time_t ttime = time(NULL);
585 localtime_r(&ttime, &tm);
586 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
592 static void sendit(char *fmt,...)
597 va_start(arg_ptr, fmt);
598 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);