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