]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Add -n option to bconsole to turn off conio
[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 # ifdef HAVE_MINGW
296    const char *prompt = (const char *)userdata;
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    const char *prompt = (const char *)userdata;
306    char *passwd;
307
308    passwd = getpass(prompt);
309    bstrncpy(buf, passwd, size);
310    return strlen(buf);
311 # endif
312 #else
313    buf[0] = 0;
314    return 0;
315 #endif
316 }
317
318
319 /*********************************************************************
320  *
321  *         Main Bacula Console -- User Interface Program
322  *
323  */
324 int main(int argc, char *argv[])
325 {
326    int ch, i, item;
327    bool no_signals = false;
328    bool test_config = false;
329    JCR jcr;
330
331    setlocale(LC_ALL, "");
332    bindtextdomain("bacula", LOCALEDIR);
333    textdomain("bacula");
334
335    init_stack_dump();
336    my_name_is(argc, argv, "bconsole");
337    init_msg(NULL, NULL);
338    working_directory = "/tmp";
339    args = get_pool_memory(PM_FNAME);
340
341    while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
342       switch (ch) {
343       case 'c':                    /* configuration file */
344          if (configfile != NULL) {
345             free(configfile);
346          }
347          configfile = bstrdup(optarg);
348          break;
349
350       case 'd':
351          debug_level = atoi(optarg);
352          if (debug_level <= 0) {
353             debug_level = 1;
354          }
355          break;
356
357       case 'n':                    /* no conio */
358          no_conio = true;
359          break;
360
361       case 's':                    /* turn off signals */
362          no_signals = true;
363          break;
364
365       case 't':
366          test_config = true;
367          break;
368
369       case '?':
370       default:
371          usage();
372          exit(1);
373       }
374    }
375    argc -= optind;
376    argv += optind;
377
378    if (!no_signals) {
379       init_signals(terminate_console);
380    }
381
382
383 #if !defined(HAVE_WIN32)
384    /* Override Bacula default signals */
385    signal(SIGQUIT, SIG_IGN);
386    signal(SIGTSTP, got_sigstop);
387    signal(SIGCONT, got_sigcontinue);
388    signal(SIGTTIN, got_sigtin);
389    signal(SIGTTOU, got_sigtout);
390    trapctlc();
391 #endif
392
393    OSDependentInit();
394
395    if (argc) {
396       usage();
397       exit(1);
398    }
399
400    if (configfile == NULL) {
401       configfile = bstrdup(CONFIG_FILE);
402    }
403
404    parse_config(configfile);
405
406    if (init_crypto() != 0) {
407       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
408    }
409
410    if (!check_resources()) {
411       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
412    }
413
414    if (!no_conio) {
415       con_init(stdin);
416    }
417
418    if (test_config) {
419       terminate_console(0);
420       exit(0);
421    }
422
423    memset(&jcr, 0, sizeof(jcr));
424
425    (void)WSA_Init();                        /* Initialize Windows sockets */
426
427    if (numdir > 1) {
428       struct sockaddr client_addr;
429       memset(&client_addr, 0, sizeof(client_addr));
430       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
431 try_again:
432       sendit(_("Available Directors:\n"));
433       LockRes();
434       numdir = 0;
435       foreach_res(dir, R_DIRECTOR) {
436          senditf( _("%d  %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
437             dir->DIRport);
438       }
439       UnlockRes();
440       if (get_cmd(stdin, _("Select Director: "), UA_sock, 600) < 0) {
441          (void)WSACleanup();               /* Cleanup Windows sockets */
442          return 1;
443       }
444       item = atoi(UA_sock->msg);
445       if (item < 0 || item > numdir) {
446          senditf(_("You must enter a number between 1 and %d\n"), numdir);
447          goto try_again;
448       }
449       LockRes();
450       dir = NULL;
451       for (i=0; i<item; i++) {
452          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
453       }
454       UnlockRes();
455       term_bsock(UA_sock);
456    } else {
457       LockRes();
458       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
459       UnlockRes();
460    }
461
462    LockRes();
463    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
464    UnlockRes();
465
466    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
467
468    char buf[1024];
469    /* Initialize Console TLS context */
470    if (cons && (cons->tls_enable || cons->tls_require)) {
471       /* Generate passphrase prompt */
472       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
473
474       /* Initialize TLS context:
475        * Args: CA certfile, CA certdir, Certfile, Keyfile,
476        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
477       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
478          cons->tls_ca_certdir, cons->tls_certfile,
479          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
480
481       if (!cons->tls_ctx) {
482          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
483             dir->hdr.name);
484          terminate_console(0);
485          return 1;
486       }
487
488    }
489
490    /* Initialize Director TLS context */
491    if (dir->tls_enable || dir->tls_require) {
492       /* Generate passphrase prompt */
493       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
494
495       /* Initialize TLS context:
496        * Args: CA certfile, CA certdir, Certfile, Keyfile,
497        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
498       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
499          dir->tls_ca_certdir, dir->tls_certfile,
500          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
501
502       if (!dir->tls_ctx) {
503          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
504             dir->hdr.name);
505          terminate_console(0);
506          return 1;
507       }
508    }
509
510    UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
511                           NULL, dir->DIRport, 0);
512    if (UA_sock == NULL) {
513       terminate_console(0);
514       return 1;
515    }
516    jcr.dir_bsock = UA_sock;
517
518    /* If cons==NULL, default console will be used */
519    if (!authenticate_director(&jcr, dir, cons)) {
520       terminate_console(0);
521       return 1;
522    }
523
524    Dmsg0(40, "Opened connection with Director daemon\n");
525
526    sendit(_("Enter a period to cancel a command.\n"));
527
528    /* Run commands in ~/.bconsolerc if any */
529    char *env = getenv("HOME");
530    if (env) {
531       FILE *fd;
532       pm_strcpy(&UA_sock->msg, env);
533       pm_strcat(&UA_sock->msg, "/.bconsolerc");
534       fd = fopen(UA_sock->msg, "rb");
535       if (fd) {
536          read_and_process_input(fd, UA_sock);
537          fclose(fd);
538       }
539    }
540
541    read_and_process_input(stdin, UA_sock);
542
543    if (UA_sock) {
544       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
545       bnet_close(UA_sock);
546    }
547
548    terminate_console(0);
549    return 0;
550 }
551
552
553 /* Cleanup and then exit */
554 static void terminate_console(int sig)
555 {
556
557    static bool already_here = false;
558
559    if (already_here) {                /* avoid recursive temination problems */
560       exit(1);
561    }
562    already_here = true;
563    cleanup_crypto();
564    free_pool_memory(args);
565    if (!no_conio) {
566       con_term();
567    }
568    (void)WSACleanup();               /* Cleanup Windows sockets */
569    if (sig != 0) {
570       exit(1);
571    }
572    return;
573 }
574
575 /*
576  * Make a quick check to see that we have all the
577  * resources needed.
578  */
579 static int check_resources()
580 {
581    bool OK = true;
582    DIRRES *director;
583
584    LockRes();
585
586    numdir = 0;
587    foreach_res(director, R_DIRECTOR) {
588
589       numdir++;
590       /* tls_require implies tls_enable */
591       if (director->tls_require) {
592          if (have_tls) {
593             director->tls_enable = true;
594          } else {
595             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
596             OK = false;
597             continue;
598          }
599       }
600
601       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
602          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
603                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
604                              " At least one CA certificate store is required.\n"),
605                              director->hdr.name, configfile);
606          OK = false;
607       }
608    }
609    
610    if (numdir == 0) {
611       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
612                           "Without that I don't how to speak to the Director :-(\n"), configfile);
613       OK = false;
614    }
615
616    CONRES *cons;
617    /* Loop over Consoles */
618    foreach_res(cons, R_CONSOLE) {
619       /* tls_require implies tls_enable */
620       if (cons->tls_require) {
621          if (have_tls) {
622             cons->tls_enable = true;
623          } else {
624             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
625             OK = false;
626             continue;
627          }
628       }
629
630       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
631          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
632                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
633                              cons->hdr.name, configfile);
634          OK = false;
635       }
636    }
637
638    UnlockRes();
639
640    return OK;
641 }
642
643
644 #ifdef HAVE_READLINE
645 #define READLINE_LIBRARY 1
646 #undef free
647 #include "readline.h"
648 #include "history.h"
649
650
651 int
652 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
653 {
654    char *line;
655
656    rl_catch_signals = 0;              /* do it ourselves */
657    line = readline((char *)prompt);   /* cast needed for old readlines */
658
659    if (!line) {
660       exit(1);
661    }
662    strip_trailing_junk(line);
663    sock->msglen = pm_strcpy(&sock->msg, line);
664    if (sock->msglen) {
665       add_history(sock->msg);
666    }
667    free(line);
668    return 1;
669 }
670
671 #else /* no readline, do it ourselves */
672
673 static bool bisatty(int fd)
674 {
675    if (no_conio) {
676       return false;
677    }
678    return isatty(fd);
679 }
680
681 /*
682  *   Returns: 1 if data available
683  *            0 if timeout
684  *           -1 if error
685  */
686 static int
687 wait_for_data(int fd, int sec)
688 {
689 #if defined(HAVE_WIN32)
690    return 1;
691 #else
692    fd_set fdset;
693    struct timeval tv;
694
695    tv.tv_sec = sec;
696    tv.tv_usec = 0;
697    for ( ;; ) {
698       FD_ZERO(&fdset);
699       FD_SET((unsigned)fd, &fdset);
700       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
701       case 0:                         /* timeout */
702          return 0;
703       case -1:
704          if (errno == EINTR || errno == EAGAIN) {
705             continue;
706          }
707          return -1;                  /* error return */
708       default:
709          return 1;
710       }
711    }
712 #endif
713 }
714
715 /*
716  * Get next input command from terminal.
717  *
718  *   Returns: 1 if got input
719  *            0 if timeout
720  *           -1 if EOF or error
721  */
722 int
723 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
724 {
725    int len;
726    if (!stop) {
727       if (output == stdout || teeout) {
728          sendit(prompt);
729       }
730    }
731 again:
732    switch (wait_for_data(fileno(input), sec)) {
733    case 0:
734       return 0;                    /* timeout */
735    case -1:
736       return -1;                   /* error */
737    default:
738       len = sizeof_pool_memory(sock->msg) - 1;
739       if (stop) {
740          sleep(1);
741          goto again;
742       }
743 #ifdef HAVE_CONIO
744       if (bisatty(fileno(input))) {
745          input_line(sock->msg, len);
746          break;
747       }
748 #endif
749 #ifdef HAVE_WIN32 /* use special console for input on win32 */
750       if (input == stdin) {
751          if (win32_cgets(sock->msg, len) == NULL) {
752             return -1;
753          }
754       }
755       else
756 #endif
757       if (fgets(sock->msg, len, input) == NULL) {
758          return -1;
759
760       }
761       break;
762    }
763    if (usrbrk()) {
764       clrbrk();
765    }
766    strip_trailing_junk(sock->msg);
767    sock->msglen = strlen(sock->msg);
768    return 1;
769 }
770
771 #endif /* end non-readline code */
772
773 static int versioncmd(FILE *input, BSOCK *UA_sock)
774 {
775    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
776       HOST_OS, DISTNAME, DISTVER);
777    return 1;
778 }
779
780 static int inputcmd(FILE *input, BSOCK *UA_sock)
781 {
782    FILE *fd;
783
784    if (argc > 2) {
785       sendit(_("Too many arguments on input command.\n"));
786       return 1;
787    }
788    if (argc == 1) {
789       sendit(_("First argument to input command must be a filename.\n"));
790       return 1;
791    }
792    fd = fopen(argk[1], "rb");
793    if (!fd) {
794       senditf(_("Cannot open file %s for input. ERR=%s\n"),
795          argk[1], strerror(errno));
796       return 1;
797    }
798    read_and_process_input(fd, UA_sock);
799    fclose(fd);
800    return 1;
801 }
802
803 /* Send output to both termina and specified file */
804 static int teecmd(FILE *input, BSOCK *UA_sock)
805 {
806    teeout = true;
807    return do_outputcmd(input, UA_sock);
808 }
809
810 /* Send output to specified "file" */
811 static int outputcmd(FILE *input, BSOCK *UA_sock)
812 {
813    teeout = false;
814    return do_outputcmd(input, UA_sock);
815 }
816
817
818 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
819 {
820    FILE *fd;
821    const char *mode = "a+b";
822
823    if (argc > 3) {
824       sendit(_("Too many arguments on output/tee command.\n"));
825       return 1;
826    }
827    if (argc == 1) {
828       if (output != stdout) {
829          fclose(output);
830          output = stdout;
831          teeout = false;
832       }
833       return 1;
834    }
835    if (argc == 3) {
836       mode = argk[2];
837    }
838    fd = fopen(argk[1], mode);
839    if (!fd) {
840       senditf(_("Cannot open file %s for output. ERR=%s\n"),
841          argk[1], strerror(errno));
842       return 1;
843    }
844    output = fd;
845    return 1;
846 }
847
848 static int quitcmd(FILE *input, BSOCK *UA_sock)
849 {
850    return 0;
851 }
852
853 static int sleepcmd(FILE *input, BSOCK *UA_sock)
854 {
855    if (argc > 1) {
856       sleep(atoi(argk[1]));
857    }
858    return 1;
859 }
860
861
862 static int timecmd(FILE *input, BSOCK *UA_sock)
863 {
864    char sdt[50];
865    time_t ttime = time(NULL);
866    struct tm tm;
867    (void)localtime_r(&ttime, &tm);
868    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
869    sendit("\n");
870    return 1;
871 }
872
873 /*
874  * Send a line to the output file and or the terminal
875  */
876 void senditf(const char *fmt,...)
877 {
878    char buf[3000];
879    va_list arg_ptr;
880
881    va_start(arg_ptr, fmt);
882    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
883    va_end(arg_ptr);
884    sendit(buf);
885 }
886
887 void sendit(const char *buf)
888 {
889 #ifdef CONIO_FIX
890    char obuf[3000];
891    if (output == stdout || teeout) {
892       const char *p, *q;
893       /*
894        * Here, we convert every \n into \r\n because the
895        *  terminal is in raw mode when we are using
896        *  conio.
897        */
898       for (p=q=buf; (p=strchr(q, '\n')); ) {
899          int len = p - q;
900          if (len > 0) {
901             memcpy(obuf, q, len);
902          }
903          memcpy(obuf+len, "\r\n", 3);
904          q = ++p;                    /* point after \n */
905          fputs(obuf, output);
906       }
907       if (*q) {
908          fputs(q, output);
909       }
910       fflush(output);
911    }
912    if (output != stdout) {
913       fputs(buf, output);
914    }
915 #else
916
917    fputs(buf, output);
918    fflush(output);
919    if (teeout) {
920       fputs(buf, stdout);
921       fflush(stdout);
922    }
923 #endif
924 }