3 * Bacula Console interface to the Director
5 * Kern Sibbald, September MM
11 Copyright (C) 2000-2004 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"
39 #define con_set_zed_keys();
47 #define isatty(fd) (fd==0)
48 DWORD g_platform_id = VER_PLATFORM_WIN32_WINDOWS;
51 /* Exported variables */
56 extern int rl_catch_signals;
59 /* Imported functions */
60 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
64 /* Forward referenced functions */
65 static void terminate_console(int sig);
66 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
67 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
68 void senditf(const char *fmt, ...);
69 void sendit(const char *buf);
71 extern "C" void got_sigstop(int sig);
72 extern "C" void got_sigcontinue(int sig);
73 extern "C" void got_sigtout(int sig);
74 extern "C" void got_sigtin(int sig);
77 /* Static variables */
78 static char *configfile = NULL;
79 static BSOCK *UA_sock = NULL;
81 static FILE *output = stdout;
82 static bool tee = false; /* output to output and stdout */
83 static bool stop = false;
86 static char *argk[MAX_CMD_ARGS];
87 static char *argv[MAX_CMD_ARGS];
90 /* Command prototypes */
91 static int versioncmd(FILE *input, BSOCK *UA_sock);
92 static int inputcmd(FILE *input, BSOCK *UA_sock);
93 static int outputcmd(FILE *input, BSOCK *UA_sock);
94 static int teecmd(FILE *input, BSOCK *UA_sock);
95 static int quitcmd(FILE *input, BSOCK *UA_sock);
96 static int timecmd(FILE *input, BSOCK *UA_sock);
97 static int sleepcmd(FILE *input, BSOCK *UA_sock);
100 #define CONFIG_FILE "./bconsole.conf" /* default configuration file */
105 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
106 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
107 " -c <file> set configuration file to file\n"
108 " -dnn set debug level to nn\n"
110 " -t test - read configuration and exit\n"
111 " -? print this message.\n"
112 "\n"), HOST_OS, DISTNAME, DISTVER);
117 void got_sigstop(int sig)
123 void got_sigcontinue(int sig)
129 void got_sigtout(int sig)
131 // printf("Got tout\n");
135 void got_sigtin(int sig)
137 // printf("Got tin\n");
141 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
148 * These are the @command
150 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
151 static struct cmdstruct commands[] = {
152 { N_("input"), inputcmd, _("input from file")},
153 { N_("output"), outputcmd, _("output to file")},
154 { N_("quit"), quitcmd, _("quit")},
155 { N_("tee"), teecmd, _("output to file and terminal")},
156 { N_("sleep"), sleepcmd, _("sleep specified time")},
157 { N_("time"), timecmd, _("print current time")},
158 { N_("version"), versioncmd, _("print Console's version")},
159 { N_("exit"), quitcmd, _("exit = quit")},
160 { N_("zed_keyst"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
162 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
164 static int do_a_command(FILE *input, BSOCK *UA_sock)
175 Dmsg1(120, "Command: %s\n", UA_sock->msg);
181 if (*cmd == '#') { /* comment */
185 for (i=0; i<comsize; i++) { /* search for command */
186 if (strncasecmp(cmd, _(commands[i].key), len) == 0) {
187 stat = (*commands[i].func)(input, UA_sock); /* go execute command */
193 pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
194 UA_sock->msglen = strlen(UA_sock->msg);
195 sendit(UA_sock->msg);
201 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
203 const char *prompt = "*";
204 bool at_prompt = false;
205 int tty_input = isatty(fileno(input));
209 if (at_prompt) { /* don't prompt multiple times */
216 stat = get_cmd(input, prompt, UA_sock, 30);
224 /* Reading input from a file */
225 int len = sizeof_pool_memory(UA_sock->msg) - 1;
229 if (fgets(UA_sock->msg, len, input) == NULL) {
232 sendit(UA_sock->msg); /* echo to terminal */
233 strip_trailing_junk(UA_sock->msg);
234 UA_sock->msglen = strlen(UA_sock->msg);
239 break; /* error or interrupt */
240 } else if (stat == 0) { /* timeout */
241 if (strcmp(prompt, "*") == 0) {
242 bnet_fsend(UA_sock, ".messages");
248 /* @ => internal command for us */
249 if (UA_sock->msg[0] == '@') {
250 parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
251 if (!do_a_command(input, UA_sock)) {
256 if (!bnet_send(UA_sock)) { /* send command */
260 if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
263 while ((stat = bnet_recv(UA_sock)) >= 0) {
270 /* Suppress output if running in background or user hit ctl-c */
271 if (!stop && !usrbrk()) {
272 sendit(UA_sock->msg);
283 if (is_bnet_stop(UA_sock)) {
284 break; /* error or term */
285 } else if (stat == BNET_SIGNAL) {
286 if (UA_sock->msglen == BNET_PROMPT) {
289 Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
295 /*********************************************************************
297 * Main Bacula Console -- User Interface Program
300 int main(int argc, char *argv[])
302 int ch, i, ndir, item;
303 bool no_signals = false;
304 bool test_config = false;
308 my_name_is(argc, argv, "bconsole");
309 textdomain("bacula");
310 init_msg(NULL, NULL);
311 working_directory = "/tmp";
312 args = get_pool_memory(PM_FNAME);
315 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
317 case 'c': /* configuration file */
318 if (configfile != NULL) {
321 configfile = bstrdup(optarg);
325 debug_level = atoi(optarg);
326 if (debug_level <= 0) {
331 case 's': /* turn off signals */
350 init_signals(terminate_console);
353 #if !defined(HAVE_WIN32)
354 /* Override Bacula default signals */
355 signal(SIGCHLD, SIG_IGN);
356 signal(SIGQUIT, SIG_IGN);
357 signal(SIGTSTP, got_sigstop);
358 signal(SIGCONT, got_sigcontinue);
359 signal(SIGTTIN, got_sigtin);
360 signal(SIGTTOU, got_sigtout);
370 if (configfile == NULL) {
371 configfile = bstrdup(CONFIG_FILE);
374 parse_config(configfile);
378 foreach_res(dir, R_DIRECTOR) {
384 Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
385 Without that I don't how to speak to the Director :-(\n"), configfile);
389 terminate_console(0);
393 memset(&jcr, 0, sizeof(jcr));
395 (void)WSA_Init(); /* Initialize Windows sockets */
398 struct sockaddr_in client_addr;
399 memset(&client_addr, 0, sizeof(client_addr));
400 UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
402 sendit(_("Available Directors:\n"));
405 foreach_res(dir, R_DIRECTOR) {
406 senditf( _("%d %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
410 if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
411 (void)WSACleanup(); /* Cleanup Windows sockets */
414 item = atoi(UA_sock->msg);
415 if (item < 0 || item > ndir) {
416 senditf(_("You must enter a number between 1 and %d\n"), ndir);
421 for (i=0; i<item; i++) {
422 dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
428 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
432 senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
433 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
434 NULL, dir->DIRport, 0);
435 if (UA_sock == NULL) {
436 terminate_console(0);
439 jcr.dir_bsock = UA_sock;
442 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
444 /* If cons==NULL, default console will be used */
445 if (!authenticate_director(&jcr, dir, cons)) {
446 fprintf(stderr, "ERR=%s", UA_sock->msg);
447 terminate_console(0);
451 Dmsg0(40, "Opened connection with Director daemon\n");
453 sendit(_("Enter a period to cancel a command.\n"));
455 /* Run commands in ~/.bconsolerc if any */
456 char *env = getenv("HOME");
459 pm_strcpy(&UA_sock->msg, env);
460 pm_strcat(&UA_sock->msg, "/.bconsolerc");
461 fd = fopen(UA_sock->msg, "r");
463 read_and_process_input(fd, UA_sock);
468 read_and_process_input(stdin, UA_sock);
471 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
475 terminate_console(0);
480 /* Cleanup and then exit */
481 static void terminate_console(int sig)
484 static bool already_here = false;
486 if (already_here) { /* avoid recursive temination problems */
490 free_pool_memory(args);
492 (void)WSACleanup(); /* Cleanup Windows sockets */
500 #define READLINE_LIBRARY 1
502 #include "readline.h"
507 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
511 rl_catch_signals = 0; /* do it ourselves */
512 line = readline((char *)prompt); /* cast needed for old readlines */
517 strip_trailing_junk(line);
518 sock->msglen = pm_strcpy(&sock->msg, line);
520 add_history(sock->msg);
526 #else /* no readline, do it ourselves */
529 * Returns: 1 if data available
534 wait_for_data(int fd, int sec)
539 return 1; /* select doesn't seem to work on Win32 */
546 FD_SET((unsigned)fd, &fdset);
547 switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
548 case 0: /* timeout */
551 if (errno == EINTR || errno == EAGAIN) {
554 return -1; /* error return */
562 * Get next input command from terminal.
564 * Returns: 1 if got input
569 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
573 if (output == stdout || tee) {
578 switch (wait_for_data(fileno(input), sec)) {
580 return 0; /* timeout */
582 return -1; /* error */
584 len = sizeof_pool_memory(sock->msg) - 1;
590 if (isatty(fileno(input))) {
591 input_line(sock->msg, len);
595 if (fgets(sock->msg, len, input) == NULL) {
603 strip_trailing_junk(sock->msg);
604 sock->msglen = strlen(sock->msg);
610 static int versioncmd(FILE *input, BSOCK *UA_sock)
612 senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
613 HOST_OS, DISTNAME, DISTVER);
617 static int inputcmd(FILE *input, BSOCK *UA_sock)
622 sendit(_("Too many arguments on input command.\n"));
626 sendit(_("First argument to input command must be a filename.\n"));
629 fd = fopen(argk[1], "r");
631 senditf(_("Cannot open file %s for input. ERR=%s\n"),
632 argk[1], strerror(errno));
635 read_and_process_input(fd, UA_sock);
640 /* Send output to both termina and specified file */
641 static int teecmd(FILE *input, BSOCK *UA_sock)
644 return do_outputcmd(input, UA_sock);
647 /* Send output to specified "file" */
648 static int outputcmd(FILE *input, BSOCK *UA_sock)
651 return do_outputcmd(input, UA_sock);
655 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
658 const char *mode = "a+";
661 sendit(_("Too many arguments on output/tee command.\n"));
665 if (output != stdout) {
675 fd = fopen(argk[1], mode);
677 senditf(_("Cannot open file %s for output. ERR=%s\n"),
678 argk[1], strerror(errno));
685 static int quitcmd(FILE *input, BSOCK *UA_sock)
690 static int sleepcmd(FILE *input, BSOCK *UA_sock)
693 sleep(atoi(argk[1]));
699 static int timecmd(FILE *input, BSOCK *UA_sock)
702 time_t ttime = time(NULL);
704 localtime_r(&ttime, &tm);
705 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
711 * Send a line to the output file and or the terminal
713 void senditf(const char *fmt,...)
718 va_start(arg_ptr, fmt);
719 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
724 void sendit(const char *buf)
727 if (output == stdout || tee) {
730 * Here, we convert every \n into \r\n because the
731 * terminal is in raw mode when we are using
734 for (p=q=buf; (p=strchr(q, '\n')); ) {
739 q = ++p; /* point after \n */
745 if (output != stdout) {
753 if (output == stdout || tee) {