]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Implement Qmsg() + conio linking
[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    if (usrbrk()) {
573       clrbrk();
574    }
575    strip_trailing_junk(sock->msg);
576    sock->msglen = strlen(sock->msg);
577    return 1;
578 }
579
580 #endif
581
582 static int versioncmd(FILE *input, BSOCK *UA_sock)
583 {
584    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
585       HOST_OS, DISTNAME, DISTVER);
586    return 1;
587 }
588
589 static int inputcmd(FILE *input, BSOCK *UA_sock)
590 {
591    FILE *fd;
592
593    if (argc > 2) {
594       sendit(_("Too many arguments on input command.\n"));
595       return 1;
596    }
597    if (argc == 1) {
598       sendit(_("First argument to input command must be a filename.\n"));
599       return 1;
600    }
601    fd = fopen(argk[1], "r");
602    if (!fd) {
603       senditf(_("Cannot open file %s for input. ERR=%s\n"), 
604          argk[1], strerror(errno));
605       return 1; 
606    }
607    read_and_process_input(fd, UA_sock);
608    fclose(fd);
609    return 1;
610 }
611
612 /* Send output to both termina and specified file */
613 static int teecmd(FILE *input, BSOCK *UA_sock)
614 {
615    tee = true;
616    return do_outputcmd(input, UA_sock);
617 }
618
619 /* Send output to specified "file" */
620 static int outputcmd(FILE *input, BSOCK *UA_sock)
621 {
622    tee = false;
623    return do_outputcmd(input, UA_sock);
624 }
625
626
627 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
628 {
629    FILE *fd;
630    char *mode = "a+";
631
632    if (argc > 3) {
633       sendit(_("Too many arguments on output/tee command.\n"));
634       return 1;
635    }
636    if (argc == 1) {
637       if (output != stdout) {
638          fclose(output);
639          output = stdout;
640          tee = false;
641       }
642       return 1;
643    }
644    if (argc == 3) {
645       mode = argk[2];
646    }
647    fd = fopen(argk[1], mode);
648    if (!fd) {
649       senditf(_("Cannot open file %s for output. ERR=%s\n"), 
650          argk[1], strerror(errno));
651       return 1; 
652    }
653    output = fd;
654    return 1;
655 }
656
657 static int quitcmd(FILE *input, BSOCK *UA_sock)
658 {
659    return 0;
660 }
661
662 static int sleepcmd(FILE *input, BSOCK *UA_sock)
663 {
664    if (argc > 1) {
665       sleep(atoi(argk[1]));
666    }
667    return 1;
668 }
669
670
671 static int timecmd(FILE *input, BSOCK *UA_sock)
672 {
673    char sdt[50];
674    time_t ttime = time(NULL);
675    struct tm tm;
676    localtime_r(&ttime, &tm);
677    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
678    sendit("\n");
679    return 1;
680 }
681
682 /*
683  * Send a line to the output file and or the terminal
684  */
685 void senditf(char *fmt,...)
686 {
687     char buf[3000];
688     va_list arg_ptr;
689
690     va_start(arg_ptr, fmt);
691     bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
692     va_end(arg_ptr);
693     sendit(buf);
694 }
695
696 void sendit(char *buf)
697 {
698 #ifdef xHAVE_CONIO
699     if (output == stdout || tee) {
700        char *p, *q;     
701        /*
702         * Here, we convert every \n into \r\n because the
703         *  terminal is in raw mode when we are using 
704         *  conio.
705         */
706        for (p=q=buf; (p=strchr(q, '\n')); ) {
707           if (p-q > 0) {
708              t_sendl(q, p-q);
709           }
710           t_sendl("\r\n", 2);
711           q = ++p;                    /* point after \n */
712        }
713        if (*q) {
714           t_send(q);
715        }
716     }
717     if (output != stdout) {
718        fputs(buf, output);
719     }
720 #else
721     fputs(buf, output);
722     if (tee) {
723        fputs(buf, stdout);
724     }
725     fflush(stdout);
726 #endif
727 }