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