]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Fix a couple of pedantic compiler warnings.
[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 and included
11    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    utime_t heart_beat;
349
350    setlocale(LC_ALL, "");
351    bindtextdomain("bacula", LOCALEDIR);
352    textdomain("bacula");
353
354    init_stack_dump();
355    my_name_is(argc, argv, "bconsole");
356    init_msg(NULL, NULL);
357    working_directory = "/tmp";
358    args = get_pool_memory(PM_FNAME);
359
360    while ((ch = getopt(argc, argv, "bc:d:nst?")) != -1) {
361       switch (ch) {
362       case 'c':                    /* configuration file */
363          if (configfile != NULL) {
364             free(configfile);
365          }
366          configfile = bstrdup(optarg);
367          break;
368
369       case 'd':
370          debug_level = atoi(optarg);
371          if (debug_level <= 0) {
372             debug_level = 1;
373          }
374          break;
375
376       case 'n':                    /* no conio */
377          no_conio = true;
378          break;
379
380       case 's':                    /* turn off signals */
381          no_signals = true;
382          break;
383
384       case 't':
385          test_config = true;
386          break;
387
388       case '?':
389       default:
390          usage();
391          exit(1);
392       }
393    }
394    argc -= optind;
395    argv += optind;
396
397    if (!no_signals) {
398       init_signals(terminate_console);
399    }
400
401
402 #if !defined(HAVE_WIN32)
403    /* Override Bacula default signals */
404    signal(SIGQUIT, SIG_IGN);
405    signal(SIGTSTP, got_sigstop);
406    signal(SIGCONT, got_sigcontinue);
407    signal(SIGTTIN, got_sigtin);
408    signal(SIGTTOU, got_sigtout);
409    trapctlc();
410 #endif
411
412    OSDependentInit();
413
414    if (argc) {
415       usage();
416       exit(1);
417    }
418
419    if (configfile == NULL) {
420       configfile = bstrdup(CONFIG_FILE);
421    }
422
423    parse_config(configfile);
424
425    if (init_crypto() != 0) {
426       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
427    }
428
429    if (!check_resources()) {
430       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
431    }
432
433    if (!no_conio) {
434       con_init(stdin);
435    }
436
437    if (test_config) {
438       terminate_console(0);
439       exit(0);
440    }
441
442    memset(&jcr, 0, sizeof(jcr));
443
444    (void)WSA_Init();                        /* Initialize Windows sockets */
445
446    LockRes();
447    numdir = 0;
448    foreach_res(dir, R_DIRECTOR) {
449       numdir++;
450    }
451    numcon = 0;
452    foreach_res(cons, R_CONSOLE) {
453       numcon++;
454    }
455    UnlockRes();
456
457    if (numdir > 1) {
458       struct sockaddr client_addr;
459       memset(&client_addr, 0, sizeof(client_addr));
460       UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
461 try_again:
462       sendit(_("Available Directors:\n"));
463       LockRes();
464       numdir = 0;
465       foreach_res(dir, R_DIRECTOR) {
466          senditf( _("%2d:  %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
467             dir->DIRport);
468       }
469       UnlockRes();
470       if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
471          (void)WSACleanup();               /* Cleanup Windows sockets */
472          return 1;
473       }
474       if (!is_a_number(UA_sock->msg)) {
475          senditf(_("%s is not a number. You must enter a number between 1 and %d\n"), 
476                  UA_sock->msg, numdir);
477          goto try_again;
478       }
479       item = atoi(UA_sock->msg);
480       if (item < 0 || item > numdir) {
481          senditf(_("You must enter a number between 1 and %d\n"), numdir);
482          goto try_again;
483       }
484       term_bsock(UA_sock);
485       LockRes();
486       for (i=0; i<item; i++) {
487          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
488       }
489       /* Look for a console linked to this director */
490       for (i=0; i<numcon; i++) {
491          cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
492          if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
493             break;
494          }
495          cons = NULL;
496       }
497       /* Look for the first non-linked console */
498       if (cons == NULL) {
499          for (i=0; i<numcon; i++) {
500             cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
501             if (cons->director == NULL)
502                break;
503             cons = NULL;
504         }
505       }
506       UnlockRes();
507    }
508    /* If no director, take first one */
509    if (!dir) {
510       LockRes();
511       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
512       UnlockRes();
513    }
514    /* If no console, take first one */
515    if (!cons) {
516       LockRes();
517       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
518       UnlockRes();
519    }
520
521    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
522
523    char buf[1024];
524    /* Initialize Console TLS context */
525    if (cons && (cons->tls_enable || cons->tls_require)) {
526       /* Generate passphrase prompt */
527       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
528
529       /* Initialize TLS context:
530        * Args: CA certfile, CA certdir, Certfile, Keyfile,
531        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer   
532        */
533       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
534          cons->tls_ca_certdir, cons->tls_certfile,
535          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
536
537       if (!cons->tls_ctx) {
538          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
539             dir->hdr.name);
540          terminate_console(0);
541          return 1;
542       }
543    }
544
545    /* Initialize Director TLS context */
546    if (dir->tls_enable || dir->tls_require) {
547       /* Generate passphrase prompt */
548       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
549
550       /* Initialize TLS context:
551        * Args: CA certfile, CA certdir, Certfile, Keyfile,
552        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
553       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
554          dir->tls_ca_certdir, dir->tls_certfile,
555          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
556
557       if (!dir->tls_ctx) {
558          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
559             dir->hdr.name);
560          terminate_console(0);
561          return 1;
562       }
563    }
564
565    if (dir->heartbeat_interval) {
566       heart_beat = dir->heartbeat_interval;
567    } else if (cons) {
568       heart_beat = cons->heartbeat_interval;
569    } else {
570       heart_beat = 0;
571    }
572    UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
573                           NULL, dir->DIRport, 0);
574    if (UA_sock == NULL) {
575       terminate_console(0);
576       return 1;
577    }
578    jcr.dir_bsock = UA_sock;
579
580    /* If cons==NULL, default console will be used */
581    if (!authenticate_director(&jcr, dir, cons)) {
582       terminate_console(0);
583       return 1;
584    }
585
586    Dmsg0(40, "Opened connection with Director daemon\n");
587
588    sendit(_("Enter a period to cancel a command.\n"));
589
590    /* Run commands in ~/.bconsolerc if any */
591    char *env = getenv("HOME");
592    if (env) {
593       FILE *fd;
594       pm_strcpy(&UA_sock->msg, env);
595       pm_strcat(&UA_sock->msg, "/.bconsolerc");
596       fd = fopen(UA_sock->msg, "rb");
597       if (fd) {
598          read_and_process_input(fd, UA_sock);
599          fclose(fd);
600       }
601    }
602
603    read_and_process_input(stdin, UA_sock);
604
605    if (UA_sock) {
606       UA_sock->signal(BNET_TERMINATE); /* send EOF */
607       UA_sock->close();
608    }
609
610    terminate_console(0);
611    return 0;
612 }
613
614
615 /* Cleanup and then exit */
616 static void terminate_console(int sig)
617 {
618
619    static bool already_here = false;
620
621    if (already_here) {                /* avoid recursive temination problems */
622       exit(1);
623    }
624    already_here = true;
625    cleanup_crypto();
626    free_pool_memory(args);
627    if (!no_conio) {
628       con_term();
629    }
630    (void)WSACleanup();               /* Cleanup Windows sockets */
631    if (sig != 0) {
632       exit(1);
633    }
634    return;
635 }
636
637 /*
638  * Make a quick check to see that we have all the
639  * resources needed.
640  */
641 static int check_resources()
642 {
643    bool OK = true;
644    DIRRES *director;
645
646    LockRes();
647
648    numdir = 0;
649    foreach_res(director, R_DIRECTOR) {
650
651       numdir++;
652       /* tls_require implies tls_enable */
653       if (director->tls_require) {
654          if (have_tls) {
655             director->tls_enable = true;
656          } else {
657             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
658             OK = false;
659             continue;
660          }
661       }
662
663       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
664          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
665                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
666                              " At least one CA certificate store is required.\n"),
667                              director->hdr.name, configfile);
668          OK = false;
669       }
670    }
671    
672    if (numdir == 0) {
673       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
674                           "Without that I don't how to speak to the Director :-(\n"), configfile);
675       OK = false;
676    }
677
678    CONRES *cons;
679    /* Loop over Consoles */
680    foreach_res(cons, R_CONSOLE) {
681       /* tls_require implies tls_enable */
682       if (cons->tls_require) {
683          if (have_tls) {
684             cons->tls_enable = true;
685          } else {
686             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
687             OK = false;
688             continue;
689          }
690       }
691
692       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
693          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
694                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
695                              cons->hdr.name, configfile);
696          OK = false;
697       }
698    }
699
700    UnlockRes();
701
702    return OK;
703 }
704
705
706 #ifdef HAVE_READLINE
707 #define READLINE_LIBRARY 1
708 #undef free
709 #include "readline.h"
710 #include "history.h"
711
712
713 int
714 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
715 {
716    char *line;
717
718    rl_catch_signals = 0;              /* do it ourselves */
719    line = readline((char *)prompt);   /* cast needed for old readlines */
720
721    if (!line) {
722       exit(1);
723    }
724    strip_trailing_junk(line);
725    sock->msglen = pm_strcpy(&sock->msg, line);
726    if (sock->msglen) {
727       add_history(sock->msg);
728    }
729    free(line);
730    return 1;
731 }
732
733 #else /* no readline, do it ourselves */
734
735 #ifdef HAVE_CONIO
736 static bool bisatty(int fd)
737 {
738    if (no_conio) {
739       return false;
740    }
741    return isatty(fd);
742 }
743 #endif
744
745 /*
746  *   Returns: 1 if data available
747  *            0 if timeout
748  *           -1 if error
749  */
750 static int
751 wait_for_data(int fd, int sec)
752 {
753 #if defined(HAVE_WIN32)
754    return 1;
755 #else
756    fd_set fdset;
757    struct timeval tv;
758
759    tv.tv_sec = sec;
760    tv.tv_usec = 0;
761    for ( ;; ) {
762       FD_ZERO(&fdset);
763       FD_SET((unsigned)fd, &fdset);
764       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
765       case 0:                         /* timeout */
766          return 0;
767       case -1:
768          if (errno == EINTR || errno == EAGAIN) {
769             continue;
770          }
771          return -1;                  /* error return */
772       default:
773          return 1;
774       }
775    }
776 #endif
777 }
778
779 /*
780  * Get next input command from terminal.
781  *
782  *   Returns: 1 if got input
783  *            0 if timeout
784  *           -1 if EOF or error
785  */
786 int
787 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
788 {
789    int len;
790    if (!stop) {
791       if (output == stdout || teeout) {
792          sendit(prompt);
793       }
794    }
795 again:
796    switch (wait_for_data(fileno(input), sec)) {
797    case 0:
798       return 0;                    /* timeout */
799    case -1:
800       return -1;                   /* error */
801    default:
802       len = sizeof_pool_memory(sock->msg) - 1;
803       if (stop) {
804          sleep(1);
805          goto again;
806       }
807 #ifdef HAVE_CONIO
808       if (bisatty(fileno(input))) {
809          input_line(sock->msg, len);
810          break;
811       }
812 #endif
813 #ifdef HAVE_WIN32 /* use special console for input on win32 */
814       if (input == stdin) {
815          if (win32_cgets(sock->msg, len) == NULL) {
816             return -1;
817          }
818       }
819       else
820 #endif
821       if (fgets(sock->msg, len, input) == NULL) {
822          return -1;
823
824       }
825       break;
826    }
827    if (usrbrk()) {
828       clrbrk();
829    }
830    strip_trailing_junk(sock->msg);
831    sock->msglen = strlen(sock->msg);
832    return 1;
833 }
834
835 #endif /* end non-readline code */
836
837 static int versioncmd(FILE *input, BSOCK *UA_sock)
838 {
839    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
840       HOST_OS, DISTNAME, DISTVER);
841    return 1;
842 }
843
844 static int inputcmd(FILE *input, BSOCK *UA_sock)
845 {
846    FILE *fd;
847
848    if (argc > 2) {
849       sendit(_("Too many arguments on input command.\n"));
850       return 1;
851    }
852    if (argc == 1) {
853       sendit(_("First argument to input command must be a filename.\n"));
854       return 1;
855    }
856    fd = fopen(argk[1], "rb");
857    if (!fd) {
858       senditf(_("Cannot open file %s for input. ERR=%s\n"),
859          argk[1], strerror(errno));
860       return 1;
861    }
862    read_and_process_input(fd, UA_sock);
863    fclose(fd);
864    return 1;
865 }
866
867 /* Send output to both termina and specified file */
868 static int teecmd(FILE *input, BSOCK *UA_sock)
869 {
870    teeout = true;
871    return do_outputcmd(input, UA_sock);
872 }
873
874 /* Send output to specified "file" */
875 static int outputcmd(FILE *input, BSOCK *UA_sock)
876 {
877    teeout = false;
878    return do_outputcmd(input, UA_sock);
879 }
880
881
882 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
883 {
884    FILE *fd;
885    const char *mode = "a+b";
886
887    if (argc > 3) {
888       sendit(_("Too many arguments on output/tee command.\n"));
889       return 1;
890    }
891    if (argc == 1) {
892       if (output != stdout) {
893          fclose(output);
894          output = stdout;
895          teeout = false;
896       }
897       return 1;
898    }
899    if (argc == 3) {
900       mode = argk[2];
901    }
902    fd = fopen(argk[1], mode);
903    if (!fd) {
904       berrno be;
905       senditf(_("Cannot open file %s for output. ERR=%s\n"),
906          argk[1], be.bstrerror(errno));
907       return 1;
908    }
909    output = fd;
910    return 1;
911 }
912
913 /*
914  * exec "some-command" [wait-seconds]
915 */
916 static int execcmd(FILE *input, BSOCK *UA_sock)
917 {
918    BPIPE *bpipe;
919    char line[5000];
920    int stat;
921    int wait = 0;
922
923    if (argc > 3) {
924       sendit(_("Too many arguments. Enclose command in double quotes.\n"));
925       return 1;
926    }
927    if (argc == 3) {
928       wait = atoi(argk[2]);
929    }
930    bpipe = open_bpipe(argk[1], wait, "r");
931    if (!bpipe) {
932       berrno be;
933       senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
934          argk[1], be.bstrerror(errno));
935       return 1;
936    }
937   
938    while (fgets(line, sizeof(line), bpipe->rfd)) {
939       senditf("%s", line);
940    }
941    stat = close_bpipe(bpipe);
942    if (stat != 0) {
943       berrno be;
944       be.set_errno(stat);
945      senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
946    }
947    return 1;
948 }
949
950
951 static int echocmd(FILE *input, BSOCK *UA_sock)
952 {
953    for (int i=1; i < argc; i++) {
954       senditf("%s", argk[i]);
955       sendit(" ");
956    }
957    sendit("\n");
958    return 1;
959 }
960
961 static int quitcmd(FILE *input, BSOCK *UA_sock)
962 {
963    return 0;
964 }
965
966 static int sleepcmd(FILE *input, BSOCK *UA_sock)
967 {
968    if (argc > 1) {
969       sleep(atoi(argk[1]));
970    }
971    return 1;
972 }
973
974
975 static int timecmd(FILE *input, BSOCK *UA_sock)
976 {
977    char sdt[50];
978    time_t ttime = time(NULL);
979    struct tm tm;
980    (void)localtime_r(&ttime, &tm);
981    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
982    sendit("\n");
983    return 1;
984 }
985
986 /*
987  * Send a line to the output file and or the terminal
988  */
989 void senditf(const char *fmt,...)
990 {
991    char buf[3000];
992    va_list arg_ptr;
993
994    va_start(arg_ptr, fmt);
995    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
996    va_end(arg_ptr);
997    sendit(buf);
998 }
999
1000 void sendit(const char *buf)
1001 {
1002 #ifdef CONIO_FIX
1003    char obuf[3000];
1004    if (output == stdout || teeout) {
1005       const char *p, *q;
1006       /*
1007        * Here, we convert every \n into \r\n because the
1008        *  terminal is in raw mode when we are using
1009        *  conio.
1010        */
1011       for (p=q=buf; (p=strchr(q, '\n')); ) {
1012          int len = p - q;
1013          if (len > 0) {
1014             memcpy(obuf, q, len);
1015          }
1016          memcpy(obuf+len, "\r\n", 3);
1017          q = ++p;                    /* point after \n */
1018          fputs(obuf, output);
1019       }
1020       if (*q) {
1021          fputs(q, output);
1022       }
1023       fflush(output);
1024    }
1025    if (output != stdout) {
1026       fputs(buf, output);
1027    }
1028 #else
1029
1030    fputs(buf, output);
1031    fflush(output);
1032    if (teeout) {
1033       fputs(buf, stdout);
1034       fflush(stdout);
1035    }
1036 #endif
1037 }