]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Fix: clock diff, Dan's patch, Nic's patch, segfault
[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    read_and_process_input(stdin, UA_sock);
383
384    if (UA_sock) {
385       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
386       bnet_close(UA_sock);
387    }
388
389    terminate_console(0);
390    return 0;
391 }
392
393
394 /* Cleanup and then exit */
395 static void terminate_console(int sig)
396 {
397    static int already_here = FALSE;
398
399    if (already_here) {                /* avoid recursive temination problems */
400       exit(1);
401    }
402    already_here = TRUE;
403    free_pool_memory(args);
404    if (sig != 0) {
405       exit(1);
406    }
407    return;
408 }
409
410 #ifdef HAVE_READLINE
411 #define READLINE_LIBRARY 1
412 #undef free
413 #include "readline.h"
414 #include "history.h"
415
416
417 int 
418 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
419 {
420    char *line;
421
422    rl_catch_signals = 0;              /* do it ourselves */
423    line = readline(prompt);
424
425    if (!line) {
426       exit(1);
427    }
428    strip_trailing_junk(line);
429    sock->msglen = pm_strcpy(&sock->msg, line);
430    if (sock->msglen) {
431       add_history(sock->msg);
432    }
433    free(line);
434    return 1;
435 }
436
437 #else /* no readline, do it ourselves */
438
439 /*
440  *   Returns: 1 if data available
441  *            0 if timeout
442  *           -1 if error
443  */
444 static int
445 wait_for_data(int fd, int sec)
446 {
447    fd_set fdset;
448    struct timeval tv;
449
450    tv.tv_sec = sec;
451    tv.tv_usec = 0;
452    for ( ;; ) {
453       FD_ZERO(&fdset);
454       FD_SET(fd, &fdset);
455       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
456       case 0:                         /* timeout */
457          return 0;
458       case -1:
459          if (errno == EINTR || errno == EAGAIN) {
460             continue;
461          }
462          return -1;                  /* error return */
463       default:
464          return 1;
465       }
466    }
467 }
468
469 /*      
470  * Get next input command from terminal. 
471  *
472  *   Returns: 1 if got input
473  *            0 if timeout
474  *           -1 if EOF or error
475  */
476 int 
477 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
478 {
479    int len;  
480    if (!stop) {
481       if (output == stdout || tee) {
482          fputs(prompt, stdout);
483          fflush(stdout);
484       }
485    }
486 again:
487    switch (wait_for_data(fileno(input), sec)) {
488    case 0:
489       return 0;                    /* timeout */
490    case -1: 
491       return -1;                   /* error */
492    default:
493       len = sizeof_pool_memory(sock->msg) - 1;
494       if (stop) {
495          sleep(1);
496          goto again;
497       }
498       if (fgets(sock->msg, len, input) == NULL) {
499          return -1;
500       }
501       break;
502    }
503    strip_trailing_junk(sock->msg);
504    sock->msglen = strlen(sock->msg);
505    return 1;
506 }
507
508 #endif
509
510 static int versioncmd(FILE *input, BSOCK *UA_sock)
511 {
512    sendit("Version: " VERSION " (" BDATE ") %s %s %s\n",
513       HOST_OS, DISTNAME, DISTVER);
514    return 1;
515 }
516
517 static int inputcmd(FILE *input, BSOCK *UA_sock)
518 {
519    FILE *fd;
520
521    if (argc > 2) {
522       sendit(_("Too many arguments on input command.\n"));
523       return 1;
524    }
525    if (argc == 1) {
526       sendit(_("First argument to input command must be a filename.\n"));
527       return 1;
528    }
529    fd = fopen(argk[1], "r");
530    if (!fd) {
531       sendit(_("Cannot open file %s for input. ERR=%s\n"), 
532          argk[1], strerror(errno));
533       return 1; 
534    }
535    read_and_process_input(fd, UA_sock);
536    fclose(fd);
537    return 1;
538 }
539
540 static int teecmd(FILE *input, BSOCK *UA_sock)
541 {
542    tee = 1;
543    return do_outputcmd(input, UA_sock);
544 }
545
546 static int outputcmd(FILE *input, BSOCK *UA_sock)
547 {
548    tee = 0;
549    return do_outputcmd(input, UA_sock);
550 }
551
552
553 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
554 {
555    FILE *fd;
556    char *mode = "a+";
557
558    if (argc > 3) {
559       sendit(_("Too many arguments on output/tee command.\n"));
560       return 1;
561    }
562    if (argc == 1) {
563       if (output != stdout) {
564          fclose(output);
565          output = stdout;
566          tee = 0;
567       }
568       return 1;
569    }
570    if (argc == 3) {
571       mode = argk[2];
572    }
573    fd = fopen(argk[1], mode);
574    if (!fd) {
575       sendit(_("Cannot open file %s for output. ERR=%s\n"), 
576          argk[1], strerror(errno));
577       return 1; 
578    }
579    output = fd;
580    return 1;
581 }
582
583 static int quitcmd(FILE *input, BSOCK *UA_sock)
584 {
585    return 0;
586 }
587
588 static int sleepcmd(FILE *input, BSOCK *UA_sock)
589 {
590    if (argc > 1) {
591       sleep(atoi(argk[1]));
592    }
593    return 1;
594 }
595
596
597 static int timecmd(FILE *input, BSOCK *UA_sock)
598 {
599    char sdt[50];
600    time_t ttime = time(NULL);
601    struct tm tm;
602    localtime_r(&ttime, &tm);
603    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
604    sendit(sdt);
605    sendit("\n");
606    return 1;
607 }
608
609 static void sendit(char *fmt,...)
610 {
611     char buf[3000];
612     va_list arg_ptr;
613
614     va_start(arg_ptr, fmt);
615     bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
616     va_end(arg_ptr);
617     fputs(buf, output);
618     if (tee) {
619        fputs(buf, stdout);
620     }
621     fflush(stdout);
622 }