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