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