]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
kes Use get_catalog_resource() to open catalog database
[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    Copyright (C) 2000-2006 Kern Sibbald
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License
14    version 2 as amended with additional clauses defined in the
15    file LICENSE in the main source directory.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
20    the file LICENSE for additional details.
21
22  */
23
24 #include "bacula.h"
25 #include "console_conf.h"
26 #include "jcr.h"
27
28
29 #ifdef HAVE_CONIO
30 #include "conio.h"
31 //#define CONIO_FIX 1
32 #else
33 #define con_init(x)
34 #define con_term()
35 #define con_set_zed_keys();
36 #define trapctlc()
37 #define clrbrk()
38 #define usrbrk() 0
39 #endif
40
41 #if defined(HAVE_WIN32)
42 #define isatty(fd) (fd==0)
43 #endif
44
45 /* Exported variables */
46
47 extern int rl_catch_signals;
48
49 /* Imported functions */
50 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
51
52 /* Forward referenced functions */
53 static void terminate_console(int sig);
54 static int check_resources();
55 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
56 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
57 void senditf(const char *fmt, ...);
58 void sendit(const char *buf);
59
60 extern "C" void got_sigstop(int sig);
61 extern "C" void got_sigcontinue(int sig);
62 extern "C" void got_sigtout(int sig);
63 extern "C" void got_sigtin(int sig);
64
65
66 /* Static variables */
67 static char *configfile = NULL;
68 static BSOCK *UA_sock = NULL;
69 static DIRRES *dir = NULL;
70 static CONRES *cons = NULL;
71 static FILE *output = stdout;
72 static bool teeout = false;               /* output to output and stdout */
73 static bool stop = false;
74 static bool no_conio = false;
75 static int argc;
76 static int numdir;
77 static int numcon;
78 static POOLMEM *args;
79 static char *argk[MAX_CMD_ARGS];
80 static char *argv[MAX_CMD_ARGS];
81
82
83 /* Command prototypes */
84 static int versioncmd(FILE *input, BSOCK *UA_sock);
85 static int inputcmd(FILE *input, BSOCK *UA_sock);
86 static int outputcmd(FILE *input, BSOCK *UA_sock);
87 static int teecmd(FILE *input, BSOCK *UA_sock);
88 static int quitcmd(FILE *input, BSOCK *UA_sock);
89 static int timecmd(FILE *input, BSOCK *UA_sock);
90 static int sleepcmd(FILE *input, BSOCK *UA_sock);
91
92
93 #define CONFIG_FILE "./bconsole.conf"   /* default configuration file */
94
95 static void usage()
96 {
97    fprintf(stderr, _(
98 "Copyright (C) 2000-%s Kern Sibbald\n"
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 "       -n          no conio\n"
104 "       -s          no signals\n"
105 "       -t          test - read configuration and exit\n"
106 "       -?          print this message.\n"
107 "\n"), BYEAR, HOST_OS, DISTNAME, DISTVER);
108 }
109
110
111 extern "C"
112 void got_sigstop(int sig)
113 {
114    stop = true;
115 }
116
117 extern "C"
118 void got_sigcontinue(int sig)
119 {
120    stop = false;
121 }
122
123 extern "C"
124 void got_sigtout(int sig)
125 {
126 // printf("Got tout\n");
127 }
128
129 extern "C"
130 void got_sigtin(int sig)
131 {
132 // printf("Got tin\n");
133 }
134
135
136 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
137 {
138    con_set_zed_keys();
139    return 1;
140 }
141
142 /*
143  * These are the @command
144  */
145 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
146 static struct cmdstruct commands[] = {
147  { N_("input"),      inputcmd,     _("input from file")},
148  { N_("output"),     outputcmd,    _("output to file")},
149  { N_("quit"),       quitcmd,      _("quit")},
150  { N_("tee"),        teecmd,       _("output to file and terminal")},
151  { N_("sleep"),      sleepcmd,     _("sleep specified time")},
152  { N_("time"),       timecmd,      _("print current time")},
153  { N_("version"),    versioncmd,   _("print Console's version")},
154  { N_("exit"),       quitcmd,      _("exit = quit")},
155  { N_("zed_keys"),   zed_keyscmd,  _("zed_keys = use zed keys instead of bash keys")},
156              };
157 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
158
159 static int do_a_command(FILE *input, BSOCK *UA_sock)
160 {
161    unsigned int i;
162    int stat;
163    int found;
164    int len;
165    char *cmd;
166
167    found = 0;
168    stat = 1;
169
170    Dmsg1(120, "Command: %s\n", UA_sock->msg);
171    if (argc == 0) {
172       return 1;
173    }
174
175    cmd = argk[0]+1;
176    if (*cmd == '#') {                 /* comment */
177       return 1;
178    }
179    len = strlen(cmd);
180    for (i=0; i<comsize; i++) {     /* search for command */
181       if (strncasecmp(cmd,  _(commands[i].key), len) == 0) {
182          stat = (*commands[i].func)(input, UA_sock);   /* go execute command */
183          found = 1;
184          break;
185       }
186    }
187    if (!found) {
188       pm_strcat(&UA_sock->msg, _(": is an illegal command\n"));
189       UA_sock->msglen = strlen(UA_sock->msg);
190       sendit(UA_sock->msg);
191    }
192    return stat;
193 }
194
195
196 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
197 {
198    const char *prompt = "*";
199    bool at_prompt = false;
200    int tty_input = isatty(fileno(input));
201    int stat;
202
203    for ( ;; ) {
204       if (at_prompt) {                /* don't prompt multiple times */
205          prompt = "";
206       } else {
207          prompt = "*";
208          at_prompt = true;
209       }
210       if (tty_input) {
211          stat = get_cmd(input, prompt, UA_sock, 30);
212          if (usrbrk() == 1) {
213             clrbrk();
214          }
215          if (usrbrk()) {
216             break;
217          }
218       } else {
219          /* Reading input from a file */
220          int len = sizeof_pool_memory(UA_sock->msg) - 1;
221          if (usrbrk()) {
222             break;
223          }
224          if (fgets(UA_sock->msg, len, input) == NULL) {
225             stat = -1;
226          } else {
227             sendit(UA_sock->msg);  /* echo to terminal */
228             strip_trailing_junk(UA_sock->msg);
229             UA_sock->msglen = strlen(UA_sock->msg);
230             stat = 1;
231          }
232       }
233       if (stat < 0) {
234          break;                       /* error or interrupt */
235       } else if (stat == 0) {         /* timeout */
236          if (strcmp(prompt, "*") == 0) {
237             bnet_fsend(UA_sock, ".messages");
238          } else {
239             continue;
240          }
241       } else {
242          at_prompt = FALSE;
243          /* @ => internal command for us */
244          if (UA_sock->msg[0] == '@') {
245             parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
246             if (!do_a_command(input, UA_sock)) {
247                break;
248             }
249             continue;
250          }
251          if (!bnet_send(UA_sock)) {   /* send command */
252             break;                    /* error */
253          }
254       }
255       if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
256          break;
257       }
258       while ((stat = bnet_recv(UA_sock)) >= 0) {
259          if (at_prompt) {
260             if (!stop) {
261                sendit("\n");
262             }
263             at_prompt = false;
264          }
265          /* Suppress output if running in background or user hit ctl-c */
266          if (!stop && !usrbrk()) {
267             sendit(UA_sock->msg);
268          }
269       }
270       if (usrbrk() > 1) {
271          break;
272       } else {
273          clrbrk();
274       }
275       if (!stop) {
276          fflush(stdout);
277       }
278       if (is_bnet_stop(UA_sock)) {
279          break;                       /* error or term */
280       } else if (stat == BNET_SIGNAL) {
281          if (UA_sock->msglen == BNET_PROMPT) {
282             at_prompt = true;
283          }
284          Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
285       }
286    }
287 }
288
289 /*
290  * Call-back for reading a passphrase for an encrypted PEM file
291  * This function uses getpass(), 
292  *  which uses a static buffer and is NOT thread-safe.
293  */
294 static int tls_pem_callback(char *buf, int size, const void *userdata)
295 {
296 #ifdef HAVE_TLS
297    const char *prompt = (const char *)userdata;
298 # if defined(HAVE_WIN32)
299    sendit(prompt);
300    if (win32_cgets(buf, size) == NULL) {
301       buf[0] = 0;
302       return 0;
303    } else {
304       return strlen(buf);
305    }
306 # else
307    char *passwd;
308
309    passwd = getpass(prompt);
310    bstrncpy(buf, passwd, size);
311    return strlen(buf);
312 # endif
313 #else
314    buf[0] = 0;
315    return 0;
316 #endif
317 }
318
319
320 /*********************************************************************
321  *
322  *         Main Bacula Console -- User Interface Program
323  *
324  */
325 int main(int argc, char *argv[])
326 {
327    int ch, i, item;
328    bool no_signals = false;
329    bool test_config = false;
330    JCR jcr;
331
332    setlocale(LC_ALL, "");
333    bindtextdomain("bacula", LOCALEDIR);
334    textdomain("bacula");
335
336    init_stack_dump();
337    my_name_is(argc, argv, "bconsole");
338    init_msg(NULL, NULL);
339    working_directory = "/tmp";
340    args = get_pool_memory(PM_FNAME);
341
342    while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
343       switch (ch) {
344       case 'c':                    /* configuration file */
345          if (configfile != NULL) {
346             free(configfile);
347          }
348          configfile = bstrdup(optarg);
349          break;
350
351       case 'd':
352          debug_level = atoi(optarg);
353          if (debug_level <= 0) {
354             debug_level = 1;
355          }
356          break;
357
358       case 'n':                    /* no conio */
359          no_conio = true;
360          break;
361
362       case 's':                    /* turn off signals */
363          no_signals = true;
364          break;
365
366       case 't':
367          test_config = true;
368          break;
369
370       case '?':
371       default:
372          usage();
373          exit(1);
374       }
375    }
376    argc -= optind;
377    argv += optind;
378
379    if (!no_signals) {
380       init_signals(terminate_console);
381    }
382
383
384 #if !defined(HAVE_WIN32)
385    /* Override Bacula default signals */
386    signal(SIGQUIT, SIG_IGN);
387    signal(SIGTSTP, got_sigstop);
388    signal(SIGCONT, got_sigcontinue);
389    signal(SIGTTIN, got_sigtin);
390    signal(SIGTTOU, got_sigtout);
391    trapctlc();
392 #endif
393
394    OSDependentInit();
395
396    if (argc) {
397       usage();
398       exit(1);
399    }
400
401    if (configfile == NULL) {
402       configfile = bstrdup(CONFIG_FILE);
403    }
404
405    parse_config(configfile);
406
407    if (init_crypto() != 0) {
408       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
409    }
410
411    if (!check_resources()) {
412       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
413    }
414
415    if (!no_conio) {
416       con_init(stdin);
417    }
418
419    if (test_config) {
420       terminate_console(0);
421       exit(0);
422    }
423
424    memset(&jcr, 0, sizeof(jcr));
425
426    (void)WSA_Init();                        /* Initialize Windows sockets */
427
428    LockRes();
429    numdir = 0;
430    foreach_res(dir, R_DIRECTOR) {
431       numdir++;
432    }
433    numcon = 0;
434    foreach_res(cons, R_CONSOLE) {
435       numcon++;
436    }
437    UnlockRes();
438
439    if (numdir > 1) {
440       struct sockaddr client_addr;
441       memset(&client_addr, 0, sizeof(client_addr));
442       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
443 try_again:
444       sendit(_("Available Directors:\n"));
445       LockRes();
446       numdir = 0;
447       foreach_res(dir, R_DIRECTOR) {
448          senditf( _("%d  %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
449             dir->DIRport);
450       }
451       UnlockRes();
452       if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
453          (void)WSACleanup();               /* Cleanup Windows sockets */
454          return 1;
455       }
456       item = atoi(UA_sock->msg);
457       if (item < 0 || item > numdir) {
458          senditf(_("You must enter a number between 1 and %d\n"), numdir);
459          goto try_again;
460       }
461       term_bsock(UA_sock);
462       LockRes();
463       for (i=0; i<item; i++) {
464          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
465       }
466       /* Look for a console linked to this director */
467       for (i=0; i<numcon; i++) {
468          cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
469          if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
470             break;
471          }
472          cons = NULL;
473       }
474       /* Look for the first non-linked console */
475       if (cons == NULL) {
476          for (i=0; i<numcon; i++) {
477             cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
478             if (cons->director == NULL)
479                break;
480             cons = NULL;
481         }
482       }
483       UnlockRes();
484    }
485    /* If no director, take first one */
486    if (!dir) {
487       LockRes();
488       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
489       UnlockRes();
490    }
491    /* If no console, take first one */
492    if (!cons) {
493       LockRes();
494       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
495       UnlockRes();
496    }
497
498    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
499
500    char buf[1024];
501    /* Initialize Console TLS context */
502    if (cons && (cons->tls_enable || cons->tls_require)) {
503       /* Generate passphrase prompt */
504       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
505
506       /* Initialize TLS context:
507        * Args: CA certfile, CA certdir, Certfile, Keyfile,
508        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
509       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
510          cons->tls_ca_certdir, cons->tls_certfile,
511          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
512
513       if (!cons->tls_ctx) {
514          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
515             dir->hdr.name);
516          terminate_console(0);
517          return 1;
518       }
519    }
520
521    /* Initialize Director TLS context */
522    if (dir->tls_enable || dir->tls_require) {
523       /* Generate passphrase prompt */
524       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
525
526       /* Initialize TLS context:
527        * Args: CA certfile, CA certdir, Certfile, Keyfile,
528        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
529       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
530          dir->tls_ca_certdir, dir->tls_certfile,
531          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
532
533       if (!dir->tls_ctx) {
534          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
535             dir->hdr.name);
536          terminate_console(0);
537          return 1;
538       }
539    }
540
541    UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
542                           NULL, dir->DIRport, 0);
543    if (UA_sock == NULL) {
544       terminate_console(0);
545       return 1;
546    }
547    jcr.dir_bsock = UA_sock;
548
549    /* If cons==NULL, default console will be used */
550    if (!authenticate_director(&jcr, dir, cons)) {
551       terminate_console(0);
552       return 1;
553    }
554
555    Dmsg0(40, "Opened connection with Director daemon\n");
556
557    sendit(_("Enter a period to cancel a command.\n"));
558
559    /* Run commands in ~/.bconsolerc if any */
560    char *env = getenv("HOME");
561    if (env) {
562       FILE *fd;
563       pm_strcpy(&UA_sock->msg, env);
564       pm_strcat(&UA_sock->msg, "/.bconsolerc");
565       fd = fopen(UA_sock->msg, "rb");
566       if (fd) {
567          read_and_process_input(fd, UA_sock);
568          fclose(fd);
569       }
570    }
571
572    read_and_process_input(stdin, UA_sock);
573
574    if (UA_sock) {
575       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
576       bnet_close(UA_sock);
577    }
578
579    terminate_console(0);
580    return 0;
581 }
582
583
584 /* Cleanup and then exit */
585 static void terminate_console(int sig)
586 {
587
588    static bool already_here = false;
589
590    if (already_here) {                /* avoid recursive temination problems */
591       exit(1);
592    }
593    already_here = true;
594    cleanup_crypto();
595    free_pool_memory(args);
596    if (!no_conio) {
597       con_term();
598    }
599    (void)WSACleanup();               /* Cleanup Windows sockets */
600    if (sig != 0) {
601       exit(1);
602    }
603    return;
604 }
605
606 /*
607  * Make a quick check to see that we have all the
608  * resources needed.
609  */
610 static int check_resources()
611 {
612    bool OK = true;
613    DIRRES *director;
614
615    LockRes();
616
617    numdir = 0;
618    foreach_res(director, R_DIRECTOR) {
619
620       numdir++;
621       /* tls_require implies tls_enable */
622       if (director->tls_require) {
623          if (have_tls) {
624             director->tls_enable = true;
625          } else {
626             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
627             OK = false;
628             continue;
629          }
630       }
631
632       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
633          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
634                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
635                              " At least one CA certificate store is required.\n"),
636                              director->hdr.name, configfile);
637          OK = false;
638       }
639    }
640    
641    if (numdir == 0) {
642       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
643                           "Without that I don't how to speak to the Director :-(\n"), configfile);
644       OK = false;
645    }
646
647    CONRES *cons;
648    /* Loop over Consoles */
649    foreach_res(cons, R_CONSOLE) {
650       /* tls_require implies tls_enable */
651       if (cons->tls_require) {
652          if (have_tls) {
653             cons->tls_enable = true;
654          } else {
655             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
656             OK = false;
657             continue;
658          }
659       }
660
661       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
662          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
663                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
664                              cons->hdr.name, configfile);
665          OK = false;
666       }
667    }
668
669    UnlockRes();
670
671    return OK;
672 }
673
674
675 #ifdef HAVE_READLINE
676 #define READLINE_LIBRARY 1
677 #undef free
678 #include "readline.h"
679 #include "history.h"
680
681
682 int
683 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
684 {
685    char *line;
686
687    rl_catch_signals = 0;              /* do it ourselves */
688    line = readline((char *)prompt);   /* cast needed for old readlines */
689
690    if (!line) {
691       exit(1);
692    }
693    strip_trailing_junk(line);
694    sock->msglen = pm_strcpy(&sock->msg, line);
695    if (sock->msglen) {
696       add_history(sock->msg);
697    }
698    free(line);
699    return 1;
700 }
701
702 #else /* no readline, do it ourselves */
703
704 static bool bisatty(int fd)
705 {
706    if (no_conio) {
707       return false;
708    }
709    return isatty(fd);
710 }
711
712 /*
713  *   Returns: 1 if data available
714  *            0 if timeout
715  *           -1 if error
716  */
717 static int
718 wait_for_data(int fd, int sec)
719 {
720 #if defined(HAVE_WIN32)
721    return 1;
722 #else
723    fd_set fdset;
724    struct timeval tv;
725
726    tv.tv_sec = sec;
727    tv.tv_usec = 0;
728    for ( ;; ) {
729       FD_ZERO(&fdset);
730       FD_SET((unsigned)fd, &fdset);
731       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
732       case 0:                         /* timeout */
733          return 0;
734       case -1:
735          if (errno == EINTR || errno == EAGAIN) {
736             continue;
737          }
738          return -1;                  /* error return */
739       default:
740          return 1;
741       }
742    }
743 #endif
744 }
745
746 /*
747  * Get next input command from terminal.
748  *
749  *   Returns: 1 if got input
750  *            0 if timeout
751  *           -1 if EOF or error
752  */
753 int
754 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
755 {
756    int len;
757    if (!stop) {
758       if (output == stdout || teeout) {
759          sendit(prompt);
760       }
761    }
762 again:
763    switch (wait_for_data(fileno(input), sec)) {
764    case 0:
765       return 0;                    /* timeout */
766    case -1:
767       return -1;                   /* error */
768    default:
769       len = sizeof_pool_memory(sock->msg) - 1;
770       if (stop) {
771          sleep(1);
772          goto again;
773       }
774 #ifdef HAVE_CONIO
775       if (bisatty(fileno(input))) {
776          input_line(sock->msg, len);
777          break;
778       }
779 #endif
780 #ifdef HAVE_WIN32 /* use special console for input on win32 */
781       if (input == stdin) {
782          if (win32_cgets(sock->msg, len) == NULL) {
783             return -1;
784          }
785       }
786       else
787 #endif
788       if (fgets(sock->msg, len, input) == NULL) {
789          return -1;
790
791       }
792       break;
793    }
794    if (usrbrk()) {
795       clrbrk();
796    }
797    strip_trailing_junk(sock->msg);
798    sock->msglen = strlen(sock->msg);
799    return 1;
800 }
801
802 #endif /* end non-readline code */
803
804 static int versioncmd(FILE *input, BSOCK *UA_sock)
805 {
806    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
807       HOST_OS, DISTNAME, DISTVER);
808    return 1;
809 }
810
811 static int inputcmd(FILE *input, BSOCK *UA_sock)
812 {
813    FILE *fd;
814
815    if (argc > 2) {
816       sendit(_("Too many arguments on input command.\n"));
817       return 1;
818    }
819    if (argc == 1) {
820       sendit(_("First argument to input command must be a filename.\n"));
821       return 1;
822    }
823    fd = fopen(argk[1], "rb");
824    if (!fd) {
825       senditf(_("Cannot open file %s for input. ERR=%s\n"),
826          argk[1], strerror(errno));
827       return 1;
828    }
829    read_and_process_input(fd, UA_sock);
830    fclose(fd);
831    return 1;
832 }
833
834 /* Send output to both termina and specified file */
835 static int teecmd(FILE *input, BSOCK *UA_sock)
836 {
837    teeout = true;
838    return do_outputcmd(input, UA_sock);
839 }
840
841 /* Send output to specified "file" */
842 static int outputcmd(FILE *input, BSOCK *UA_sock)
843 {
844    teeout = false;
845    return do_outputcmd(input, UA_sock);
846 }
847
848
849 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
850 {
851    FILE *fd;
852    const char *mode = "a+b";
853
854    if (argc > 3) {
855       sendit(_("Too many arguments on output/tee command.\n"));
856       return 1;
857    }
858    if (argc == 1) {
859       if (output != stdout) {
860          fclose(output);
861          output = stdout;
862          teeout = false;
863       }
864       return 1;
865    }
866    if (argc == 3) {
867       mode = argk[2];
868    }
869    fd = fopen(argk[1], mode);
870    if (!fd) {
871       senditf(_("Cannot open file %s for output. ERR=%s\n"),
872          argk[1], strerror(errno));
873       return 1;
874    }
875    output = fd;
876    return 1;
877 }
878
879 static int quitcmd(FILE *input, BSOCK *UA_sock)
880 {
881    return 0;
882 }
883
884 static int sleepcmd(FILE *input, BSOCK *UA_sock)
885 {
886    if (argc > 1) {
887       sleep(atoi(argk[1]));
888    }
889    return 1;
890 }
891
892
893 static int timecmd(FILE *input, BSOCK *UA_sock)
894 {
895    char sdt[50];
896    time_t ttime = time(NULL);
897    struct tm tm;
898    (void)localtime_r(&ttime, &tm);
899    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
900    sendit("\n");
901    return 1;
902 }
903
904 /*
905  * Send a line to the output file and or the terminal
906  */
907 void senditf(const char *fmt,...)
908 {
909    char buf[3000];
910    va_list arg_ptr;
911
912    va_start(arg_ptr, fmt);
913    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
914    va_end(arg_ptr);
915    sendit(buf);
916 }
917
918 void sendit(const char *buf)
919 {
920 #ifdef CONIO_FIX
921    char obuf[3000];
922    if (output == stdout || teeout) {
923       const char *p, *q;
924       /*
925        * Here, we convert every \n into \r\n because the
926        *  terminal is in raw mode when we are using
927        *  conio.
928        */
929       for (p=q=buf; (p=strchr(q, '\n')); ) {
930          int len = p - q;
931          if (len > 0) {
932             memcpy(obuf, q, len);
933          }
934          memcpy(obuf+len, "\r\n", 3);
935          q = ++p;                    /* point after \n */
936          fputs(obuf, output);
937       }
938       if (*q) {
939          fputs(q, output);
940       }
941       fflush(output);
942    }
943    if (output != stdout) {
944       fputs(buf, output);
945    }
946 #else
947
948    fputs(buf, output);
949    fflush(output);
950    if (teeout) {
951       fputs(buf, stdout);
952       fflush(stdout);
953    }
954 #endif
955 }