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