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