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