]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
8ea78bbbad7833e7bf3e61b3fb78cfc998395c07
[bacula/bacula] / bacula / src / console / console.c
1 /*
2  *
3  *   Bacula Console interface to the Director
4  *
5  *     Kern Sibbald, September MM
6  *
7  *     Version $Id$
8  */
9
10 /*
11    Copyright (C) 2000-2003 Kern Sibbald and John Walker
12
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.
17
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.
22
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,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "console_conf.h"
32 #include "jcr.h"
33
34 #ifdef HAVE_CONIO
35 #include "conio.h"
36 #else
37 #define con_init(x) 
38 #define con_term()
39 #define con_set_zed_keys();
40 #define trapctlc()
41 #define clrbrk()
42 #define usrbrk() 0  
43 #endif
44  
45 /* Exported variables */
46
47 #ifdef HAVE_CYGWIN
48 int rl_catch_signals;
49 #else
50 extern int rl_catch_signals;
51 #endif
52
53 /* Imported functions */
54 int authenticate_director(JCR *jcr, DIRRES *director, char *name);
55
56
57
58 /* Forward referenced functions */
59 static void terminate_console(int sig);
60 int get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec);
61 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
62 void senditf(char *fmt, ...);
63 void sendit(char *buf);
64
65 /* Static variables */
66 static char *configfile = NULL;
67 static BSOCK *UA_sock = NULL;
68 static DIRRES *dir; 
69 static FILE *output = stdout;
70 static bool tee = false;                  /* output to output and stdout */
71 static bool stop = false;
72 static int argc;
73 static POOLMEM *args;
74 static char *argk[MAX_CMD_ARGS];
75 static char *argv[MAX_CMD_ARGS];
76
77
78 /* Command prototypes */
79 static int versioncmd(FILE *input, BSOCK *UA_sock);
80 static int inputcmd(FILE *input, BSOCK *UA_sock);
81 static int outputcmd(FILE *input, BSOCK *UA_sock);
82 static int teecmd(FILE *input, BSOCK *UA_sock);
83 static int quitcmd(FILE *input, BSOCK *UA_sock);
84 static int timecmd(FILE *input, BSOCK *UA_sock);
85 static int sleepcmd(FILE *input, BSOCK *UA_sock);
86
87
88 #define CONFIG_FILE "./bconsole.conf"   /* default configuration file */
89
90 static void usage()
91 {
92    fprintf(stderr, _(
93 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
94 "Usage: bconsole [-s] [-c config_file] [-d debug_level] [config_file]\n"
95 "       -c <file>   set configuration file to file\n"
96 "       -dnn        set debug level to nn\n"
97 "       -s          no signals\n"
98 "       -t          test - read configuration and exit\n"
99 "       -?          print this message.\n"  
100 "\n"), HOST_OS, DISTNAME, DISTVER);
101 }
102
103 void got_sigstop(int sig)
104 {
105    stop = true;
106 }
107
108 void got_sigcontinue(int sig)
109 {
110    stop = false;
111 }
112
113 void got_sigtout(int sig) 
114 {
115 // printf("Got tout\n");
116 }
117
118 void got_sigtin(int sig)
119 {   
120 // printf("Got tin\n");
121 }
122
123 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
124 {
125    con_set_zed_keys();
126    return 1;
127 }
128
129 /*
130  * These are the @command
131  */
132 struct cmdstruct { char *key; int (*func)(FILE *input, BSOCK *UA_sock); char *help; }; 
133 static struct cmdstruct commands[] = {
134  { N_("input"),      inputcmd,     _("input from file")},
135  { N_("output"),     outputcmd,    _("output to file")},
136  { N_("quit"),       quitcmd,      _("quit")},
137  { N_("tee"),        teecmd,       _("output to file and terminal")},
138  { N_("sleep"),      sleepcmd,     _("sleep specified time")},
139  { N_("time"),       timecmd,      _("print current time")},
140  { N_("version"),    versioncmd,   _("print Console's version")},
141  { N_("exit"),       quitcmd,      _("exit = quit")},
142  { N_("zed_keyst"),  zed_keyscmd,  _("zed_keys = use zed keys instead of bash keys")},
143              };
144 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
145
146 static int do_a_command(FILE *input, BSOCK *UA_sock)
147 {
148    unsigned int i;
149    int stat;
150    int found;
151    int len;
152    char *cmd;
153
154    found = 0;
155    stat = 1;
156
157    Dmsg1(120, "Command: %s\n", UA_sock->msg);
158    if (argc == 0) {
159       return 1;
160    }
161
162    cmd = argk[0]+1;
163    if (*cmd == '#') {                 /* comment */
164       return 1;
165    }
166    len = strlen(cmd);
167    for (i=0; i<comsize; i++) {     /* search for command */
168       if (strncasecmp(cmd,  _(commands[i].key), len) == 0) {
169          stat = (*commands[i].func)(input, UA_sock);   /* go execute command */
170          found = 1;
171          break;
172       }
173    }
174    if (!found) {
175       pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
176       UA_sock->msglen = strlen(UA_sock->msg);
177       sendit(UA_sock->msg);
178    }
179    return stat;
180 }
181
182
183 static void read_and_process_input(FILE *input, BSOCK *UA_sock) 
184 {
185    char *prompt = "*";
186    bool at_prompt = false;
187    int tty_input = isatty(fileno(input));
188    int stat;
189
190    for ( ;; ) { 
191       if (at_prompt) {                /* don't prompt multiple times */
192          prompt = "";
193       } else {
194          prompt = "*";
195          at_prompt = true;
196       }
197       if (tty_input) {
198          stat = get_cmd(input, prompt, UA_sock, 30);
199          clrbrk();
200       } else {
201          int len = sizeof_pool_memory(UA_sock->msg) - 1;
202          if (fgets(UA_sock->msg, len, input) == NULL || usrbrk()) {
203             stat = -1;
204          } else {
205             sendit(UA_sock->msg);  /* echo to terminal */
206             strip_trailing_junk(UA_sock->msg);
207             UA_sock->msglen = strlen(UA_sock->msg);
208             stat = 1;
209          }
210       }
211       if (stat < 0) {
212          break;                       /* error or interrupt */
213       } else if (stat == 0) {         /* timeout */
214          if (strcmp(prompt, "*") == 0) {
215             bnet_fsend(UA_sock, ".messages");
216          } else {
217             continue;
218          }
219       } else {
220          at_prompt = FALSE;
221          /* @ => internal command for us */
222          if (UA_sock->msg[0] == '@') {
223             parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
224             if (!do_a_command(input, UA_sock)) {
225                break;
226             }
227             continue;
228          }
229          if (!bnet_send(UA_sock)) {   /* send command */
230             break;                    /* error */
231          }
232       }
233       if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
234          break;
235       }
236       while ((stat = bnet_recv(UA_sock)) >= 0) {
237          if (at_prompt) {
238             if (!stop) {
239                sendit("\n");
240             }
241             at_prompt = false;
242          }
243          /* Suppress output if running in background or user hit ctl-c */
244          if (!stop && !usrbrk()) {
245             sendit(UA_sock->msg);
246          }
247       }
248       if (!stop) {
249          fflush(stdout);
250       }
251       if (is_bnet_stop(UA_sock)) {
252          break;                       /* error or term */
253       } else if (stat == BNET_SIGNAL) {
254          if (UA_sock->msglen == BNET_PROMPT) {
255             at_prompt = true;
256          }
257          Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
258       }
259    }
260 }
261
262
263 /*********************************************************************
264  *
265  *         Main Bacula Console -- User Interface Program
266  *
267  */
268 int main(int argc, char *argv[])
269 {
270    int ch, i, ndir, item;
271    bool no_signals = false;
272    bool test_config = false;
273    JCR jcr;
274
275    init_stack_dump();
276    my_name_is(argc, argv, "bconsole");
277    textdomain("bacula-console");
278    init_msg(NULL, NULL);
279    working_directory = "/tmp";
280    args = get_pool_memory(PM_FNAME);
281    con_init(stdin);
282
283    while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
284       switch (ch) {
285       case 'c':                    /* configuration file */
286          if (configfile != NULL) {
287             free(configfile);
288          }
289          configfile = bstrdup(optarg);
290          break;
291
292       case 'd':
293          debug_level = atoi(optarg);
294          if (debug_level <= 0) {
295             debug_level = 1;
296          }
297          break;
298
299       case 's':                    /* turn off signals */
300          no_signals = true;
301          break;
302
303       case 't':
304          test_config = true;
305          break;
306
307       case '?':
308       default:
309          usage();
310          con_term();
311          exit(1);
312       }  
313    }
314    argc -= optind;
315    argv += optind;
316
317    if (!no_signals) {
318       init_signals(terminate_console);
319    }
320
321    /* Override Bacula default signals */
322    signal(SIGCHLD, SIG_IGN);
323    signal(SIGTSTP, got_sigstop);
324    signal(SIGCONT, got_sigcontinue);
325    signal(SIGTTIN, got_sigtin);
326    signal(SIGTTOU, got_sigtout);
327    trapctlc();
328
329    if (argc) {
330       usage();
331       con_term();
332       exit(1);
333    }
334
335    if (configfile == NULL) {
336       configfile = bstrdup(CONFIG_FILE);
337    }
338
339    parse_config(configfile);
340
341    LockRes();
342    ndir = 0;
343    foreach_res(dir, R_DIRECTOR) {
344       ndir++;
345    }
346    UnlockRes();
347    if (ndir == 0) {
348       con_term();
349       Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
350 Without that I don't how to speak to the Director :-(\n"), configfile);
351    }
352
353    if (test_config) {
354       terminate_console(0);
355       exit(0);
356    }
357
358    memset(&jcr, 0, sizeof(jcr));
359
360
361    if (ndir > 1) {
362       struct sockaddr_in client_addr;
363       memset(&client_addr, 0, sizeof(client_addr));
364       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
365 try_again:
366       sendit(_("Available Directors:\n"));
367       LockRes();
368       ndir = 0;
369       foreach_res(dir, R_DIRECTOR) {
370          senditf( _("%d  %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
371             dir->DIRport);
372       }
373       UnlockRes();
374       if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
375          return 1;
376       }
377       item = atoi(UA_sock->msg);
378       if (item < 0 || item > ndir) {
379          senditf(_("You must enter a number between 1 and %d\n"), ndir);
380          goto try_again;
381       }
382       LockRes();
383       dir = NULL;
384       for (i=0; i<item; i++) {
385          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
386       }
387       UnlockRes();
388       term_bsock(UA_sock);
389    } else {
390       LockRes();
391       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
392       UnlockRes();
393    }
394       
395    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
396    UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address, 
397                           NULL, dir->DIRport, 0);
398    if (UA_sock == NULL) {
399       terminate_console(0);
400       return 1;
401    }
402    jcr.dir_bsock = UA_sock;
403
404    LockRes();
405    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
406    UnlockRes();
407    char *con_name;
408    if (cons) {
409       con_name = cons->hdr.name;
410    } else {
411       con_name = "*UserAgent*";
412    }
413    if (!authenticate_director(&jcr, dir, con_name)) {
414       fprintf(stderr, "ERR=%s", UA_sock->msg);
415       terminate_console(0);
416       return 1;
417    }
418
419    Dmsg0(40, "Opened connection with Director daemon\n");
420
421    sendit(_("Enter a period to cancel a command.\n"));
422
423    /* Run commands in ~/.bconsolerc if any */
424    char *env = getenv("HOME");
425    if (env) {
426       FILE *fd;
427       pm_strcpy(&UA_sock->msg, env);
428       pm_strcat(&UA_sock->msg, "/.bconsolerc");
429       fd = fopen(UA_sock->msg, "r");
430       if (fd) {
431          read_and_process_input(fd, UA_sock);
432          fclose(fd);
433       }
434    }
435
436    read_and_process_input(stdin, UA_sock);
437
438    if (UA_sock) {
439       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
440       bnet_close(UA_sock);
441    }
442
443    terminate_console(0);
444    return 0;
445 }
446
447
448 /* Cleanup and then exit */
449 static void terminate_console(int sig)
450 {
451    static bool already_here = false;
452
453    if (already_here) {                /* avoid recursive temination problems */
454       exit(1);
455    }
456    already_here = true;
457    free_pool_memory(args);
458    con_term();
459    if (sig != 0) {
460       exit(1);
461    }
462    return;
463 }
464
465 #ifdef HAVE_READLINE
466 #define READLINE_LIBRARY 1
467 #undef free
468 #include "readline.h"
469 #include "history.h"
470
471
472 int 
473 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
474 {
475    char *line;
476
477    rl_catch_signals = 0;              /* do it ourselves */
478    line = readline(prompt);
479
480    if (!line) {
481       exit(1);
482    }
483    strip_trailing_junk(line);
484    sock->msglen = pm_strcpy(&sock->msg, line);
485    if (sock->msglen) {
486       add_history(sock->msg);
487    }
488    free(line);
489    return 1;
490 }
491
492 #else /* no readline, do it ourselves */
493
494 /*
495  *   Returns: 1 if data available
496  *            0 if timeout
497  *           -1 if error
498  */
499 static int
500 wait_for_data(int fd, int sec)
501 {
502    fd_set fdset;
503    struct timeval tv;
504
505    tv.tv_sec = sec;
506    tv.tv_usec = 0;
507    for ( ;; ) {
508       FD_ZERO(&fdset);
509       FD_SET(fd, &fdset);
510       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
511       case 0:                         /* timeout */
512          return 0;
513       case -1:
514          if (errno == EINTR || errno == EAGAIN) {
515             continue;
516          }
517          return -1;                  /* error return */
518       default:
519          return 1;
520       }
521    }
522 }
523
524 /*      
525  * Get next input command from terminal. 
526  *
527  *   Returns: 1 if got input
528  *            0 if timeout
529  *           -1 if EOF or error
530  */
531 int 
532 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
533 {
534    int len;  
535    if (!stop) {
536       if (output == stdout || tee) {
537          sendit(prompt);
538       }
539    }
540 again:
541    switch (wait_for_data(fileno(input), sec)) {
542    case 0:
543       return 0;                    /* timeout */
544    case -1: 
545       return -1;                   /* error */
546    default:
547       len = sizeof_pool_memory(sock->msg) - 1;
548       if (stop) {
549          sleep(1);
550          goto again;
551       }
552 #ifdef HAVE_CONIO
553       if (isatty(fileno(input))) {
554          input_line(sock->msg, len);
555          break;
556       }
557 #endif
558       if (fgets(sock->msg, len, input) == NULL) {
559          return -1;
560       }
561       break;
562    }
563    strip_trailing_junk(sock->msg);
564    sock->msglen = strlen(sock->msg);
565    return 1;
566 }
567
568 #endif
569
570 static int versioncmd(FILE *input, BSOCK *UA_sock)
571 {
572    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
573       HOST_OS, DISTNAME, DISTVER);
574    return 1;
575 }
576
577 static int inputcmd(FILE *input, BSOCK *UA_sock)
578 {
579    FILE *fd;
580
581    if (argc > 2) {
582       sendit(_("Too many arguments on input command.\n"));
583       return 1;
584    }
585    if (argc == 1) {
586       sendit(_("First argument to input command must be a filename.\n"));
587       return 1;
588    }
589    fd = fopen(argk[1], "r");
590    if (!fd) {
591       senditf(_("Cannot open file %s for input. ERR=%s\n"), 
592          argk[1], strerror(errno));
593       return 1; 
594    }
595    read_and_process_input(fd, UA_sock);
596    fclose(fd);
597    return 1;
598 }
599
600 /* Send output to both termina and specified file */
601 static int teecmd(FILE *input, BSOCK *UA_sock)
602 {
603    tee = true;
604    return do_outputcmd(input, UA_sock);
605 }
606
607 /* Send output to specified "file" */
608 static int outputcmd(FILE *input, BSOCK *UA_sock)
609 {
610    tee = false;
611    return do_outputcmd(input, UA_sock);
612 }
613
614
615 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
616 {
617    FILE *fd;
618    char *mode = "a+";
619
620    if (argc > 3) {
621       sendit(_("Too many arguments on output/tee command.\n"));
622       return 1;
623    }
624    if (argc == 1) {
625       if (output != stdout) {
626          fclose(output);
627          output = stdout;
628          tee = false;
629       }
630       return 1;
631    }
632    if (argc == 3) {
633       mode = argk[2];
634    }
635    fd = fopen(argk[1], mode);
636    if (!fd) {
637       senditf(_("Cannot open file %s for output. ERR=%s\n"), 
638          argk[1], strerror(errno));
639       return 1; 
640    }
641    output = fd;
642    return 1;
643 }
644
645 static int quitcmd(FILE *input, BSOCK *UA_sock)
646 {
647    return 0;
648 }
649
650 static int sleepcmd(FILE *input, BSOCK *UA_sock)
651 {
652    if (argc > 1) {
653       sleep(atoi(argk[1]));
654    }
655    return 1;
656 }
657
658
659 static int timecmd(FILE *input, BSOCK *UA_sock)
660 {
661    char sdt[50];
662    time_t ttime = time(NULL);
663    struct tm tm;
664    localtime_r(&ttime, &tm);
665    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
666    sendit("\n");
667    return 1;
668 }
669
670 /*
671  * Send a line to the output file and or the terminal
672  */
673 void senditf(char *fmt,...)
674 {
675     char buf[3000];
676     va_list arg_ptr;
677
678     va_start(arg_ptr, fmt);
679     bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
680     va_end(arg_ptr);
681     sendit(buf);
682 }
683
684 void sendit(char *buf)
685 {
686 #ifdef xHAVE_CONIO
687     if (output == stdout || tee) {
688        char *p, *q;     
689        /*
690         * Here, we convert every \n into \r\n because the
691         *  terminal is in raw mode when we are using 
692         *  conio.
693         */
694        for (p=q=buf; (p=strchr(q, '\n')); ) {
695           if (p-q > 0) {
696              t_sendl(q, p-q);
697           }
698           t_sendl("\r\n", 2);
699           q = ++p;                    /* point after \n */
700        }
701        if (*q) {
702           t_send(q);
703        }
704     }
705     if (output != stdout) {
706        fputs(buf, output);
707     }
708 #else
709     fputs(buf, output);
710     if (tee) {
711        fputs(buf, stdout);
712     }
713     fflush(stdout);
714 #endif
715 }