]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Fix console prompt + apply Nic's file descriptor leak fix
[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 (at_prompt) {
195             bnet_fsend(UA_sock, ".messages");
196          }
197       } else {
198          at_prompt = FALSE;
199          /* @ => internal command for us */
200          if (UA_sock->msg[0] == '@') {
201             parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
202             if (!do_a_command(input, UA_sock)) {
203                break;
204             }
205             continue;
206          }
207          if (!bnet_send(UA_sock)) {   /* send command */
208             break;                    /* error */
209          }
210       }
211       if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
212          break;
213       }
214       while ((stat = bnet_recv(UA_sock)) >= 0) {
215          if (at_prompt) {
216             if (!stop) {
217                sendit("\n");
218             }
219             at_prompt = FALSE;
220          }
221          if (!stop) {
222             sendit("%s", UA_sock->msg);
223          }
224       }
225       if (!stop) {
226          fflush(stdout);
227       }
228       if (is_bnet_stop(UA_sock)) {
229          break;                       /* error or term */
230       } else if (stat == BNET_SIGNAL) {
231          if (UA_sock->msglen == BNET_PROMPT) {
232             at_prompt = TRUE;
233          }
234          Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
235       }
236    }
237 }
238
239
240 /*********************************************************************
241  *
242  *         Main Bacula Console -- User Interface Program
243  *
244  */
245 int main(int argc, char *argv[])
246 {
247    int ch, i, ndir, item;
248    int no_signals = FALSE;
249    int test_config = FALSE;
250    JCR jcr;
251
252    init_stack_dump();
253    my_name_is(argc, argv, "console");
254    textdomain("bacula-console");
255    init_msg(NULL, NULL);
256    working_directory = "/tmp";
257    args = get_pool_memory(PM_FNAME);
258
259    while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
260       switch (ch) {
261          case 'c':                    /* configuration file */
262             if (configfile != NULL) {
263                free(configfile);
264             }
265             configfile = bstrdup(optarg);
266             break;
267
268          case 'd':
269             debug_level = atoi(optarg);
270             if (debug_level <= 0) {
271                debug_level = 1;
272             }
273             break;
274
275          case 's':                    /* turn off signals */
276             no_signals = TRUE;
277             break;
278
279          case 't':
280             test_config = TRUE;
281             break;
282
283          case '?':
284          default:
285             usage();
286
287       }  
288    }
289    argc -= optind;
290    argv += optind;
291
292    if (!no_signals) {
293       init_signals(terminate_console);
294    }
295    signal(SIGCHLD, SIG_IGN);
296    signal(SIGTSTP, got_stop);
297    signal(SIGCONT, got_continue);
298    signal(SIGTTIN, got_tin);
299    signal(SIGTTOU, got_tout);
300
301    if (argc) {
302       usage();
303    }
304
305    if (configfile == NULL) {
306       configfile = bstrdup(CONFIG_FILE);
307    }
308
309    parse_config(configfile);
310
311    LockRes();
312    ndir = 0;
313    for (dir=NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
314       ndir++;
315    }
316    UnlockRes();
317    if (ndir == 0) {
318       Emsg1(M_ERROR_TERM, 0, _("No Director resource defined in %s\n\
319 Without that I don't how to speak to the Director :-(\n"), configfile);
320    }
321
322    if (test_config) {
323       terminate_console(0);
324       exit(0);
325    }
326
327    memset(&jcr, 0, sizeof(jcr));
328
329    if (ndir > 1) {
330       UA_sock = init_bsock(NULL, 0, "", "", 0);
331 try_again:
332       sendit(_("Available Directors:\n"));
333       LockRes();
334       ndir = 0;
335       for (dir = NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
336          fprintf(output, _("%d  %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
337             dir->DIRport);
338       }
339       UnlockRes();
340       if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
341          return 1;
342       }
343       item = atoi(UA_sock->msg);
344       if (item < 0 || item > ndir) {
345          sendit(_("You must enter a number between 1 and %d\n"), ndir);
346          goto try_again;
347       }
348       LockRes();
349       dir = NULL;
350       for (i=0; i<item; i++) {
351          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
352       }
353       UnlockRes();
354       term_bsock(UA_sock);
355    } else {
356       LockRes();
357       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
358       UnlockRes();
359    }
360       
361
362    sendit(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
363    UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address, 
364                           NULL, dir->DIRport, 0);
365    if (UA_sock == NULL) {
366       terminate_console(0);
367       return 1;
368    }
369    jcr.dir_bsock = UA_sock;
370    if (!authenticate_director(&jcr, dir)) {
371       fprintf(stderr, "ERR=%s", UA_sock->msg);
372       terminate_console(0);
373       return 1;
374    }
375
376    Dmsg0(40, "Opened connection with Director daemon\n");
377
378    read_and_process_input(stdin, UA_sock);
379
380    if (UA_sock) {
381       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
382       bnet_close(UA_sock);
383    }
384
385    terminate_console(0);
386    return 0;
387 }
388
389
390 /* Cleanup and then exit */
391 static void terminate_console(int sig)
392 {
393    static int already_here = FALSE;
394
395    if (already_here) {                /* avoid recursive temination problems */
396       exit(1);
397    }
398    already_here = TRUE;
399    free_pool_memory(args);
400    if (sig != 0) {
401       exit(1);
402    }
403    return;
404 }
405
406 #ifdef HAVE_READLINE
407 #define READLINE_LIBRARY 1
408 #undef free
409 #include "readline.h"
410 #include "history.h"
411
412
413 int 
414 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
415 {
416    char *line;
417
418    rl_catch_signals = 0;              /* do it ourselves */
419    line = readline(prompt);
420
421    if (!line) {
422       exit(1);
423    }
424    strcpy(sock->msg, line);
425    strip_trailing_junk(sock->msg);
426    sock->msglen = strlen(sock->msg);
427    if (sock->msglen) {
428       add_history(sock->msg);
429    }
430    free(line);
431    return 1;
432 }
433
434 #else /* no readline, do it ourselves */
435
436 /*
437  *   Returns: 1 if data available
438  *            0 if timeout
439  *           -1 if error
440  */
441 static int
442 wait_for_data(int fd, int sec)
443 {
444    fd_set fdset;
445    struct timeval tv;
446
447    tv.tv_sec = sec;
448    tv.tv_usec = 0;
449    for ( ;; ) {
450       FD_ZERO(&fdset);
451       FD_SET(fd, &fdset);
452       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
453       case 0:                         /* timeout */
454          return 0;
455       case -1:
456          if (errno == EINTR || errno == EAGAIN) {
457             continue;
458          }
459          return -1;                  /* error return */
460       default:
461          return 1;
462       }
463    }
464 }
465
466 /*      
467  * Get next input command from terminal. 
468  *
469  *   Returns: 1 if got input
470  *            0 if timeout
471  *           -1 if EOF or error
472  */
473 int 
474 get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
475 {
476    int len;  
477    if (!stop) {
478       if (output == stdout || tee) {
479          fputs(prompt, stdout);
480          fflush(stdout);
481       }
482    }
483 again:
484    switch (wait_for_data(fileno(input), sec)) {
485    case 0:
486       return 0;                    /* timeout */
487    case -1: 
488       return -1;                   /* error */
489    default:
490       len = sizeof_pool_memory(sock->msg) - 1;
491       if (stop) {
492          sleep(1);
493          goto again;
494       }
495       if (fgets(sock->msg, len, input) == NULL) {
496          return -1;
497       }
498       break;
499    }
500    strip_trailing_junk(sock->msg);
501    sock->msglen = strlen(sock->msg);
502    return 1;
503 }
504
505 #endif
506
507 static int versioncmd(FILE *input, BSOCK *UA_sock)
508 {
509    sendit("Version: " VERSION " (" BDATE ") %s %s %s\n",
510       HOST_OS, DISTNAME, DISTVER);
511    return 1;
512 }
513
514 static int inputcmd(FILE *input, BSOCK *UA_sock)
515 {
516    FILE *fd;
517
518    if (argc > 2) {
519       sendit(_("Too many arguments.\n"));
520       return 0;
521    }
522    if (argc == 1) {
523       sendit(_("First argument must be a filename.\n"));
524       return 0;
525    }
526    fd = fopen(argk[1], "r");
527    if (!fd) {
528       sendit(_("Cannot open file. ERR=%s\n"), strerror(errno));
529       return 0; 
530    }
531    read_and_process_input(fd, UA_sock);
532    fclose(fd);
533    return 1;
534 }
535
536 static int teecmd(FILE *input, BSOCK *UA_sock)
537 {
538    tee = 1;
539    return do_outputcmd(input, UA_sock);
540 }
541
542 static int outputcmd(FILE *input, BSOCK *UA_sock)
543 {
544    tee = 0;
545    return do_outputcmd(input, UA_sock);
546 }
547
548
549 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
550 {
551    FILE *fd;
552    char *mode = "a+";
553
554    if (argc > 3) {
555       sendit(_("Too many arguments.\n"));
556       return 1;
557    }
558    if (argc == 1) {
559       if (output != stdout) {
560          fclose(output);
561          output = stdout;
562          tee = 0;
563       }
564       return 1;
565    }
566    if (argc == 3) {
567       mode = argk[2];
568    }
569    fd = fopen(argk[1], mode);
570    if (!fd) {
571       sendit(_("Cannot open file. ERR=%s\n"), strerror(errno));
572       return 1; 
573    }
574    output = fd;
575    return 1;
576 }
577
578 static int quitcmd(FILE *input, BSOCK *UA_sock)
579 {
580    return 0;
581 }
582
583 static int sleepcmd(FILE *input, BSOCK *UA_sock)
584 {
585    if (argc > 1) {
586       sleep(atoi(argk[1]));
587    }
588    return 1;
589 }
590
591
592 static int timecmd(FILE *input, BSOCK *UA_sock)
593 {
594    char sdt[50];
595    time_t ttime = time(NULL);
596    struct tm tm;
597    localtime_r(&ttime, &tm);
598    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
599    sendit(sdt);
600    sendit("\n");
601    return 1;
602 }
603
604 static void sendit(char *fmt,...)
605 {
606     char buf[3000];
607     va_list arg_ptr;
608
609     va_start(arg_ptr, fmt);
610     bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
611     va_end(arg_ptr);
612     fputs(buf, output);
613     if (tee) {
614        fputs(buf, stdout);
615     }
616     fflush(stdout);
617 }