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