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