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