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