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