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