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