]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
kes Extend new GUI API.
[bacula/bacula] / bacula / src / console / console.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Console interface to the Director
31  *
32  *     Kern Sibbald, September MM
33  *
34  *     Version $Id$
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        */
525       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
526          cons->tls_ca_certdir, cons->tls_certfile,
527          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
528
529       if (!cons->tls_ctx) {
530          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
531             dir->hdr.name);
532          terminate_console(0);
533          return 1;
534       }
535    }
536
537    /* Initialize Director TLS context */
538    if (dir->tls_enable || dir->tls_require) {
539       /* Generate passphrase prompt */
540       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
541
542       /* Initialize TLS context:
543        * Args: CA certfile, CA certdir, Certfile, Keyfile,
544        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
545       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
546          dir->tls_ca_certdir, dir->tls_certfile,
547          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
548
549       if (!dir->tls_ctx) {
550          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
551             dir->hdr.name);
552          terminate_console(0);
553          return 1;
554       }
555    }
556
557    UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
558                           NULL, dir->DIRport, 0);
559    if (UA_sock == NULL) {
560       terminate_console(0);
561       return 1;
562    }
563    jcr.dir_bsock = UA_sock;
564
565    /* If cons==NULL, default console will be used */
566    if (!authenticate_director(&jcr, dir, cons)) {
567       terminate_console(0);
568       return 1;
569    }
570
571    Dmsg0(40, "Opened connection with Director daemon\n");
572
573    sendit(_("Enter a period to cancel a command.\n"));
574
575    /* Run commands in ~/.bconsolerc if any */
576    char *env = getenv("HOME");
577    if (env) {
578       FILE *fd;
579       pm_strcpy(&UA_sock->msg, env);
580       pm_strcat(&UA_sock->msg, "/.bconsolerc");
581       fd = fopen(UA_sock->msg, "rb");
582       if (fd) {
583          read_and_process_input(fd, UA_sock);
584          fclose(fd);
585       }
586    }
587
588    read_and_process_input(stdin, UA_sock);
589
590    if (UA_sock) {
591       UA_sock->signal(BNET_TERMINATE); /* send EOF */
592       UA_sock->close();
593    }
594
595    terminate_console(0);
596    return 0;
597 }
598
599
600 /* Cleanup and then exit */
601 static void terminate_console(int sig)
602 {
603
604    static bool already_here = false;
605
606    if (already_here) {                /* avoid recursive temination problems */
607       exit(1);
608    }
609    already_here = true;
610    cleanup_crypto();
611    free_pool_memory(args);
612    if (!no_conio) {
613       con_term();
614    }
615    (void)WSACleanup();               /* Cleanup Windows sockets */
616    if (sig != 0) {
617       exit(1);
618    }
619    return;
620 }
621
622 /*
623  * Make a quick check to see that we have all the
624  * resources needed.
625  */
626 static int check_resources()
627 {
628    bool OK = true;
629    DIRRES *director;
630
631    LockRes();
632
633    numdir = 0;
634    foreach_res(director, R_DIRECTOR) {
635
636       numdir++;
637       /* tls_require implies tls_enable */
638       if (director->tls_require) {
639          if (have_tls) {
640             director->tls_enable = true;
641          } else {
642             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
643             OK = false;
644             continue;
645          }
646       }
647
648       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
649          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
650                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
651                              " At least one CA certificate store is required.\n"),
652                              director->hdr.name, configfile);
653          OK = false;
654       }
655    }
656    
657    if (numdir == 0) {
658       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
659                           "Without that I don't how to speak to the Director :-(\n"), configfile);
660       OK = false;
661    }
662
663    CONRES *cons;
664    /* Loop over Consoles */
665    foreach_res(cons, R_CONSOLE) {
666       /* tls_require implies tls_enable */
667       if (cons->tls_require) {
668          if (have_tls) {
669             cons->tls_enable = true;
670          } else {
671             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
672             OK = false;
673             continue;
674          }
675       }
676
677       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
678          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
679                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
680                              cons->hdr.name, configfile);
681          OK = false;
682       }
683    }
684
685    UnlockRes();
686
687    return OK;
688 }
689
690
691 #ifdef HAVE_READLINE
692 #define READLINE_LIBRARY 1
693 #undef free
694 #include "readline.h"
695 #include "history.h"
696
697
698 int
699 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
700 {
701    char *line;
702
703    rl_catch_signals = 0;              /* do it ourselves */
704    line = readline((char *)prompt);   /* cast needed for old readlines */
705
706    if (!line) {
707       exit(1);
708    }
709    strip_trailing_junk(line);
710    sock->msglen = pm_strcpy(&sock->msg, line);
711    if (sock->msglen) {
712       add_history(sock->msg);
713    }
714    free(line);
715    return 1;
716 }
717
718 #else /* no readline, do it ourselves */
719
720 #if !defined(HAVE_WIN32)
721 static bool bisatty(int fd)
722 {
723    if (no_conio) {
724       return false;
725    }
726    return isatty(fd);
727 }
728 #endif
729
730 /*
731  *   Returns: 1 if data available
732  *            0 if timeout
733  *           -1 if error
734  */
735 static int
736 wait_for_data(int fd, int sec)
737 {
738 #if defined(HAVE_WIN32)
739    return 1;
740 #else
741    fd_set fdset;
742    struct timeval tv;
743
744    tv.tv_sec = sec;
745    tv.tv_usec = 0;
746    for ( ;; ) {
747       FD_ZERO(&fdset);
748       FD_SET((unsigned)fd, &fdset);
749       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
750       case 0:                         /* timeout */
751          return 0;
752       case -1:
753          if (errno == EINTR || errno == EAGAIN) {
754             continue;
755          }
756          return -1;                  /* error return */
757       default:
758          return 1;
759       }
760    }
761 #endif
762 }
763
764 /*
765  * Get next input command from terminal.
766  *
767  *   Returns: 1 if got input
768  *            0 if timeout
769  *           -1 if EOF or error
770  */
771 int
772 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
773 {
774    int len;
775    if (!stop) {
776       if (output == stdout || teeout) {
777          sendit(prompt);
778       }
779    }
780 again:
781    switch (wait_for_data(fileno(input), sec)) {
782    case 0:
783       return 0;                    /* timeout */
784    case -1:
785       return -1;                   /* error */
786    default:
787       len = sizeof_pool_memory(sock->msg) - 1;
788       if (stop) {
789          sleep(1);
790          goto again;
791       }
792 #ifdef HAVE_CONIO
793       if (bisatty(fileno(input))) {
794          input_line(sock->msg, len);
795          break;
796       }
797 #endif
798 #ifdef HAVE_WIN32 /* use special console for input on win32 */
799       if (input == stdin) {
800          if (win32_cgets(sock->msg, len) == NULL) {
801             return -1;
802          }
803       }
804       else
805 #endif
806       if (fgets(sock->msg, len, input) == NULL) {
807          return -1;
808
809       }
810       break;
811    }
812    if (usrbrk()) {
813       clrbrk();
814    }
815    strip_trailing_junk(sock->msg);
816    sock->msglen = strlen(sock->msg);
817    return 1;
818 }
819
820 #endif /* end non-readline code */
821
822 static int versioncmd(FILE *input, BSOCK *UA_sock)
823 {
824    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
825       HOST_OS, DISTNAME, DISTVER);
826    return 1;
827 }
828
829 static int inputcmd(FILE *input, BSOCK *UA_sock)
830 {
831    FILE *fd;
832
833    if (argc > 2) {
834       sendit(_("Too many arguments on input command.\n"));
835       return 1;
836    }
837    if (argc == 1) {
838       sendit(_("First argument to input command must be a filename.\n"));
839       return 1;
840    }
841    fd = fopen(argk[1], "rb");
842    if (!fd) {
843       senditf(_("Cannot open file %s for input. ERR=%s\n"),
844          argk[1], strerror(errno));
845       return 1;
846    }
847    read_and_process_input(fd, UA_sock);
848    fclose(fd);
849    return 1;
850 }
851
852 /* Send output to both termina and specified file */
853 static int teecmd(FILE *input, BSOCK *UA_sock)
854 {
855    teeout = true;
856    return do_outputcmd(input, UA_sock);
857 }
858
859 /* Send output to specified "file" */
860 static int outputcmd(FILE *input, BSOCK *UA_sock)
861 {
862    teeout = false;
863    return do_outputcmd(input, UA_sock);
864 }
865
866
867 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
868 {
869    FILE *fd;
870    const char *mode = "a+b";
871
872    if (argc > 3) {
873       sendit(_("Too many arguments on output/tee command.\n"));
874       return 1;
875    }
876    if (argc == 1) {
877       if (output != stdout) {
878          fclose(output);
879          output = stdout;
880          teeout = false;
881       }
882       return 1;
883    }
884    if (argc == 3) {
885       mode = argk[2];
886    }
887    fd = fopen(argk[1], mode);
888    if (!fd) {
889       senditf(_("Cannot open file %s for output. ERR=%s\n"),
890          argk[1], strerror(errno));
891       return 1;
892    }
893    output = fd;
894    return 1;
895 }
896
897 static int echocmd(FILE *intut, BSOCK *UA_sock)
898 {
899    for (int i=1; i < argc; i++) {
900       senditf("%s", argk[i]);
901       sendit(" ");
902    }
903    sendit("\n");
904    return 1;
905 }
906
907 static int quitcmd(FILE *input, BSOCK *UA_sock)
908 {
909    return 0;
910 }
911
912 static int sleepcmd(FILE *input, BSOCK *UA_sock)
913 {
914    if (argc > 1) {
915       sleep(atoi(argk[1]));
916    }
917    return 1;
918 }
919
920
921 static int timecmd(FILE *input, BSOCK *UA_sock)
922 {
923    char sdt[50];
924    time_t ttime = time(NULL);
925    struct tm tm;
926    (void)localtime_r(&ttime, &tm);
927    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
928    sendit("\n");
929    return 1;
930 }
931
932 /*
933  * Send a line to the output file and or the terminal
934  */
935 void senditf(const char *fmt,...)
936 {
937    char buf[3000];
938    va_list arg_ptr;
939
940    va_start(arg_ptr, fmt);
941    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
942    va_end(arg_ptr);
943    sendit(buf);
944 }
945
946 void sendit(const char *buf)
947 {
948 #ifdef CONIO_FIX
949    char obuf[3000];
950    if (output == stdout || teeout) {
951       const char *p, *q;
952       /*
953        * Here, we convert every \n into \r\n because the
954        *  terminal is in raw mode when we are using
955        *  conio.
956        */
957       for (p=q=buf; (p=strchr(q, '\n')); ) {
958          int len = p - q;
959          if (len > 0) {
960             memcpy(obuf, q, len);
961          }
962          memcpy(obuf+len, "\r\n", 3);
963          q = ++p;                    /* point after \n */
964          fputs(obuf, output);
965       }
966       if (*q) {
967          fputs(q, output);
968       }
969       fflush(output);
970    }
971    if (output != stdout) {
972       fputs(buf, output);
973    }
974 #else
975
976    fputs(buf, output);
977    fflush(output);
978    if (teeout) {
979       fputs(buf, stdout);
980       fflush(stdout);
981    }
982 #endif
983 }