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