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