]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
b59e601eba6ffe61a1bbb4f653e8b0a8d9852678
[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       UA_sock = init_bsock(NULL, 0, "", "", 0);
363 try_again:
364       sendit(_("Available Directors:\n"));
365       LockRes();
366       ndir = 0;
367       foreach_res(dir, R_DIRECTOR) {
368          senditf( _("%d  %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
369             dir->DIRport);
370       }
371       UnlockRes();
372       if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
373          return 1;
374       }
375       item = atoi(UA_sock->msg);
376       if (item < 0 || item > ndir) {
377          senditf(_("You must enter a number between 1 and %d\n"), ndir);
378          goto try_again;
379       }
380       LockRes();
381       dir = NULL;
382       for (i=0; i<item; i++) {
383          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
384       }
385       UnlockRes();
386       term_bsock(UA_sock);
387    } else {
388       LockRes();
389       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
390       UnlockRes();
391    }
392       
393    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
394    UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address, 
395                           NULL, dir->DIRport, 0);
396    if (UA_sock == NULL) {
397       terminate_console(0);
398       return 1;
399    }
400    jcr.dir_bsock = UA_sock;
401
402    LockRes();
403    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
404    UnlockRes();
405    char *con_name;
406    if (cons) {
407       con_name = cons->hdr.name;
408    } else {
409       con_name = "*UserAgent*";
410    }
411    if (!authenticate_director(&jcr, dir, con_name)) {
412       fprintf(stderr, "ERR=%s", UA_sock->msg);
413       terminate_console(0);
414       return 1;
415    }
416
417    Dmsg0(40, "Opened connection with Director daemon\n");
418
419    sendit(_("Enter a period to cancel a command.\n"));
420
421    /* Run commands in ~/.bconsolerc if any */
422    char *env = getenv("HOME");
423    if (env) {
424       FILE *fd;
425       pm_strcpy(&UA_sock->msg, env);
426       pm_strcat(&UA_sock->msg, "/.bconsolerc");
427       fd = fopen(UA_sock->msg, "r");
428       if (fd) {
429          read_and_process_input(fd, UA_sock);
430          fclose(fd);
431       }
432    }
433
434    read_and_process_input(stdin, UA_sock);
435
436    if (UA_sock) {
437       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
438       bnet_close(UA_sock);
439    }
440
441    terminate_console(0);
442    return 0;
443 }
444
445
446 /* Cleanup and then exit */
447 static void terminate_console(int sig)
448 {
449    static bool already_here = false;
450
451    if (already_here) {                /* avoid recursive temination problems */
452       exit(1);
453    }
454    already_here = true;
455    free_pool_memory(args);
456    con_term();
457    if (sig != 0) {
458       exit(1);
459    }
460    return;
461 }
462
463 #ifdef HAVE_READLINE
464 #define READLINE_LIBRARY 1
465 #undef free
466 #include "readline.h"
467 #include "history.h"
468
469
470 int 
471 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
472 {
473    char *line;
474
475    rl_catch_signals = 0;              /* do it ourselves */
476    line = readline(prompt);
477
478    if (!line) {
479       exit(1);
480    }
481    strip_trailing_junk(line);
482    sock->msglen = pm_strcpy(&sock->msg, line);
483    if (sock->msglen) {
484       add_history(sock->msg);
485    }
486    free(line);
487    return 1;
488 }
489
490 #else /* no readline, do it ourselves */
491
492 /*
493  *   Returns: 1 if data available
494  *            0 if timeout
495  *           -1 if error
496  */
497 static int
498 wait_for_data(int fd, int sec)
499 {
500    fd_set fdset;
501    struct timeval tv;
502
503    tv.tv_sec = sec;
504    tv.tv_usec = 0;
505    for ( ;; ) {
506       FD_ZERO(&fdset);
507       FD_SET(fd, &fdset);
508       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
509       case 0:                         /* timeout */
510          return 0;
511       case -1:
512          if (errno == EINTR || errno == EAGAIN) {
513             continue;
514          }
515          return -1;                  /* error return */
516       default:
517          return 1;
518       }
519    }
520 }
521
522 /*      
523  * Get next input command from terminal. 
524  *
525  *   Returns: 1 if got input
526  *            0 if timeout
527  *           -1 if EOF or error
528  */
529 int 
530 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
531 {
532    int len;  
533    if (!stop) {
534       if (output == stdout || tee) {
535          sendit(prompt);
536       }
537    }
538 again:
539    switch (wait_for_data(fileno(input), sec)) {
540    case 0:
541       return 0;                    /* timeout */
542    case -1: 
543       return -1;                   /* error */
544    default:
545       len = sizeof_pool_memory(sock->msg) - 1;
546       if (stop) {
547          sleep(1);
548          goto again;
549       }
550 #ifdef HAVE_CONIO
551       if (isatty(fileno(input))) {
552          input_line(sock->msg, len);
553          break;
554       }
555 #endif
556       if (fgets(sock->msg, len, input) == NULL) {
557          return -1;
558       }
559       break;
560    }
561    strip_trailing_junk(sock->msg);
562    sock->msglen = strlen(sock->msg);
563    return 1;
564 }
565
566 #endif
567
568 static int versioncmd(FILE *input, BSOCK *UA_sock)
569 {
570    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
571       HOST_OS, DISTNAME, DISTVER);
572    return 1;
573 }
574
575 static int inputcmd(FILE *input, BSOCK *UA_sock)
576 {
577    FILE *fd;
578
579    if (argc > 2) {
580       sendit(_("Too many arguments on input command.\n"));
581       return 1;
582    }
583    if (argc == 1) {
584       sendit(_("First argument to input command must be a filename.\n"));
585       return 1;
586    }
587    fd = fopen(argk[1], "r");
588    if (!fd) {
589       senditf(_("Cannot open file %s for input. ERR=%s\n"), 
590          argk[1], strerror(errno));
591       return 1; 
592    }
593    read_and_process_input(fd, UA_sock);
594    fclose(fd);
595    return 1;
596 }
597
598 /* Send output to both termina and specified file */
599 static int teecmd(FILE *input, BSOCK *UA_sock)
600 {
601    tee = true;
602    return do_outputcmd(input, UA_sock);
603 }
604
605 /* Send output to specified "file" */
606 static int outputcmd(FILE *input, BSOCK *UA_sock)
607 {
608    tee = false;
609    return do_outputcmd(input, UA_sock);
610 }
611
612
613 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
614 {
615    FILE *fd;
616    char *mode = "a+";
617
618    if (argc > 3) {
619       sendit(_("Too many arguments on output/tee command.\n"));
620       return 1;
621    }
622    if (argc == 1) {
623       if (output != stdout) {
624          fclose(output);
625          output = stdout;
626          tee = false;
627       }
628       return 1;
629    }
630    if (argc == 3) {
631       mode = argk[2];
632    }
633    fd = fopen(argk[1], mode);
634    if (!fd) {
635       senditf(_("Cannot open file %s for output. ERR=%s\n"), 
636          argk[1], strerror(errno));
637       return 1; 
638    }
639    output = fd;
640    return 1;
641 }
642
643 static int quitcmd(FILE *input, BSOCK *UA_sock)
644 {
645    return 0;
646 }
647
648 static int sleepcmd(FILE *input, BSOCK *UA_sock)
649 {
650    if (argc > 1) {
651       sleep(atoi(argk[1]));
652    }
653    return 1;
654 }
655
656
657 static int timecmd(FILE *input, BSOCK *UA_sock)
658 {
659    char sdt[50];
660    time_t ttime = time(NULL);
661    struct tm tm;
662    localtime_r(&ttime, &tm);
663    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
664    sendit("\n");
665    return 1;
666 }
667
668 /*
669  * Send a line to the output file and or the terminal
670  */
671 void senditf(char *fmt,...)
672 {
673     char buf[3000];
674     va_list arg_ptr;
675
676     va_start(arg_ptr, fmt);
677     bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
678     va_end(arg_ptr);
679     sendit(buf);
680 }
681
682 void sendit(char *buf)
683 {
684 #ifdef xHAVE_CONIO
685     if (output == stdout || tee) {
686        char *p, *q;     
687        /*
688         * Here, we convert every \n into \r\n because the
689         *  terminal is in raw mode when we are using 
690         *  conio.
691         */
692        for (p=q=buf; (p=strchr(q, '\n')); ) {
693           if (p-q > 0) {
694              t_sendl(q, p-q);
695           }
696           t_sendl("\r\n", 2);
697           q = ++p;                    /* point after \n */
698        }
699        if (*q) {
700           t_send(q);
701        }
702     }
703     if (output != stdout) {
704        fputs(buf, output);
705     }
706 #else
707     fputs(buf, output);
708     if (tee) {
709        fputs(buf, stdout);
710     }
711     fflush(stdout);
712 #endif
713 }