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