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