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