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