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