]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Update copyright
[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 static bool bisatty(int fd)
718 {
719    if (no_conio) {
720       return false;
721    }
722    return isatty(fd);
723 }
724
725 /*
726  *   Returns: 1 if data available
727  *            0 if timeout
728  *           -1 if error
729  */
730 static int
731 wait_for_data(int fd, int sec)
732 {
733 #if defined(HAVE_WIN32)
734    return 1;
735 #else
736    fd_set fdset;
737    struct timeval tv;
738
739    tv.tv_sec = sec;
740    tv.tv_usec = 0;
741    for ( ;; ) {
742       FD_ZERO(&fdset);
743       FD_SET((unsigned)fd, &fdset);
744       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
745       case 0:                         /* timeout */
746          return 0;
747       case -1:
748          if (errno == EINTR || errno == EAGAIN) {
749             continue;
750          }
751          return -1;                  /* error return */
752       default:
753          return 1;
754       }
755    }
756 #endif
757 }
758
759 /*
760  * Get next input command from terminal.
761  *
762  *   Returns: 1 if got input
763  *            0 if timeout
764  *           -1 if EOF or error
765  */
766 int
767 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
768 {
769    int len;
770    if (!stop) {
771       if (output == stdout || teeout) {
772          sendit(prompt);
773       }
774    }
775 again:
776    switch (wait_for_data(fileno(input), sec)) {
777    case 0:
778       return 0;                    /* timeout */
779    case -1:
780       return -1;                   /* error */
781    default:
782       len = sizeof_pool_memory(sock->msg) - 1;
783       if (stop) {
784          sleep(1);
785          goto again;
786       }
787 #ifdef HAVE_CONIO
788       if (bisatty(fileno(input))) {
789          input_line(sock->msg, len);
790          break;
791       }
792 #endif
793 #ifdef HAVE_WIN32 /* use special console for input on win32 */
794       if (input == stdin) {
795          if (win32_cgets(sock->msg, len) == NULL) {
796             return -1;
797          }
798       }
799       else
800 #endif
801       if (fgets(sock->msg, len, input) == NULL) {
802          return -1;
803
804       }
805       break;
806    }
807    if (usrbrk()) {
808       clrbrk();
809    }
810    strip_trailing_junk(sock->msg);
811    sock->msglen = strlen(sock->msg);
812    return 1;
813 }
814
815 #endif /* end non-readline code */
816
817 static int versioncmd(FILE *input, BSOCK *UA_sock)
818 {
819    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
820       HOST_OS, DISTNAME, DISTVER);
821    return 1;
822 }
823
824 static int inputcmd(FILE *input, BSOCK *UA_sock)
825 {
826    FILE *fd;
827
828    if (argc > 2) {
829       sendit(_("Too many arguments on input command.\n"));
830       return 1;
831    }
832    if (argc == 1) {
833       sendit(_("First argument to input command must be a filename.\n"));
834       return 1;
835    }
836    fd = fopen(argk[1], "rb");
837    if (!fd) {
838       senditf(_("Cannot open file %s for input. ERR=%s\n"),
839          argk[1], strerror(errno));
840       return 1;
841    }
842    read_and_process_input(fd, UA_sock);
843    fclose(fd);
844    return 1;
845 }
846
847 /* Send output to both termina and specified file */
848 static int teecmd(FILE *input, BSOCK *UA_sock)
849 {
850    teeout = true;
851    return do_outputcmd(input, UA_sock);
852 }
853
854 /* Send output to specified "file" */
855 static int outputcmd(FILE *input, BSOCK *UA_sock)
856 {
857    teeout = false;
858    return do_outputcmd(input, UA_sock);
859 }
860
861
862 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
863 {
864    FILE *fd;
865    const char *mode = "a+b";
866
867    if (argc > 3) {
868       sendit(_("Too many arguments on output/tee command.\n"));
869       return 1;
870    }
871    if (argc == 1) {
872       if (output != stdout) {
873          fclose(output);
874          output = stdout;
875          teeout = false;
876       }
877       return 1;
878    }
879    if (argc == 3) {
880       mode = argk[2];
881    }
882    fd = fopen(argk[1], mode);
883    if (!fd) {
884       senditf(_("Cannot open file %s for output. ERR=%s\n"),
885          argk[1], strerror(errno));
886       return 1;
887    }
888    output = fd;
889    return 1;
890 }
891
892 static int quitcmd(FILE *input, BSOCK *UA_sock)
893 {
894    return 0;
895 }
896
897 static int sleepcmd(FILE *input, BSOCK *UA_sock)
898 {
899    if (argc > 1) {
900       sleep(atoi(argk[1]));
901    }
902    return 1;
903 }
904
905
906 static int timecmd(FILE *input, BSOCK *UA_sock)
907 {
908    char sdt[50];
909    time_t ttime = time(NULL);
910    struct tm tm;
911    (void)localtime_r(&ttime, &tm);
912    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
913    sendit("\n");
914    return 1;
915 }
916
917 /*
918  * Send a line to the output file and or the terminal
919  */
920 void senditf(const char *fmt,...)
921 {
922    char buf[3000];
923    va_list arg_ptr;
924
925    va_start(arg_ptr, fmt);
926    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
927    va_end(arg_ptr);
928    sendit(buf);
929 }
930
931 void sendit(const char *buf)
932 {
933 #ifdef CONIO_FIX
934    char obuf[3000];
935    if (output == stdout || teeout) {
936       const char *p, *q;
937       /*
938        * Here, we convert every \n into \r\n because the
939        *  terminal is in raw mode when we are using
940        *  conio.
941        */
942       for (p=q=buf; (p=strchr(q, '\n')); ) {
943          int len = p - q;
944          if (len > 0) {
945             memcpy(obuf, q, len);
946          }
947          memcpy(obuf+len, "\r\n", 3);
948          q = ++p;                    /* point after \n */
949          fputs(obuf, output);
950       }
951       if (*q) {
952          fputs(q, output);
953       }
954       fflush(output);
955    }
956    if (output != stdout) {
957       fputs(buf, output);
958    }
959 #else
960
961    fputs(buf, output);
962    fflush(output);
963    if (teeout) {
964       fputs(buf, stdout);
965       fflush(stdout);
966    }
967 #endif
968 }