]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Add Peter Eriksson's const code + turn bsscanf code
[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 /* 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]\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 { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const 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    const 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");
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    (void)WSA_Init();                        /* Initialize Windows sockets */
384
385    if (ndir > 1) {
386       struct sockaddr_in client_addr;
387       memset(&client_addr, 0, sizeof(client_addr));
388       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
389 try_again:
390       sendit(_("Available Directors:\n"));
391       LockRes();
392       ndir = 0;
393       foreach_res(dir, R_DIRECTOR) {
394          senditf( _("%d  %s at %s:%d\n"), 1+ndir++, dir->hdr.name, dir->address,
395             dir->DIRport);
396       }
397       UnlockRes();
398       if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
399          (void)WSACleanup();               /* Cleanup Windows sockets */
400          return 1;
401       }
402       item = atoi(UA_sock->msg);
403       if (item < 0 || item > ndir) {
404          senditf(_("You must enter a number between 1 and %d\n"), ndir);
405          goto try_again;
406       }
407       LockRes();
408       dir = NULL;
409       for (i=0; i<item; i++) {
410          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
411       }
412       UnlockRes();
413       term_bsock(UA_sock);
414    } else {
415       LockRes();
416       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
417       UnlockRes();
418    }
419       
420    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
421    UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address, 
422                           NULL, dir->DIRport, 0);
423    if (UA_sock == NULL) {
424       terminate_console(0);
425       return 1;
426    }
427    jcr.dir_bsock = UA_sock;
428
429    LockRes();
430    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
431    UnlockRes();
432    /* If cons==NULL, default console will be used */
433    if (!authenticate_director(&jcr, dir, cons)) {
434       fprintf(stderr, "ERR=%s", UA_sock->msg);
435       terminate_console(0);
436       return 1;
437    }
438
439    Dmsg0(40, "Opened connection with Director daemon\n");
440
441    sendit(_("Enter a period to cancel a command.\n"));
442
443    /* Run commands in ~/.bconsolerc if any */
444    char *env = getenv("HOME");
445    if (env) {
446       FILE *fd;
447       pm_strcpy(&UA_sock->msg, env);
448       pm_strcat(&UA_sock->msg, "/.bconsolerc");
449       fd = fopen(UA_sock->msg, "r");
450       if (fd) {
451          read_and_process_input(fd, UA_sock);
452          fclose(fd);
453       }
454    }
455
456    read_and_process_input(stdin, UA_sock);
457
458    if (UA_sock) {
459       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
460       bnet_close(UA_sock);
461    }
462
463    terminate_console(0);
464    return 0;
465 }
466
467
468 /* Cleanup and then exit */
469 static void terminate_console(int sig)
470 {
471
472    static bool already_here = false;
473
474    if (already_here) {                /* avoid recursive temination problems */
475       exit(1);
476    }
477    already_here = true;
478    free_pool_memory(args);
479    con_term();
480    (void)WSACleanup();               /* Cleanup Windows sockets */
481    if (sig != 0) {
482       exit(1);
483    }
484    return;
485 }
486
487 #ifdef HAVE_READLINE
488 #define READLINE_LIBRARY 1
489 #undef free
490 #include "readline.h"
491 #include "history.h"
492
493
494 int 
495 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
496 {
497    char *line;
498
499    rl_catch_signals = 0;              /* do it ourselves */
500    line = readline(prompt);
501
502    if (!line) {
503       exit(1);
504    }
505    strip_trailing_junk(line);
506    sock->msglen = pm_strcpy(&sock->msg, line);
507    if (sock->msglen) {
508       add_history(sock->msg);
509    }
510    free(line);
511    return 1;
512 }
513
514 #else /* no readline, do it ourselves */
515
516 /*
517  *   Returns: 1 if data available
518  *            0 if timeout
519  *           -1 if error
520  */
521 static int
522 wait_for_data(int fd, int sec)
523 {
524    fd_set fdset;
525    struct timeval tv;
526 #ifdef HAVE_WIN32
527    return 1;                          /* select doesn't seem to work on Win32 */
528 #endif
529
530    tv.tv_sec = sec;
531    tv.tv_usec = 0;
532    for ( ;; ) {
533       FD_ZERO(&fdset);
534       FD_SET(fd, &fdset);
535       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
536       case 0:                         /* timeout */
537          return 0;
538       case -1:
539          if (errno == EINTR || errno == EAGAIN) {
540             continue;
541          }
542          return -1;                  /* error return */
543       default:
544          return 1;
545       }
546    }
547 }
548
549 /*      
550  * Get next input command from terminal. 
551  *
552  *   Returns: 1 if got input
553  *            0 if timeout
554  *           -1 if EOF or error
555  */
556 int 
557 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
558 {
559    int len;  
560    if (!stop) {
561       if (output == stdout || tee) {
562          sendit(prompt);
563       }
564    }
565 again:
566    switch (wait_for_data(fileno(input), sec)) {
567    case 0:
568       return 0;                    /* timeout */
569    case -1: 
570       return -1;                   /* error */
571    default:
572       len = sizeof_pool_memory(sock->msg) - 1;
573       if (stop) {
574          sleep(1);
575          goto again;
576       }
577 #ifdef HAVE_CONIO
578       if (isatty(fileno(input))) {
579          input_line(sock->msg, len);
580          break;
581       }
582 #endif
583       if (fgets(sock->msg, len, input) == NULL) {
584          return -1;
585       }
586       break;
587    }
588    if (usrbrk()) {
589       clrbrk();
590    }
591    strip_trailing_junk(sock->msg);
592    sock->msglen = strlen(sock->msg);
593    return 1;
594 }
595
596 #endif
597
598 static int versioncmd(FILE *input, BSOCK *UA_sock)
599 {
600    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
601       HOST_OS, DISTNAME, DISTVER);
602    return 1;
603 }
604
605 static int inputcmd(FILE *input, BSOCK *UA_sock)
606 {
607    FILE *fd;
608
609    if (argc > 2) {
610       sendit(_("Too many arguments on input command.\n"));
611       return 1;
612    }
613    if (argc == 1) {
614       sendit(_("First argument to input command must be a filename.\n"));
615       return 1;
616    }
617    fd = fopen(argk[1], "r");
618    if (!fd) {
619       senditf(_("Cannot open file %s for input. ERR=%s\n"), 
620          argk[1], strerror(errno));
621       return 1; 
622    }
623    read_and_process_input(fd, UA_sock);
624    fclose(fd);
625    return 1;
626 }
627
628 /* Send output to both termina and specified file */
629 static int teecmd(FILE *input, BSOCK *UA_sock)
630 {
631    tee = true;
632    return do_outputcmd(input, UA_sock);
633 }
634
635 /* Send output to specified "file" */
636 static int outputcmd(FILE *input, BSOCK *UA_sock)
637 {
638    tee = false;
639    return do_outputcmd(input, UA_sock);
640 }
641
642
643 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
644 {
645    FILE *fd;
646    const char *mode = "a+";
647
648    if (argc > 3) {
649       sendit(_("Too many arguments on output/tee command.\n"));
650       return 1;
651    }
652    if (argc == 1) {
653       if (output != stdout) {
654          fclose(output);
655          output = stdout;
656          tee = false;
657       }
658       return 1;
659    }
660    if (argc == 3) {
661       mode = argk[2];
662    }
663    fd = fopen(argk[1], mode);
664    if (!fd) {
665       senditf(_("Cannot open file %s for output. ERR=%s\n"), 
666          argk[1], strerror(errno));
667       return 1; 
668    }
669    output = fd;
670    return 1;
671 }
672
673 static int quitcmd(FILE *input, BSOCK *UA_sock)
674 {
675    return 0;
676 }
677
678 static int sleepcmd(FILE *input, BSOCK *UA_sock)
679 {
680    if (argc > 1) {
681       sleep(atoi(argk[1]));
682    }
683    return 1;
684 }
685
686
687 static int timecmd(FILE *input, BSOCK *UA_sock)
688 {
689    char sdt[50];
690    time_t ttime = time(NULL);
691    struct tm tm;
692    localtime_r(&ttime, &tm);
693    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
694    sendit("\n");
695    return 1;
696 }
697
698 /*
699  * Send a line to the output file and or the terminal
700  */
701 void senditf(const char *fmt,...)
702 {
703     char buf[3000];
704     va_list arg_ptr;
705
706     va_start(arg_ptr, fmt);
707     bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
708     va_end(arg_ptr);
709     sendit(buf);
710 }
711
712 void sendit(const char *buf)
713 {
714 #ifdef xHAVE_CONIO
715     if (output == stdout || tee) {
716        char *p, *q;     
717        /*
718         * Here, we convert every \n into \r\n because the
719         *  terminal is in raw mode when we are using 
720         *  conio.
721         */
722        for (p=q=buf; (p=strchr(q, '\n')); ) {
723           if (p-q > 0) {
724              t_sendl(q, p-q);
725           }
726           t_sendl("\r\n", 2);
727           q = ++p;                    /* point after \n */
728        }
729        if (*q) {
730           t_send(q);
731        }
732     }
733     if (output != stdout) {
734        fputs(buf, output);
735     }
736 #else
737     fputs(buf, output);
738     if (tee) {
739        fputs(buf, stdout);
740     }
741     if (output == stdout || tee) {
742        fflush(stdout);
743     }
744 #endif
745 }