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