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