]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Doc updates
[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, CONRES *cons);
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          if (usrbrk() == 1) {
200             clrbrk();
201          }
202          if (usrbrk()) {
203             break;
204          }
205       } else {
206          /* Reading input from a file */
207          int len = sizeof_pool_memory(UA_sock->msg) - 1;
208          if (usrbrk()) {
209             break;
210          }
211          if (fgets(UA_sock->msg, len, input) == NULL) {
212             stat = -1;
213          } else {
214             sendit(UA_sock->msg);  /* echo to terminal */
215             strip_trailing_junk(UA_sock->msg);
216             UA_sock->msglen = strlen(UA_sock->msg);
217             stat = 1;
218          }
219       }
220       if (stat < 0) {
221          break;                       /* error or interrupt */
222       } else if (stat == 0) {         /* timeout */
223          if (strcmp(prompt, "*") == 0) {
224             bnet_fsend(UA_sock, ".messages");
225          } else {
226             continue;
227          }
228       } else {
229          at_prompt = FALSE;
230          /* @ => internal command for us */
231          if (UA_sock->msg[0] == '@') {
232             parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
233             if (!do_a_command(input, UA_sock)) {
234                break;
235             }
236             continue;
237          }
238          if (!bnet_send(UA_sock)) {   /* send command */
239             break;                    /* error */
240          }
241       }
242       if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
243          break;
244       }
245       while ((stat = bnet_recv(UA_sock)) >= 0) {
246          if (at_prompt) {
247             if (!stop) {
248                sendit("\n");
249             }
250             at_prompt = false;
251          }
252          /* Suppress output if running in background or user hit ctl-c */
253          if (!stop && !usrbrk()) {
254             sendit(UA_sock->msg);
255          }
256       }
257       if (usrbrk() > 1) {
258          break;
259       } else {
260          clrbrk();
261       }
262       if (!stop) {
263          fflush(stdout);
264       }
265       if (is_bnet_stop(UA_sock)) {
266          break;                       /* error or term */
267       } else if (stat == BNET_SIGNAL) {
268          if (UA_sock->msglen == BNET_PROMPT) {
269             at_prompt = true;
270          }
271          Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
272       }
273    }
274 }
275
276
277 /*********************************************************************
278  *
279  *         Main Bacula Console -- User Interface Program
280  *
281  */
282 int main(int argc, char *argv[])
283 {
284    int ch, i, ndir, item;
285    bool no_signals = false;
286    bool test_config = false;
287    JCR jcr;
288
289    init_stack_dump();
290    my_name_is(argc, argv, "bconsole");
291    textdomain("bacula-console");
292    init_msg(NULL, NULL);
293    working_directory = "/tmp";
294    args = get_pool_memory(PM_FNAME);
295    con_init(stdin);
296
297    while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
298       switch (ch) {
299       case 'c':                    /* configuration file */
300          if (configfile != NULL) {
301             free(configfile);
302          }
303          configfile = bstrdup(optarg);
304          break;
305
306       case 'd':
307          debug_level = atoi(optarg);
308          if (debug_level <= 0) {
309             debug_level = 1;
310          }
311          break;
312
313       case 's':                    /* turn off signals */
314          no_signals = true;
315          break;
316
317       case 't':
318          test_config = true;
319          break;
320
321       case '?':
322       default:
323          usage();
324          con_term();
325          exit(1);
326       }  
327    }
328    argc -= optind;
329    argv += optind;
330
331    if (!no_signals) {
332       init_signals(terminate_console);
333    }
334
335    /* Override Bacula default signals */
336    signal(SIGCHLD, SIG_IGN);
337    signal(SIGQUIT, SIG_IGN);
338    signal(SIGTSTP, got_sigstop);
339    signal(SIGCONT, got_sigcontinue);
340    signal(SIGTTIN, got_sigtin);
341    signal(SIGTTOU, got_sigtout);
342    trapctlc();
343
344    if (argc) {
345       usage();
346       con_term();
347       exit(1);
348    }
349
350    if (configfile == NULL) {
351       configfile = bstrdup(CONFIG_FILE);
352    }
353
354    parse_config(configfile);
355
356    LockRes();
357    ndir = 0;
358    foreach_res(dir, R_DIRECTOR) {
359       ndir++;
360    }
361    UnlockRes();
362    if (ndir == 0) {
363       con_term();
364       Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
365 Without that I don't how to speak to the Director :-(\n"), configfile);
366    }
367
368    if (test_config) {
369       terminate_console(0);
370       exit(0);
371    }
372
373    memset(&jcr, 0, sizeof(jcr));
374
375
376    if (ndir > 1) {
377       struct sockaddr_in client_addr;
378       memset(&client_addr, 0, sizeof(client_addr));
379       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
380 try_again:
381       sendit(_("Available Directors:\n"));
382       LockRes();
383       ndir = 0;
384       foreach_res(dir, R_DIRECTOR) {
385          senditf( _("%d  %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
386             dir->DIRport);
387       }
388       UnlockRes();
389       if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
390          return 1;
391       }
392       item = atoi(UA_sock->msg);
393       if (item < 0 || item > ndir) {
394          senditf(_("You must enter a number between 1 and %d\n"), ndir);
395          goto try_again;
396       }
397       LockRes();
398       dir = NULL;
399       for (i=0; i<item; i++) {
400          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
401       }
402       UnlockRes();
403       term_bsock(UA_sock);
404    } else {
405       LockRes();
406       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
407       UnlockRes();
408    }
409       
410    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
411    UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address, 
412                           NULL, dir->DIRport, 0);
413    if (UA_sock == NULL) {
414       terminate_console(0);
415       return 1;
416    }
417    jcr.dir_bsock = UA_sock;
418
419    LockRes();
420    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
421    UnlockRes();
422    /* If cons==NULL, default console will be used */
423    if (!authenticate_director(&jcr, dir, cons)) {
424       fprintf(stderr, "ERR=%s", UA_sock->msg);
425       terminate_console(0);
426       return 1;
427    }
428
429    Dmsg0(40, "Opened connection with Director daemon\n");
430
431    sendit(_("Enter a period to cancel a command.\n"));
432
433    /* Run commands in ~/.bconsolerc if any */
434    char *env = getenv("HOME");
435    if (env) {
436       FILE *fd;
437       pm_strcpy(&UA_sock->msg, env);
438       pm_strcat(&UA_sock->msg, "/.bconsolerc");
439       fd = fopen(UA_sock->msg, "r");
440       if (fd) {
441          read_and_process_input(fd, UA_sock);
442          fclose(fd);
443       }
444    }
445
446    read_and_process_input(stdin, UA_sock);
447
448    if (UA_sock) {
449       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
450       bnet_close(UA_sock);
451    }
452
453    terminate_console(0);
454    return 0;
455 }
456
457
458 /* Cleanup and then exit */
459 static void terminate_console(int sig)
460 {
461    static bool already_here = false;
462
463    if (already_here) {                /* avoid recursive temination problems */
464       exit(1);
465    }
466    already_here = true;
467    free_pool_memory(args);
468    con_term();
469    if (sig != 0) {
470       exit(1);
471    }
472    return;
473 }
474
475 #ifdef HAVE_READLINE
476 #define READLINE_LIBRARY 1
477 #undef free
478 #include "readline.h"
479 #include "history.h"
480
481
482 int 
483 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
484 {
485    char *line;
486
487    rl_catch_signals = 0;              /* do it ourselves */
488    line = readline(prompt);
489
490    if (!line) {
491       exit(1);
492    }
493    strip_trailing_junk(line);
494    sock->msglen = pm_strcpy(&sock->msg, line);
495    if (sock->msglen) {
496       add_history(sock->msg);
497    }
498    free(line);
499    return 1;
500 }
501
502 #else /* no readline, do it ourselves */
503
504 /*
505  *   Returns: 1 if data available
506  *            0 if timeout
507  *           -1 if error
508  */
509 static int
510 wait_for_data(int fd, int sec)
511 {
512    fd_set fdset;
513    struct timeval tv;
514
515    tv.tv_sec = sec;
516    tv.tv_usec = 0;
517    for ( ;; ) {
518       FD_ZERO(&fdset);
519       FD_SET(fd, &fdset);
520       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
521       case 0:                         /* timeout */
522          return 0;
523       case -1:
524          if (errno == EINTR || errno == EAGAIN) {
525             continue;
526          }
527          return -1;                  /* error return */
528       default:
529          return 1;
530       }
531    }
532 }
533
534 /*      
535  * Get next input command from terminal. 
536  *
537  *   Returns: 1 if got input
538  *            0 if timeout
539  *           -1 if EOF or error
540  */
541 int 
542 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
543 {
544    int len;  
545    if (!stop) {
546       if (output == stdout || tee) {
547          sendit(prompt);
548       }
549    }
550 again:
551    switch (wait_for_data(fileno(input), sec)) {
552    case 0:
553       return 0;                    /* timeout */
554    case -1: 
555       return -1;                   /* error */
556    default:
557       len = sizeof_pool_memory(sock->msg) - 1;
558       if (stop) {
559          sleep(1);
560          goto again;
561       }
562 #ifdef HAVE_CONIO
563       if (isatty(fileno(input))) {
564          input_line(sock->msg, len);
565          break;
566       }
567 #endif
568       if (fgets(sock->msg, len, input) == NULL) {
569          return -1;
570       }
571       break;
572    }
573    if (usrbrk()) {
574       clrbrk();
575    }
576    strip_trailing_junk(sock->msg);
577    sock->msglen = strlen(sock->msg);
578    return 1;
579 }
580
581 #endif
582
583 static int versioncmd(FILE *input, BSOCK *UA_sock)
584 {
585    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
586       HOST_OS, DISTNAME, DISTVER);
587    return 1;
588 }
589
590 static int inputcmd(FILE *input, BSOCK *UA_sock)
591 {
592    FILE *fd;
593
594    if (argc > 2) {
595       sendit(_("Too many arguments on input command.\n"));
596       return 1;
597    }
598    if (argc == 1) {
599       sendit(_("First argument to input command must be a filename.\n"));
600       return 1;
601    }
602    fd = fopen(argk[1], "r");
603    if (!fd) {
604       senditf(_("Cannot open file %s for input. ERR=%s\n"), 
605          argk[1], strerror(errno));
606       return 1; 
607    }
608    read_and_process_input(fd, UA_sock);
609    fclose(fd);
610    return 1;
611 }
612
613 /* Send output to both termina and specified file */
614 static int teecmd(FILE *input, BSOCK *UA_sock)
615 {
616    tee = true;
617    return do_outputcmd(input, UA_sock);
618 }
619
620 /* Send output to specified "file" */
621 static int outputcmd(FILE *input, BSOCK *UA_sock)
622 {
623    tee = false;
624    return do_outputcmd(input, UA_sock);
625 }
626
627
628 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
629 {
630    FILE *fd;
631    char *mode = "a+";
632
633    if (argc > 3) {
634       sendit(_("Too many arguments on output/tee command.\n"));
635       return 1;
636    }
637    if (argc == 1) {
638       if (output != stdout) {
639          fclose(output);
640          output = stdout;
641          tee = false;
642       }
643       return 1;
644    }
645    if (argc == 3) {
646       mode = argk[2];
647    }
648    fd = fopen(argk[1], mode);
649    if (!fd) {
650       senditf(_("Cannot open file %s for output. ERR=%s\n"), 
651          argk[1], strerror(errno));
652       return 1; 
653    }
654    output = fd;
655    return 1;
656 }
657
658 static int quitcmd(FILE *input, BSOCK *UA_sock)
659 {
660    return 0;
661 }
662
663 static int sleepcmd(FILE *input, BSOCK *UA_sock)
664 {
665    if (argc > 1) {
666       sleep(atoi(argk[1]));
667    }
668    return 1;
669 }
670
671
672 static int timecmd(FILE *input, BSOCK *UA_sock)
673 {
674    char sdt[50];
675    time_t ttime = time(NULL);
676    struct tm tm;
677    localtime_r(&ttime, &tm);
678    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
679    sendit("\n");
680    return 1;
681 }
682
683 /*
684  * Send a line to the output file and or the terminal
685  */
686 void senditf(char *fmt,...)
687 {
688     char buf[3000];
689     va_list arg_ptr;
690
691     va_start(arg_ptr, fmt);
692     bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
693     va_end(arg_ptr);
694     sendit(buf);
695 }
696
697 void sendit(char *buf)
698 {
699 #ifdef xHAVE_CONIO
700     if (output == stdout || tee) {
701        char *p, *q;     
702        /*
703         * Here, we convert every \n into \r\n because the
704         *  terminal is in raw mode when we are using 
705         *  conio.
706         */
707        for (p=q=buf; (p=strchr(q, '\n')); ) {
708           if (p-q > 0) {
709              t_sendl(q, p-q);
710           }
711           t_sendl("\r\n", 2);
712           q = ++p;                    /* point after \n */
713        }
714        if (*q) {
715           t_send(q);
716        }
717     }
718     if (output != stdout) {
719        fputs(buf, output);
720     }
721 #else
722     fputs(buf, output);
723     if (tee) {
724        fputs(buf, stdout);
725     }
726     fflush(stdout);
727 #endif
728 }