]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/console/console.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / console / console.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2018 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *   Bacula Console interface to the Director
22  *
23  *     Kern Sibbald, September MM
24  *
25  */
26
27 #include "bacula.h"
28 #include "console_conf.h"
29 #include "jcr.h"
30
31
32 #if defined(HAVE_CONIO)
33 #include "conio.h"
34 //#define CONIO_FIX 1
35 #else /* defined(HAVE_READLINE) || "DUMB" */
36 #define con_init(x)
37 #define con_term()
38 #define con_set_zed_keys();
39 #endif
40
41 void trapctlc();
42 void clrbrk();
43 int usrbrk();
44 static int brkflg = 0;              /* set on user break */
45
46 #if defined(HAVE_WIN32)
47 #define isatty(fd) (fd==0)
48 #endif
49
50 /* Exported variables */
51
52 //extern int rl_catch_signals;
53
54 /* Imported functions */
55 int authenticate_director(BSOCK *dir, DIRRES *director, CONRES *cons);
56
57 /* Forward referenced functions */
58 static void terminate_console(int sig);
59 static int check_resources();
60 int get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec);
61 static int do_outputcmd(FILE *input, BSOCK *UA_sock);
62 void senditf(const char *fmt, ...);
63 void sendit(const char *buf);
64
65 extern "C" void got_sigstop(int sig);
66 extern "C" void got_sigcontinue(int sig);
67 extern "C" void got_sigtout(int sig);
68 extern "C" void got_sigtin(int sig);
69
70
71 /* Static variables */
72 static char *configfile = NULL;
73 static BSOCK *UA_sock = NULL;
74 static DIRRES *dir = NULL;
75 static CONRES *cons = NULL;
76 static FILE *output = stdout;
77 static bool teeout = false;               /* output to output and stdout */
78 static bool teein = false;                /* input to output and stdout */
79 static bool stop = false;
80 static bool no_conio = false;
81 static int timeout = 0;
82 static int argc;
83 static int numdir;
84 static POOLMEM *args;
85 static char *argk[MAX_CMD_ARGS];
86 static char *argv[MAX_CMD_ARGS];
87 static CONFIG *config;
88
89
90 /* Command prototypes */
91 static int versioncmd(FILE *input, BSOCK *UA_sock);
92 static int inputcmd(FILE *input, BSOCK *UA_sock);
93 static int outputcmd(FILE *input, BSOCK *UA_sock);
94 static int teecmd(FILE *input, BSOCK *UA_sock);
95 static int teeallcmd(FILE *input, BSOCK *UA_sock);
96 static int quitcmd(FILE *input, BSOCK *UA_sock);
97 static int helpcmd(FILE *input, BSOCK *UA_sock);
98 static int echocmd(FILE *input, BSOCK *UA_sock);
99 static int timecmd(FILE *input, BSOCK *UA_sock);
100 static int sleepcmd(FILE *input, BSOCK *UA_sock);
101 static int execcmd(FILE *input, BSOCK *UA_sock);
102 static int putfilecmd(FILE *input, BSOCK *UA_sock);
103
104 #ifdef HAVE_READLINE
105 static int eolcmd(FILE *input, BSOCK *UA_sock);
106
107 # ifndef HAVE_REGEX_H
108 #  include "lib/bregex.h"
109 # else
110 #  include <regex.h>
111 # endif
112
113 #endif
114
115
116 #define CONFIG_FILE "bconsole.conf"   /* default configuration file */
117
118 static void usage()
119 {
120    fprintf(stderr, _(
121 PROG_COPYRIGHT
122 "\n%sVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
123 "Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
124 "       -D <dir>    select a Director\n"
125 "       -l          list Directors defined\n"
126 "       -L          list Consoles defined\n"
127 "       -C <cons>   select a console\n"
128 "       -c <file>   set configuration file to file\n"
129 "       -d <nn>     set debug level to <nn>\n"
130 "       -dt         print timestamp in debug output\n"
131 "       -n          no conio\n"
132 "       -s          no signals\n"
133 "       -u <nn>     set command execution timeout to <nn> seconds\n"
134 "       -t          test - read configuration and exit\n"
135 "       -?          print this message.\n"
136 "\n"), 2000, BDEMO, HOST_OS, DISTNAME, DISTVER);
137 }
138
139
140 extern "C"
141 void got_sigstop(int sig)
142 {
143    stop = true;
144 }
145
146 extern "C"
147 void got_sigcontinue(int sig)
148 {
149    stop = false;
150 }
151
152 extern "C"
153 void got_sigtout(int sig)
154 {
155 // printf("Got tout\n");
156 }
157
158 extern "C"
159 void got_sigtin(int sig)
160 {
161 // printf("Got tin\n");
162 }
163
164
165 static int zed_keyscmd(FILE *input, BSOCK *UA_sock)
166 {
167    con_set_zed_keys();
168    return 1;
169 }
170
171 /*
172  * These are the @command
173  */
174 struct cmdstruct { const char *key; int (*func)(FILE *input, BSOCK *UA_sock); const char *help; };
175 static struct cmdstruct commands[] = {
176  { N_("input"),      inputcmd,     _("input from file")},
177  { N_("output"),     outputcmd,    _("output to file")},
178  { N_("quit"),       quitcmd,      _("quit")},
179  { N_("tee"),        teecmd,       _("output to file and terminal")},
180  { N_("tall"),       teeallcmd,    _("output everything to file and terminal (tee all)")},
181  { N_("sleep"),      sleepcmd,     _("sleep specified time")},
182  { N_("time"),       timecmd,      _("print current time")},
183  { N_("version"),    versioncmd,   _("print Console's version")},
184  { N_("echo"),       echocmd,      _("echo command string")},
185  { N_("exec"),       execcmd,      _("execute an external command")},
186  { N_("exit"),       quitcmd,      _("exit = quit")},
187  { N_("putfile"),    putfilecmd,   _("send a file to the director")},
188  { N_("zed_keys"),   zed_keyscmd,  _("zed_keys = use zed keys instead of bash keys")},
189  { N_("help"),       helpcmd,      _("help listing")},
190 #ifdef HAVE_READLINE
191  { N_("separator"),  eolcmd,       _("set command separator")},
192 #endif
193              };
194 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
195
196 static int do_a_command(FILE *input, BSOCK *UA_sock)
197 {
198    unsigned int i;
199    int stat;
200    int found;
201    int len;
202    char *cmd;
203
204    found = 0;
205    stat = 1;
206
207    Dmsg1(120, "Command: %s\n", UA_sock->msg);
208    if (argc == 0) {
209       return 1;
210    }
211
212    cmd = argk[0]+1;
213    if (*cmd == '#') {                 /* comment */
214       return 1;
215    }
216    len = strlen(cmd);
217    for (i=0; i<comsize; i++) {     /* search for command */
218       if (strncasecmp(cmd,  _(commands[i].key), len) == 0) {
219          stat = (*commands[i].func)(input, UA_sock);   /* go execute command */
220          found = 1;
221          break;
222       }
223    }
224    if (!found) {
225       pm_strcat(&UA_sock->msg, _(": is an invalid command\n"));
226       UA_sock->msglen = strlen(UA_sock->msg);
227       sendit(UA_sock->msg);
228    }
229    return stat;
230 }
231
232 /* When getting .api command, we can ignore some signals, so we set
233  * api_mode=true
234  */
235 static bool api_mode=false;
236
237 static bool ignore_signal(int stat, BSOCK *s)
238 {
239    /* Not in API mode */
240    if (!api_mode) {
241       return false;
242    }
243
244    /* not a signal */
245    if (stat != -1) {
246       return false;
247    }
248
249    /* List signal that should not stop the read loop */
250    Dmsg1(100, "Got signal %s\n", bnet_sig_to_ascii(s->msglen));
251    switch(s->msglen) {
252    case BNET_CMD_BEGIN:
253    case BNET_CMD_FAILED:        /* might want to print **ERROR** */
254    case BNET_CMD_OK:            /* might want to print **OK** */
255    case BNET_MSGS_PENDING:
256       return true;
257    default:
258       break;
259    }
260
261    /* The signal should break the read loop */
262    return false;
263 }
264
265 static void read_and_process_input(FILE *input, BSOCK *UA_sock)
266 {
267    const char *prompt = "*";
268    bool at_prompt = false;
269    int tty_input = isatty(fileno(input));
270    int stat;
271    btimer_t *tid=NULL;
272
273    for ( ;; ) {
274       if (at_prompt) {                /* don't prompt multiple times */
275          prompt = "";
276       } else {
277          prompt = "*";
278          at_prompt = true;
279       }
280       if (tty_input) {
281          stat = get_cmd(input, prompt, UA_sock, 30);
282          if (usrbrk() >= 1) {
283             clrbrk();
284          }
285          if (usrbrk()) {
286             break;
287          }
288       } else {
289          /* Reading input from a file */
290          if (usrbrk()) {
291             break;
292          }
293          if (bfgets(UA_sock->msg, input) == NULL) {
294             stat = -1;
295          } else {
296             sendit(UA_sock->msg);  /* echo to terminal */
297             strip_trailing_junk(UA_sock->msg);
298             UA_sock->msglen = strlen(UA_sock->msg);
299             stat = 1;
300          }
301       }
302       if (stat < 0) {
303          break;                       /* error or interrupt */
304       } else if (stat == 0) {         /* timeout */
305          if (strcmp(prompt, "*") == 0) {
306             tid = start_bsock_timer(UA_sock, timeout);
307             UA_sock->fsend(".messages");
308             stop_bsock_timer(tid);
309          } else {
310             continue;
311          }
312       } else {
313          at_prompt = false;
314          /* @ => internal command for us */
315          if (UA_sock->msg[0] == '@') {
316             parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
317             if (!do_a_command(input, UA_sock)) {
318                break;
319             }
320             continue;
321          }
322          tid = start_bsock_timer(UA_sock, timeout);
323          if (!UA_sock->send()) {   /* send command */
324             stop_bsock_timer(tid);
325             break;                    /* error */
326          }
327          stop_bsock_timer(tid);
328       }
329       if (strncasecmp(UA_sock->msg, ".api", 4) == 0) {
330          api_mode = true;
331       }
332       if (strcasecmp(UA_sock->msg, ".quit") == 0 || strcasecmp(UA_sock->msg, ".exit") == 0) {
333          break;
334       }
335       tid = start_bsock_timer(UA_sock, timeout);
336       while (1) {
337          stat = UA_sock->recv();
338          if (ignore_signal(stat, UA_sock)) {
339             continue;
340          }
341
342          if (stat < 0) {
343             break;
344          }
345
346          if (at_prompt) {
347             if (!stop) {
348                sendit("\n");
349             }
350             at_prompt = false;
351          }
352          /* Suppress output if running in background or user hit ctl-c */
353          if (!stop && !usrbrk()) {
354             sendit(UA_sock->msg);
355          }
356       }
357       stop_bsock_timer(tid);
358       if (usrbrk() > 1) {
359          break;
360       } else {
361          clrbrk();
362       }
363       if (!stop) {
364          fflush(stdout);
365       }
366       if (UA_sock->is_stop()) {
367          break;                       /* error or term */
368       } else if (stat == BNET_SIGNAL) {
369          if (UA_sock->msglen == BNET_SUB_PROMPT) {
370             at_prompt = true;
371          }
372          Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock->msglen));
373       }
374    }
375 }
376
377 /*
378  * Call-back for reading a passphrase for an encrypted PEM file
379  * This function uses getpass(),
380  *  which uses a static buffer and is NOT thread-safe.
381  */
382 static int tls_pem_callback(char *buf, int size, const void *userdata)
383 {
384 #ifdef HAVE_TLS
385    const char *prompt = (const char *)userdata;
386 # if defined(HAVE_WIN32)
387    sendit(prompt);
388    if (win32_cgets(buf, size) == NULL) {
389       buf[0] = 0;
390       return 0;
391    } else {
392       return strlen(buf);
393    }
394 # else
395    char *passwd;
396
397    passwd = getpass(prompt);
398    bstrncpy(buf, passwd, size);
399    return strlen(buf);
400 # endif
401 #else
402    buf[0] = 0;
403    return 0;
404 #endif
405 }
406
407 #ifdef HAVE_READLINE
408 #define READLINE_LIBRARY 1
409 #include "readline.h"
410 #include "history.h"
411
412 /* Get the first keyword of the line */
413 static char *
414 get_first_keyword()
415 {
416    char *ret=NULL;
417    int len;
418    char *first_space = strchr(rl_line_buffer, ' ');
419    if (first_space) {
420       len = first_space - rl_line_buffer;
421       ret = (char *) malloc((len + 1) * sizeof(char));
422       memcpy(ret, rl_line_buffer, len);
423       ret[len]=0;
424    }
425    return ret;
426 }
427
428 /*
429  * Return the command before the current point.
430  * Set nb to the number of command to skip
431  */
432 static char *
433 get_previous_keyword(int current_point, int nb)
434 {
435    int i, end=-1, start, inquotes=0;
436    char *s=NULL;
437
438    while (nb-- >= 0) {
439       /* first we look for a space before the current word */
440       for (i = current_point; i >= 0; i--) {
441          if (rl_line_buffer[i] == ' ' || rl_line_buffer[i] == '=') {
442             break;
443          }
444       }
445
446       /* find the end of the command */
447       for (; i >= 0; i--) {
448          if (rl_line_buffer[i] != ' ') {
449             end = i;
450             break;
451          }
452       }
453
454       /* no end of string */
455       if (end == -1) {
456          return NULL;
457       }
458
459       /* look for the start of the command */
460       for (start = end; start > 0; start--) {
461          if (rl_line_buffer[start] == '"') {
462             inquotes = !inquotes;
463          }
464          if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0) {
465             break;
466          }
467          current_point = start;
468       }
469    }
470
471    s = (char *)malloc(end - start + 2);
472    memcpy(s, rl_line_buffer + start, end - start + 1);
473    s[end - start + 1] = 0;
474
475    //  printf("=======> %i:%i <%s>\n", start, end, s);
476
477    return s;
478 }
479
480 /* Simple structure that will contain the completion list */
481 struct ItemList {
482    alist list;
483 };
484
485 static ItemList *items = NULL;
486 void init_items()
487 {
488    if (!items) {
489       items = (ItemList*) malloc(sizeof(ItemList));
490       memset(items, 0, sizeof(ItemList));
491
492    } else {
493       items->list.destroy();
494    }
495
496    items->list.init();
497 }
498
499 /* Match a regexp and add the result to the items list
500  * This function is recursive
501  */
502 static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf)
503 {
504    int rc, size;
505    int nmatch=20;
506    regmatch_t pmatch[nmatch];
507
508    if (len <= 0) {
509       return;
510    }
511    rc = regexec(preg, what, nmatch, pmatch,  0);
512    if (rc == 0) {
513 #if 0
514       Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n        10         20         30\n", what);
515       Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo);
516       Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo);
517       Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo);
518       Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo);
519 #endif
520       size = pmatch[1].rm_eo - pmatch[1].rm_so;
521       *buf = check_pool_memory_size(*buf, size + 1);
522       memcpy(*buf, what+pmatch[1].rm_so, size);
523       (*buf)[size] = 0;
524
525       items->list.append(bstrdup(*buf));
526       /* We search for the next keyword in the line */
527       match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf);
528    }
529 }
530
531 /* fill the items list with the output of the help command */
532 void get_arguments(const char *what)
533 {
534    regex_t preg;
535    POOLMEM *buf;
536    int rc;
537    init_items();
538
539    rc = regcomp(&preg, "(([a-z]+=)|([a-z]+)( |$))", REG_EXTENDED);
540    if (rc != 0) {
541       return;
542    }
543
544    buf = get_pool_memory(PM_MESSAGE);
545    UA_sock->fsend(".help item=%s", what);
546    while (UA_sock->recv() > 0) {
547       strip_trailing_junk(UA_sock->msg);
548       match_kw(&preg, UA_sock->msg, UA_sock->msglen, &buf);
549    }
550    free_pool_memory(buf);
551    regfree(&preg);
552 }
553
554 /* retreive a simple list (.pool, .client) and store it into items */
555 void get_items(const char *what)
556 {
557    init_items();
558
559    UA_sock->fsend("%s", what);
560    while (UA_sock->recv() > 0) {
561       strip_trailing_junk(UA_sock->msg);
562       items->list.append(bstrdup(UA_sock->msg));
563    }
564 }
565
566 typedef enum
567 {
568    ITEM_ARG,       /* item with simple list like .jobs */
569    ITEM_HELP       /* use help item=xxx and detect all arguments */
570 } cpl_item_t;
571
572 /* Generator function for command completion.  STATE lets us know whether
573  * to start from scratch; without any state (i.e. STATE == 0), then we
574  * start at the top of the list.
575  */
576 static char *item_generator(const char *text, int state,
577                             const char *item, cpl_item_t type)
578 {
579   static int list_index, len;
580   char *name;
581
582   /* If this is a new word to complete, initialize now.  This includes
583    * saving the length of TEXT for efficiency, and initializing the index
584    *  variable to 0.
585    */
586   if (!state)
587   {
588      list_index = 0;
589      len = strlen(text);
590      switch(type) {
591      case ITEM_ARG:
592         get_items(item);
593         break;
594      case ITEM_HELP:
595         get_arguments(item);
596         break;
597      }
598   }
599
600   /* Return the next name which partially matches from the command list. */
601   while (items && list_index < items->list.size())
602   {
603      name = (char *)items->list[list_index];
604      list_index++;
605
606      if (strncmp(name, text, len) == 0) {
607         char *ret = (char *) actuallymalloc(strlen(name)+1);
608         strcpy(ret, name);
609         return ret;
610      }
611   }
612
613   /* If no names matched, then return NULL. */
614   return ((char *)NULL);
615 }
616
617 /* gobal variables for the type and the item to search
618  * the readline API doesn' permit to pass user data.
619  */
620 static const char *cpl_item;
621 static cpl_item_t cpl_type;
622
623 static char *cpl_generator(const char *text, int state)
624 {
625    return item_generator(text, state, cpl_item, cpl_type);
626 }
627
628 /* this function is used to not use the default filename completion */
629 static char *dummy_completion_function(const char *text, int state)
630 {
631    return NULL;
632 }
633
634 struct cpl_keywords_t {
635    const char *key;
636    const char *cmd;
637 };
638
639 static struct cpl_keywords_t cpl_keywords[] = {
640    {"pool=",      ".pool"          },
641    {"fileset=",   ".fileset"       },
642    {"client=",    ".client"        },
643    {"job=",       ".jobs"          },
644    {"restore_job=",".jobs type=R"  },
645    {"level=",     ".level"         },
646    {"storage=",   ".storage"       },
647    {"schedule=",  ".schedule"      },
648    {"volume=",    ".media"         },
649    {"oldvolume=", ".media"         },
650    {"volstatus=", ".volstatus"     },
651    {"ls",         ".ls"            },
652    {"cd",         ".lsdir"         },
653    {"mark",       ".ls"            },
654    {"m",          ".ls"            },
655    {"unmark",     ".lsmark"        },
656    {"catalog=",   ".catalogs"      },
657    {"actiononpurge=", ".actiononpurge" },
658    {"tags=",      ".tags"          },
659    {"recylepool=", ".pool"         },
660    {"allfrompool=",".pool"         }
661 };
662 #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
663
664 /* Attempt to complete on the contents of TEXT.  START and END bound the
665  * region of rl_line_buffer that contains the word to complete.  TEXT is
666  * the word to complete.  We can use the entire contents of rl_line_buffer
667  * in case we want to do some simple parsing.  Return the array of matches,
668  * or NULL if there aren't any.
669  */
670 static char **readline_completion(const char *text, int start, int end)
671 {
672    bool found=false;
673    char **matches;
674    char *s, *cmd;
675    matches = (char **)NULL;
676
677    /* If this word is at the start of the line, then it is a command
678     * to complete.  Otherwise it is the name of a file in the current
679     * directory.
680     */
681    s = get_previous_keyword(start, 0);
682    cmd = get_first_keyword();
683    if (s) {
684       for (int i=0; i < key_size; i++) {
685          if (!strcasecmp(s, cpl_keywords[i].key)) {
686             cpl_item = cpl_keywords[i].cmd;
687             cpl_type = ITEM_ARG;
688             matches = rl_completion_matches(text, cpl_generator);
689             found=true;
690             break;
691          }
692       }
693
694       if (!found) {             /* we try to get help with the first command */
695          cpl_item = cmd;
696          cpl_type = ITEM_HELP;
697          /* we don't want to append " " at the end */
698          rl_completion_suppress_append=true;
699          matches = rl_completion_matches(text, cpl_generator);
700       }
701       free(s);
702    } else {                     /* nothing on the line, display all commands */
703       cpl_item = ".help all";
704       cpl_type = ITEM_ARG;
705       matches = rl_completion_matches(text, cpl_generator);
706    }
707    if (cmd) {
708       free(cmd);
709    }
710    return (matches);
711 }
712
713 static char eol = '\0';
714 static int eolcmd(FILE *input, BSOCK *UA_sock)
715 {
716    if ((argc > 1) && (strchr("!$%&'()*+,-/:;<>?[]^`{|}~", argk[1][0]) != NULL)) {
717       eol = argk[1][0];
718    } else if (argc == 1) {
719       eol = '\0';
720    } else {
721       sendit(_("Illegal separator character.\n"));
722    }
723    return 1;
724 }
725
726 /*
727  * Return 1 if OK
728  *        0 if no input
729  *       -1 error (must stop)
730  */
731 int
732 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
733 {
734    static char *line = NULL;
735    static char *next = NULL;
736    static int do_history = 0;
737    char *command;
738
739    if (line == NULL) {
740       do_history = 0;
741       rl_catch_signals = 0;              /* do it ourselves */
742       /* Here, readline does ***real*** malloc
743        * so, be we have to use the real free
744        */
745       line = readline((char *)prompt);   /* cast needed for old readlines */
746       if (!line) {
747          return -1;                      /* error return and exit */
748       }
749       strip_trailing_junk(line);
750       command = line;
751    } else if (next) {
752       command = next + 1;
753    } else {
754      sendit(_("Command logic problem\n"));
755      sock->msglen = 0;
756      sock->msg[0] = 0;
757      return 0;                  /* No input */
758    }
759
760    /*
761     * Split "line" into multiple commands separated by the eol character.
762     *   Each part is pointed to by "next" until finally it becomes null.
763     */
764    if (eol == '\0') {
765       next = NULL;
766    } else {
767       next = strchr(command, eol);
768       if (next) {
769          *next = '\0';
770       }
771    }
772    if (command != line && isatty(fileno(input))) {
773       senditf("%s%s\n", prompt, command);
774
775    } else {
776       /* Send the intput to the output file if needed */
777       if (teein && output != stdout) {
778          fputs(prompt, output);
779          fputs(command, output);
780          fputs("\n", output);
781       }
782    }
783
784    sock->msglen = pm_strcpy(&sock->msg, command);
785    if (sock->msglen) {
786       do_history++;
787    }
788
789    if (!next) {
790       if (do_history) {
791         add_history(line);
792       }
793       actuallyfree(line);       /* allocated by readline() malloc */
794       line = NULL;
795    }
796    return 1;                    /* OK */
797 }
798
799 #else /* no readline, do it ourselves */
800
801 #ifdef HAVE_CONIO
802 static bool bisatty(int fd)
803 {
804    if (no_conio) {
805       return false;
806    }
807    return isatty(fd);
808 }
809 #endif
810
811 /*
812  *   Returns: 1 if data available
813  *            0 if timeout
814  *           -1 if error
815  */
816 static int
817 wait_for_data(int fd, int sec)
818 {
819 #if defined(HAVE_WIN32)
820    return 1;
821 #else
822    for ( ;; ) {
823       switch(fd_wait_data(fd, WAIT_READ, sec, 0)) {
824       case 0:                         /* timeout */
825          return 0;
826       case -1:
827          if (errno == EINTR || errno == EAGAIN) {
828             continue;
829          }
830          return -1;                  /* error return */
831       default:
832          return 1;
833       }
834    }
835 #endif
836 }
837
838 /*
839  * Get next input command from terminal.
840  *
841  *   Returns: 1 if got input
842  *            0 if timeout
843  *           -1 if EOF or error
844  */
845 int
846 get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
847 {
848    int len;
849    if (!stop) {
850       if (output == stdout || teeout) {
851          sendit(prompt);
852       }
853    }
854 again:
855    switch (wait_for_data(fileno(input), sec)) {
856    case 0:
857       return 0;                    /* timeout */
858    case -1:
859       return -1;                   /* error */
860    default:
861       len = sizeof_pool_memory(sock->msg) - 1;
862       if (stop) {
863          sleep(1);
864          goto again;
865       }
866 #ifdef HAVE_CONIO
867       if (bisatty(fileno(input))) {
868          input_line(sock->msg, len);
869          break;
870       }
871 #endif
872 #ifdef HAVE_WIN32 /* use special console for input on win32 */
873       if (input == stdin) {
874          if (win32_cgets(sock->msg, len) == NULL) {
875             return -1;
876          }
877       }
878       else
879 #endif
880       if (bfgets(sock->msg, input) == NULL) {
881          return -1;
882
883       }
884       break;
885    }
886    if (usrbrk()) {
887       clrbrk();
888    }
889    strip_trailing_junk(sock->msg);
890    sock->msglen = strlen(sock->msg);
891
892    /* Send input to log file if needed */
893    if (teein && output != stdout) {
894       fputs(sock->msg, output);
895       fputs("\n", output);
896    }
897
898    return 1;
899 }
900
901 #endif /* ! HAVE_READLINE */
902
903 /* Routine to return true if user types break */
904 int usrbrk()
905 {
906    return brkflg;
907 }
908
909 /* Clear break flag */
910 void clrbrk()
911 {
912    brkflg = 0;
913 }
914
915 /* Interrupt caught here */
916 static void sigintcatcher(int sig)
917 {
918    brkflg++;
919    if (brkflg > 3) {
920       terminate_console(sig);
921    }
922    signal(SIGINT, sigintcatcher);
923 }
924
925 /* Trap Ctl-C */
926 void trapctlc()
927 {
928    signal(SIGINT, sigintcatcher);
929 }
930
931 static int console_update_history(const char *histfile)
932 {
933    int ret=0;
934
935 #ifdef HAVE_READLINE
936 /*
937  * first, try to truncate the history file, and if it
938  * fails, the file is probably not present, and we
939  * can use write_history to create it
940  */
941
942    if (history_truncate_file(histfile, 100) == 0) {
943       ret = append_history(history_length, histfile);
944    } else {
945       ret = write_history(histfile);
946    }
947 #endif
948
949    return ret;
950 }
951
952 static int console_init_history(const char *histfile)
953 {
954    int ret=0;
955
956 #ifdef HAVE_READLINE
957    using_history();
958    ret = read_history(histfile);
959    /* Tell the completer that we want a complete . */
960    rl_completion_entry_function = dummy_completion_function;
961    rl_attempted_completion_function = readline_completion;
962    rl_filename_completion_desired = 0;
963    stifle_history(100);
964 #endif
965
966    return ret;
967 }
968
969 static bool select_director(const char *director, const char *console,
970                             DIRRES **ret_dir, CONRES **ret_cons)
971 {
972    int numcon=0, numdir=0;
973    int i=0, item=0;
974    BSOCK *UA_sock;
975    DIRRES *dir = NULL;
976    CONRES *cons = NULL;
977
978    *ret_cons = NULL;
979    *ret_dir = NULL;
980
981    LockRes();
982    numdir = 0;
983    foreach_res(dir, R_DIRECTOR) {
984       numdir++;
985    }
986    numcon = 0;
987    foreach_res(cons, R_CONSOLE) {
988       numcon++;
989    }
990    UnlockRes();
991
992    if (numdir == 1) {           /* No choose */
993       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
994    }
995
996    if (director) {    /* Command line choice overwrite the no choose option */
997       LockRes();
998       foreach_res(dir, R_DIRECTOR) {
999          if (bstrcasecmp(dir->hdr.name, director)) {
1000             break;
1001          }
1002       }
1003       UnlockRes();
1004       if (!dir) {               /* Can't find Director used as argument */
1005          senditf(_("Can't find %s in Director list\n"), director);
1006          return 0;
1007       }
1008    }
1009
1010    if (dir == NULL) {               /* prompt for director */
1011       UA_sock = new_bsock();
1012 try_again:
1013       sendit(_("Available Directors:\n"));
1014       LockRes();
1015       numdir = 0;
1016       foreach_res(dir, R_DIRECTOR) {
1017          senditf( _("%2d:  %s at %s:%d\n"), 1+numdir++, dir->hdr.name,
1018                   dir->address, dir->DIRport);
1019       }
1020       UnlockRes();
1021       if (get_cmd(stdin, _("Select Director by entering a number: "),
1022                   UA_sock, 600) < 0)
1023       {
1024          (void)WSACleanup();               /* Cleanup Windows sockets */
1025          return 0;
1026       }
1027       if (!is_a_number(UA_sock->msg)) {
1028          senditf(_("%s is not a number. You must enter a number between "
1029                    "1 and %d\n"),
1030                  UA_sock->msg, numdir);
1031          goto try_again;
1032       }
1033       item = atoi(UA_sock->msg);
1034       if (item < 0 || item > numdir) {
1035          senditf(_("You must enter a number between 1 and %d\n"), numdir);
1036          goto try_again;
1037       }
1038       free_bsock(UA_sock);
1039       LockRes();
1040       for (i=0; i<item; i++) {
1041          dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
1042       }
1043       UnlockRes();
1044    }
1045    LockRes();
1046    /* Look for a console linked to this director */
1047    for (i=0; i<numcon; i++) {
1048       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1049       if (console) {
1050          if (strcmp(cons->hdr.name, console) == 0) {
1051             break;
1052          }
1053       } else if (cons->director && strcasecmp(cons->director, dir->hdr.name) == 0) {
1054          break;
1055       }
1056       if (i == (numcon - 1)) {
1057          cons = NULL;
1058       }
1059    }
1060
1061    if (cons == NULL && console != NULL) {
1062       UnlockRes();
1063       senditf(_("Can't find %s in Console list\n"), console);
1064       return 0;
1065    }
1066
1067    /* Look for the first non-linked console */
1068    if (cons == NULL) {
1069       for (i=0; i<numcon; i++) {
1070          cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
1071          if (cons->director == NULL) {
1072             break;
1073          }
1074          if (i == (numcon - 1)) {
1075             cons = NULL;
1076          }
1077       }
1078    }
1079
1080    /* If no console, take first one */
1081    if (!cons) {
1082       cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
1083    }
1084    UnlockRes();
1085
1086    *ret_dir = dir;
1087    *ret_cons = cons;
1088
1089    return 1;
1090 }
1091
1092 /*********************************************************************
1093  *
1094  *         Main Bacula Console -- User Interface Program
1095  *
1096  */
1097 int main(int argc, char *argv[])
1098 {
1099    int ch;
1100    char *director = NULL;
1101    char *console = NULL;
1102    bool list_directors=false, list_consoles=false;
1103    bool no_signals = false;
1104    bool test_config = false;
1105    JCR jcr;
1106    utime_t heart_beat;
1107
1108    setlocale(LC_ALL, "");
1109    bindtextdomain("bacula", LOCALEDIR);
1110    textdomain("bacula");
1111
1112    init_stack_dump();
1113    lmgr_init_thread();
1114    my_name_is(argc, argv, "bconsole");
1115    init_msg(NULL, NULL);
1116    working_directory = "/tmp";
1117    args = get_pool_memory(PM_FNAME);
1118
1119    while ((ch = getopt(argc, argv, "D:lc:d:nstu:?C:L")) != -1) {
1120       switch (ch) {
1121       case 'D':                    /* Director */
1122          if (director) {
1123             free(director);
1124          }
1125          director = bstrdup(optarg);
1126          break;
1127
1128       case 'C':                    /* Console */
1129          if (console) {
1130             free(console);
1131          }
1132          console = bstrdup(optarg);
1133          break;
1134
1135       case 'L':                    /* Console */
1136          list_consoles = true;
1137          test_config = true;
1138          break;
1139
1140       case 'l':
1141          list_directors = true;
1142          test_config = true;
1143          break;
1144
1145       case 'c':                    /* configuration file */
1146          if (configfile != NULL) {
1147             free(configfile);
1148          }
1149          configfile = bstrdup(optarg);
1150          break;
1151
1152       case 'd':
1153          if (*optarg == 't') {
1154             dbg_timestamp = true;
1155          } else {
1156             debug_level = atoi(optarg);
1157             if (debug_level <= 0) {
1158                debug_level = 1;
1159             }
1160          }
1161          break;
1162
1163       case 'n':                    /* no conio */
1164          no_conio = true;
1165          break;
1166
1167       case 's':                    /* turn off signals */
1168          no_signals = true;
1169          break;
1170
1171       case 't':
1172          test_config = true;
1173          break;
1174
1175       case 'u':
1176          timeout = atoi(optarg);
1177          break;
1178
1179       case '?':
1180       default:
1181          usage();
1182          exit(1);
1183       }
1184    }
1185    argc -= optind;
1186    argv += optind;
1187
1188    if (!no_signals) {
1189       init_signals(terminate_console);
1190    }
1191
1192
1193 #if !defined(HAVE_WIN32)
1194    /* Override Bacula default signals */
1195    signal(SIGQUIT, SIG_IGN);
1196    signal(SIGTSTP, got_sigstop);
1197    signal(SIGCONT, got_sigcontinue);
1198    signal(SIGTTIN, got_sigtin);
1199    signal(SIGTTOU, got_sigtout);
1200    trapctlc();
1201 #endif
1202
1203    OSDependentInit();
1204
1205    if (argc) {
1206       usage();
1207       exit(1);
1208    }
1209
1210    if (configfile == NULL) {
1211       configfile = bstrdup(CONFIG_FILE);
1212    }
1213
1214    config = New(CONFIG());
1215    parse_cons_config(config, configfile, M_ERROR_TERM);
1216
1217    if (init_crypto() != 0) {
1218       Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
1219    }
1220
1221    if (!check_resources()) {
1222       Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
1223    }
1224
1225    if (!no_conio) {
1226       con_init(stdin);
1227    }
1228
1229    if (list_directors) {
1230       LockRes();
1231       foreach_res(dir, R_DIRECTOR) {
1232          senditf("%s\n", dir->hdr.name);
1233       }
1234       UnlockRes();
1235    }
1236
1237    if (list_consoles) {
1238       LockRes();
1239       foreach_res(cons, R_CONSOLE) {
1240          senditf("%s\n", cons->hdr.name);
1241       }
1242       UnlockRes();
1243    }
1244
1245    if (test_config) {
1246       terminate_console(0);
1247       exit(0);
1248    }
1249
1250    memset(&jcr, 0, sizeof(jcr));
1251
1252    (void)WSA_Init();                        /* Initialize Windows sockets */
1253
1254    start_watchdog();                        /* Start socket watchdog */
1255
1256    if (!select_director(director, console, &dir, &cons)) {
1257       terminate_console(0);
1258       return 1;
1259    }
1260
1261    senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
1262
1263    char buf[1024];
1264    /* Initialize Console TLS context */
1265    if (cons && (cons->tls_enable || cons->tls_require)) {
1266       /* Generate passphrase prompt */
1267       bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ", cons->hdr.name);
1268
1269       /* Initialize TLS context:
1270        * Args: CA certfile, CA certdir, Certfile, Keyfile,
1271        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
1272        */
1273       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
1274          cons->tls_ca_certdir, cons->tls_certfile,
1275          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1276
1277       if (!cons->tls_ctx) {
1278          senditf(_("Failed to initialize TLS context for Console \"%s\".\n"),
1279             dir->hdr.name);
1280          terminate_console(0);
1281          return 1;
1282       }
1283    }
1284
1285    /* Initialize Director TLS context */
1286    if (dir->tls_enable || dir->tls_require) {
1287       /* Generate passphrase prompt */
1288       bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ", dir->hdr.name);
1289
1290       /* Initialize TLS context:
1291        * Args: CA certfile, CA certdir, Certfile, Keyfile,
1292        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
1293       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
1294          dir->tls_ca_certdir, dir->tls_certfile,
1295          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
1296
1297       if (!dir->tls_ctx) {
1298          senditf(_("Failed to initialize TLS context for Director \"%s\".\n"),
1299             dir->hdr.name);
1300          terminate_console(0);
1301          return 1;
1302       }
1303    }
1304
1305    if (dir->heartbeat_interval) {
1306       heart_beat = dir->heartbeat_interval;
1307    } else if (cons) {
1308       heart_beat = cons->heartbeat_interval;
1309    } else {
1310       heart_beat = 0;
1311    }
1312    if (!UA_sock) {
1313       UA_sock = new_bsock();
1314    }
1315    if (!UA_sock->connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
1316                           NULL, dir->DIRport, 0)) {
1317       UA_sock->destroy();
1318       UA_sock = NULL;
1319       terminate_console(0);
1320       return 1;
1321    }
1322    jcr.dir_bsock = UA_sock;
1323
1324    /* If cons==NULL, default console will be used */
1325    if (!authenticate_director(UA_sock, dir, cons)) {
1326       terminate_console(0);
1327       return 1;
1328    }
1329
1330    Dmsg0(40, "Opened connection with Director daemon\n");
1331
1332    sendit(_("Enter a period to cancel a command.\n"));
1333
1334    /* Read/Update history file if HOME exists */
1335    POOL_MEM history_file;
1336
1337    /* Run commands in ~/.bconsolerc if any */
1338    char *env = getenv("HOME");
1339    if (env) {
1340       FILE *fd;
1341       pm_strcpy(&UA_sock->msg, env);
1342       pm_strcat(&UA_sock->msg, "/.bconsolerc");
1343       fd = fopen(UA_sock->msg, "rb");
1344       if (fd) {
1345          read_and_process_input(fd, UA_sock);
1346          fclose(fd);
1347       }
1348
1349       pm_strcpy(history_file, env);
1350       pm_strcat(history_file, "/.bconsole_history");
1351       console_init_history(history_file.c_str());
1352    }
1353
1354    read_and_process_input(stdin, UA_sock);
1355
1356    if (UA_sock) {
1357       UA_sock->signal(BNET_TERMINATE); /* send EOF */
1358       UA_sock->close();
1359    }
1360
1361    if (env) {
1362       console_update_history(history_file.c_str());
1363    }
1364
1365    terminate_console(0);
1366    return 0;
1367 }
1368
1369 /* Cleanup and then exit */
1370 static void terminate_console(int sig)
1371 {
1372
1373    static bool already_here = false;
1374
1375    if (already_here) {                /* avoid recursive temination problems */
1376       exit(1);
1377    }
1378    already_here = true;
1379    stop_watchdog();
1380    delete(config);
1381    config = NULL;
1382    cleanup_crypto();
1383    free(res_head);
1384    res_head = NULL;
1385    free_pool_memory(args);
1386 #if defined(HAVE_CONIO)
1387    if (!no_conio) {
1388       con_term();
1389    }
1390 #elif defined(HAVE_READLINE)
1391    rl_cleanup_after_signal();
1392 #else /* !HAVE_CONIO && !HAVE_READLINE */
1393 #endif
1394    (void)WSACleanup();               /* Cleanup Windows sockets */
1395    lmgr_cleanup_main();
1396
1397    if (sig != 0) {
1398       exit(1);
1399    }
1400    return;
1401 }
1402
1403 /*
1404  * Make a quick check to see that we have all the
1405  * resources needed.
1406  */
1407 static int check_resources()
1408 {
1409    bool OK = true;
1410    DIRRES *director;
1411    bool tls_needed;
1412
1413    LockRes();
1414
1415    numdir = 0;
1416    foreach_res(director, R_DIRECTOR) {
1417
1418       numdir++;
1419       /* tls_require implies tls_enable */
1420       if (director->tls_require) {
1421          if (have_tls) {
1422             director->tls_enable = true;
1423          } else {
1424             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1425             OK = false;
1426             continue;
1427          }
1428       }
1429       tls_needed = director->tls_enable || director->tls_authenticate;
1430
1431       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && tls_needed) {
1432          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1433                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
1434                              " At least one CA certificate store is required.\n"),
1435                              director->hdr.name, configfile);
1436          OK = false;
1437       }
1438    }
1439
1440    if (numdir == 0) {
1441       Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
1442                           "Without that I don't how to speak to the Director :-(\n"), configfile);
1443       OK = false;
1444    }
1445
1446    CONRES *cons;
1447    /* Loop over Consoles */
1448    foreach_res(cons, R_CONSOLE) {
1449       /* tls_require implies tls_enable */
1450       if (cons->tls_require) {
1451          if (have_tls) {
1452             cons->tls_enable = true;
1453          } else {
1454             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
1455             OK = false;
1456             continue;
1457          }
1458       }
1459       tls_needed = cons->tls_enable || cons->tls_authenticate;
1460       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && tls_needed) {
1461          Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
1462                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
1463                              cons->hdr.name, configfile);
1464          OK = false;
1465       }
1466    }
1467
1468    UnlockRes();
1469
1470    return OK;
1471 }
1472
1473 /* @version */
1474 static int versioncmd(FILE *input, BSOCK *UA_sock)
1475 {
1476    senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
1477       HOST_OS, DISTNAME, DISTVER);
1478    return 1;
1479 }
1480
1481 /* @input <input-filename> */
1482 static int inputcmd(FILE *input, BSOCK *UA_sock)
1483 {
1484    FILE *fd;
1485
1486    if (argc > 2) {
1487       sendit(_("Too many arguments on input command.\n"));
1488       return 1;
1489    }
1490    if (argc == 1) {
1491       sendit(_("First argument to input command must be a filename.\n"));
1492       return 1;
1493    }
1494    fd = fopen(argk[1], "rb");
1495    if (!fd) {
1496       berrno be;
1497       senditf(_("Cannot open file %s for input. ERR=%s\n"),
1498          argk[1], be.bstrerror());
1499       return 1;
1500    }
1501    read_and_process_input(fd, UA_sock);
1502    fclose(fd);
1503    return 1;
1504 }
1505
1506 /* @tall <output-filename> */
1507 /* Send input/output to both terminal and specified file */
1508 static int teeallcmd(FILE *input, BSOCK *UA_sock)
1509 {
1510    teeout = true;
1511    teein = true;
1512    return do_outputcmd(input, UA_sock);
1513 }
1514
1515 /* @tee <output-filename> */
1516 /* Send output to both terminal and specified file */
1517 static int teecmd(FILE *input, BSOCK *UA_sock)
1518 {
1519    teeout = true;
1520    teein = false;
1521    return do_outputcmd(input, UA_sock);
1522 }
1523
1524 /* @output <output-filename> */
1525 /* Send output to specified "file" */
1526 static int outputcmd(FILE *input, BSOCK *UA_sock)
1527 {
1528    teeout = false;
1529    teein  = false;
1530    return do_outputcmd(input, UA_sock);
1531 }
1532
1533
1534 static int do_outputcmd(FILE *input, BSOCK *UA_sock)
1535 {
1536    FILE *fd;
1537    const char *mode = "a+b";
1538
1539    if (argc > 3) {
1540       sendit(_("Too many arguments on output/tee command.\n"));
1541       return 1;
1542    }
1543    if (argc == 1) {
1544       if (output != stdout) {
1545          fclose(output);
1546          output = stdout;
1547          teeout = false;
1548          teein = false;
1549       }
1550       return 1;
1551    }
1552    if (argc == 3) {
1553       mode = argk[2];
1554    }
1555    fd = fopen(argk[1], mode);
1556    if (!fd) {
1557       berrno be;
1558       senditf(_("Cannot open file %s for output. ERR=%s\n"),
1559          argk[1], be.bstrerror(errno));
1560       return 1;
1561    }
1562    output = fd;
1563    return 1;
1564 }
1565
1566 /*
1567  * @exec "some-command" [wait-seconds]
1568 */
1569 static int execcmd(FILE *input, BSOCK *UA_sock)
1570 {
1571    BPIPE *bpipe;
1572    char line[5000];
1573    int stat;
1574    int wait = 0;
1575    char *cmd;
1576
1577    if (argc > 3) {
1578       sendit(_("Too many arguments. Enclose command in double quotes.\n"));
1579       return 1;
1580    }
1581
1582    /* old syntax */
1583    if (argc == 3) {
1584       wait = atoi(argk[2]);
1585    }
1586    cmd = argk[1];
1587
1588    /* handle cmd=XXXX and wait=XXXX */
1589    for (int i=1; i<argc; i++) {
1590       if (strcmp(argk[i], "cmd") == 0) {
1591          cmd = argv[i];
1592       }
1593       if (strcmp(argk[i], "wait") == 0) {
1594          wait = atoi(argv[i]);
1595       }
1596    }
1597
1598    bpipe = open_bpipe(cmd, wait, "r");
1599    if (!bpipe) {
1600       berrno be;
1601       senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
1602          argk[1], be.bstrerror(errno));
1603       return 1;
1604    }
1605
1606    while (fgets(line, sizeof(line), bpipe->rfd)) {
1607       senditf("%s", line);
1608    }
1609    stat = close_bpipe(bpipe);
1610    if (stat != 0) {
1611       berrno be;
1612       be.set_errno(stat);
1613       senditf(_("@exec error: ERR=%s\n"), be.bstrerror());
1614    }
1615    return 1;
1616 }
1617
1618 /* @echo xxx yyy */
1619 static int echocmd(FILE *input, BSOCK *UA_sock)
1620 {
1621    for (int i=1; i < argc; i++) {
1622       senditf("%s ", argk[i]);
1623    }
1624    sendit("\n");
1625    return 1;
1626 }
1627
1628 /* @quit */
1629 static int quitcmd(FILE *input, BSOCK *UA_sock)
1630 {
1631    return 0;
1632 }
1633
1634 /* @help */
1635 static int helpcmd(FILE *input, BSOCK *UA_sock)
1636 {
1637    int i;
1638    for (i=0; i<comsize; i++) {
1639       senditf("  %-10s %s\n", commands[i].key, commands[i].help);
1640    }
1641    return 1;
1642 }
1643
1644
1645 /* @sleep secs */
1646 static int sleepcmd(FILE *input, BSOCK *UA_sock)
1647 {
1648    if (argc > 1) {
1649       sleep(atoi(argk[1]));
1650    }
1651    return 1;
1652 }
1653
1654 /* @putfile key /path/to/file
1655  *
1656  * The Key parameter is needed to use the file on the director side.
1657  */
1658 static int putfilecmd(FILE *input, BSOCK *UA_sock)
1659 {
1660    int i = 0;
1661    const char *key = "putfile";
1662    const char *fname;
1663    FILE *fp;
1664
1665    if (argc != 3) {
1666       sendit("Usage: @putfile key file\n");
1667       return 1;
1668    }
1669
1670    key = argk[1];
1671    fname = argk[2];
1672
1673    if (!key || !fname) {
1674       senditf("Syntax error in @putfile command\n");
1675       return 1;
1676    }
1677
1678    fp = fopen(fname, "r");
1679    if (!fp) {
1680       berrno be;
1681       senditf("Unable to open %s. ERR=%s\n", fname, be.bstrerror(errno));
1682       return 1;
1683    }
1684
1685    UA_sock->fsend(".putfile key=\"%s\"", key);
1686
1687    /* Just read the file and send it to the director */
1688    while (!feof(fp)) {
1689       i = fread(UA_sock->msg, 1, sizeof_pool_memory(UA_sock->msg) - 1, fp);
1690       if (i > 0) {
1691          UA_sock->msg[i] = 0;
1692          UA_sock->msglen = i;
1693          UA_sock->send();
1694       }
1695    }
1696
1697    UA_sock->signal(BNET_EOD);
1698    fclose(fp);
1699
1700    /* Get the file name associated */
1701    while (UA_sock->recv() > 0) {
1702       senditf("%s", UA_sock->msg);
1703    }
1704    return 1;
1705 }
1706
1707 /* @time */
1708 static int timecmd(FILE *input, BSOCK *UA_sock)
1709 {
1710    char sdt[50];
1711    time_t ttime = time(NULL);
1712    struct tm tm;
1713    (void)localtime_r(&ttime, &tm);
1714    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1715    sendit("\n");
1716    return 1;
1717 }
1718
1719 /*
1720  * Send a line to the output file and or the terminal
1721  */
1722 void senditf(const char *fmt,...)
1723 {
1724    char buf[3000];
1725    va_list arg_ptr;
1726
1727    va_start(arg_ptr, fmt);
1728    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1729    va_end(arg_ptr);
1730    sendit(buf);
1731 }
1732
1733 void sendit(const char *buf)
1734 {
1735 #ifdef CONIO_FIX
1736    char obuf[3000];
1737    if (output == stdout || teeout) {
1738       const char *p, *q;
1739       /*
1740        * Here, we convert every \n into \r\n because the
1741        *  terminal is in raw mode when we are using
1742        *  conio.
1743        */
1744       for (p=q=buf; (p=strchr(q, '\n')); ) {
1745          int len = p - q;
1746          if (len > 0) {
1747             memcpy(obuf, q, len);
1748          }
1749          memcpy(obuf+len, "\r\n", 3);
1750          q = ++p;                    /* point after \n */
1751          fputs(obuf, output);
1752       }
1753       if (*q) {
1754          fputs(q, output);
1755       }
1756       fflush(output);
1757    }
1758    if (output != stdout) {
1759       fputs(buf, output);
1760    }
1761 #else
1762
1763    fputs(buf, output);
1764    fflush(output);
1765    if (teeout) {
1766       fputs(buf, stdout);
1767       fflush(stdout);
1768    }
1769 #endif
1770 }