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