/*
- Bacula® - The Network Backup Solution
-
- Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
-
- The main author of Bacula is Kern Sibbald, with contributions from
- many others, a complete list can be found in the file AUTHORS.
- This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
- License as published by the Free Software Foundation and included
- in the file LICENSE.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
- Bacula® is a registered trademark of Kern Sibbald.
- The licensor of Bacula is the Free Software Foundation Europe
- (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
- Switzerland, email:ftf@fsfeurope.org.
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2018 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
*/
/*
*
*
* Kern Sibbald, September MM
*
- * Version $Id$
*/
#include "bacula.h"
#include "jcr.h"
-#ifdef HAVE_CONIO
+#if defined(HAVE_CONIO)
#include "conio.h"
//#define CONIO_FIX 1
-#else
+#else /* defined(HAVE_READLINE) || "DUMB" */
#define con_init(x)
#define con_term()
#define con_set_zed_keys();
-#define trapctlc()
-#define clrbrk()
-#define usrbrk() 0
#endif
+void trapctlc();
+void clrbrk();
+int usrbrk();
+static int brkflg = 0; /* set on user break */
+
#if defined(HAVE_WIN32)
#define isatty(fd) (fd==0)
#endif
//extern int rl_catch_signals;
/* Imported functions */
-int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
-extern bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code);
+int authenticate_director(BSOCK *dir, DIRRES *director, CONRES *cons);
/* Forward referenced functions */
static void terminate_console(int sig);
static CONRES *cons = NULL;
static FILE *output = stdout;
static bool teeout = false; /* output to output and stdout */
+static bool teein = false; /* input to output and stdout */
static bool stop = false;
static bool no_conio = false;
static int timeout = 0;
static int argc;
static int numdir;
-static int numcon;
static POOLMEM *args;
static char *argk[MAX_CMD_ARGS];
static char *argv[MAX_CMD_ARGS];
static int inputcmd(FILE *input, BSOCK *UA_sock);
static int outputcmd(FILE *input, BSOCK *UA_sock);
static int teecmd(FILE *input, BSOCK *UA_sock);
+static int teeallcmd(FILE *input, BSOCK *UA_sock);
static int quitcmd(FILE *input, BSOCK *UA_sock);
static int helpcmd(FILE *input, BSOCK *UA_sock);
static int echocmd(FILE *input, BSOCK *UA_sock);
static int timecmd(FILE *input, BSOCK *UA_sock);
static int sleepcmd(FILE *input, BSOCK *UA_sock);
static int execcmd(FILE *input, BSOCK *UA_sock);
+static int putfilecmd(FILE *input, BSOCK *UA_sock);
+
#ifdef HAVE_READLINE
static int eolcmd(FILE *input, BSOCK *UA_sock);
{
fprintf(stderr, _(
PROG_COPYRIGHT
-"\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
+"\n%sVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
"Usage: bconsole [-s] [-c config_file] [-d debug_level]\n"
+" -D <dir> select a Director\n"
+" -l list Directors defined\n"
+" -L list Consoles defined\n"
+" -C <cons> select a console\n"
" -c <file> set configuration file to file\n"
" -d <nn> set debug level to <nn>\n"
" -dt print timestamp in debug output\n"
" -u <nn> set command execution timeout to <nn> seconds\n"
" -t test - read configuration and exit\n"
" -? print this message.\n"
-"\n"), 2000, HOST_OS, DISTNAME, DISTVER);
+"\n"), 2000, BDEMO, HOST_OS, DISTNAME, DISTVER);
}
{ N_("output"), outputcmd, _("output to file")},
{ N_("quit"), quitcmd, _("quit")},
{ N_("tee"), teecmd, _("output to file and terminal")},
+ { N_("tall"), teeallcmd, _("output everything to file and terminal (tee all)")},
{ N_("sleep"), sleepcmd, _("sleep specified time")},
{ N_("time"), timecmd, _("print current time")},
{ N_("version"), versioncmd, _("print Console's version")},
{ N_("echo"), echocmd, _("echo command string")},
{ N_("exec"), execcmd, _("execute an external command")},
{ N_("exit"), quitcmd, _("exit = quit")},
+ { N_("putfile"), putfilecmd, _("send a file to the director")},
{ N_("zed_keys"), zed_keyscmd, _("zed_keys = use zed keys instead of bash keys")},
{ N_("help"), helpcmd, _("help listing")},
#ifdef HAVE_READLINE
return stat;
}
+/* When getting .api command, we can ignore some signals, so we set
+ * api_mode=true
+ */
+static bool api_mode=false;
+
+static bool ignore_signal(int stat, BSOCK *s)
+{
+ /* Not in API mode */
+ if (!api_mode) {
+ return false;
+ }
+
+ /* not a signal */
+ if (stat != -1) {
+ return false;
+ }
+
+ /* List signal that should not stop the read loop */
+ Dmsg1(100, "Got signal %s\n", bnet_sig_to_ascii(s->msglen));
+ switch(s->msglen) {
+ case BNET_CMD_BEGIN:
+ case BNET_CMD_FAILED: /* might want to print **ERROR** */
+ case BNET_CMD_OK: /* might want to print **OK** */
+ case BNET_MSGS_PENDING:
+ return true;
+ default:
+ break;
+ }
+
+ /* The signal should break the read loop */
+ return false;
+}
static void read_and_process_input(FILE *input, BSOCK *UA_sock)
{
}
if (tty_input) {
stat = get_cmd(input, prompt, UA_sock, 30);
- if (usrbrk() == 1) {
+ if (usrbrk() >= 1) {
clrbrk();
}
if (usrbrk()) {
}
} else {
/* Reading input from a file */
- int len = sizeof_pool_memory(UA_sock->msg) - 1;
if (usrbrk()) {
break;
}
- if (fgets(UA_sock->msg, len, input) == NULL) {
+ if (bfgets(UA_sock->msg, input) == NULL) {
stat = -1;
} else {
sendit(UA_sock->msg); /* echo to terminal */
} else if (stat == 0) { /* timeout */
if (strcmp(prompt, "*") == 0) {
tid = start_bsock_timer(UA_sock, timeout);
- bnet_fsend(UA_sock, ".messages");
+ UA_sock->fsend(".messages");
stop_bsock_timer(tid);
} else {
continue;
}
} else {
- at_prompt = FALSE;
+ at_prompt = false;
/* @ => internal command for us */
if (UA_sock->msg[0] == '@') {
parse_args(UA_sock->msg, &args, &argc, argk, argv, MAX_CMD_ARGS);
continue;
}
tid = start_bsock_timer(UA_sock, timeout);
- if (!bnet_send(UA_sock)) { /* send command */
+ if (!UA_sock->send()) { /* send command */
stop_bsock_timer(tid);
break; /* error */
}
stop_bsock_timer(tid);
}
- if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) {
+ if (strncasecmp(UA_sock->msg, ".api", 4) == 0) {
+ api_mode = true;
+ }
+ if (strcasecmp(UA_sock->msg, ".quit") == 0 || strcasecmp(UA_sock->msg, ".exit") == 0) {
break;
}
tid = start_bsock_timer(UA_sock, timeout);
- while ((stat = bnet_recv(UA_sock)) >= 0) {
+ while (1) {
+ stat = UA_sock->recv();
+ if (ignore_signal(stat, UA_sock)) {
+ continue;
+ }
+
+ if (stat < 0) {
+ break;
+ }
+
if (at_prompt) {
if (!stop) {
sendit("\n");
if (!stop) {
fflush(stdout);
}
- if (is_bnet_stop(UA_sock)) {
+ if (UA_sock->is_stop()) {
break; /* error or term */
} else if (stat == BNET_SIGNAL) {
- if (UA_sock->msglen == BNET_PROMPT) {
+ if (UA_sock->msglen == BNET_SUB_PROMPT) {
at_prompt = true;
}
- Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
+ Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock->msglen));
}
}
}
/*
* Call-back for reading a passphrase for an encrypted PEM file
- * This function uses getpass(),
+ * This function uses getpass(),
* which uses a static buffer and is NOT thread-safe.
*/
static int tls_pem_callback(char *buf, int size, const void *userdata)
break;
}
}
-
+
/* find the end of the command */
for (; i >= 0; i--) {
if (rl_line_buffer[i] != ' ') {
break;
}
}
-
+
/* no end of string */
if (end == -1) {
return NULL;
}
-
+
/* look for the start of the command */
for (start = end; start > 0; start--) {
if (rl_line_buffer[start] == '"') {
{
int rc, size;
int nmatch=20;
- regmatch_t pmatch[20];
+ regmatch_t pmatch[nmatch];
if (len <= 0) {
return;
}
- rc = regexec(preg, what, nmatch, pmatch, 0);
+ rc = regexec(preg, what, nmatch, pmatch, 0);
if (rc == 0) {
#if 0
Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n 10 20 30\n", what);
}
}
-typedef enum
+typedef enum
{
- ITEM_ARG, /* item with simple list like .job */
+ ITEM_ARG, /* item with simple list like .jobs */
ITEM_HELP /* use help item=xxx and detect all arguments */
} cpl_item_t;
/* Generator function for command completion. STATE lets us know whether
* to start from scratch; without any state (i.e. STATE == 0), then we
- * start at the top of the list.
+ * start at the top of the list.
*/
-static char *item_generator(const char *text, int state,
+static char *item_generator(const char *text, int state,
const char *item, cpl_item_t type)
{
static int list_index, len;
/* If this is a new word to complete, initialize now. This includes
* saving the length of TEXT for efficiency, and initializing the index
- * variable to 0.
+ * variable to 0.
*/
if (!state)
{
{
name = (char *)items->list[list_index];
list_index++;
-
+
if (strncmp(name, text, len) == 0) {
char *ret = (char *) actuallymalloc(strlen(name)+1);
strcpy(ret, name);
}
/* If no names matched, then return NULL. */
- return ((char *)NULL);
+ return ((char *)NULL);
}
-/* gobal variables for the type and the item to search
+/* gobal variables for the type and the item to search
* the readline API doesn' permit to pass user data.
*/
static const char *cpl_item;
{"pool=", ".pool" },
{"fileset=", ".fileset" },
{"client=", ".client" },
- {"job=", ".job" },
+ {"job=", ".jobs" },
+ {"restore_job=",".jobs type=R" },
{"level=", ".level" },
{"storage=", ".storage" },
{"schedule=", ".schedule" },
{"cd", ".lsdir" },
{"mark", ".ls" },
{"m", ".ls" },
- {"actiononpurge=", ".actiononpurge" }
+ {"unmark", ".lsmark" },
+ {"catalog=", ".catalogs" },
+ {"actiononpurge=", ".actiononpurge" },
+ {"tags=", ".tags" },
+ {"recylepool=", ".pool" },
+ {"allfrompool=",".pool" }
};
#define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t)))
* region of rl_line_buffer that contains the word to complete. TEXT is
* the word to complete. We can use the entire contents of rl_line_buffer
* in case we want to do some simple parsing. Return the array of matches,
- * or NULL if there aren't any.
+ * or NULL if there aren't any.
*/
static char **readline_completion(const char *text, int start, int end)
{
/* If this word is at the start of the line, then it is a command
* to complete. Otherwise it is the name of a file in the current
- * directory.
+ * directory.
*/
s = get_previous_keyword(start, 0);
cmd = get_first_keyword();
break;
}
}
-
+
if (!found) { /* we try to get help with the first command */
cpl_item = cmd;
cpl_type = ITEM_HELP;
/* we don't want to append " " at the end */
- rl_completion_suppress_append=true;
+ rl_completion_suppress_append=true;
matches = rl_completion_matches(text, cpl_generator);
- }
+ }
free(s);
} else { /* nothing on the line, display all commands */
cpl_item = ".help all";
return 1;
}
+/*
+ * Return 1 if OK
+ * 0 if no input
+ * -1 error (must stop)
+ */
int
get_cmd(FILE *input, const char *prompt, BSOCK *sock, int sec)
{
*/
line = readline((char *)prompt); /* cast needed for old readlines */
if (!line) {
- exit(1);
+ return -1; /* error return and exit */
}
strip_trailing_junk(line);
command = line;
sendit(_("Command logic problem\n"));
sock->msglen = 0;
sock->msg[0] = 0;
- return 0;
+ return 0; /* No input */
}
/*
}
if (command != line && isatty(fileno(input))) {
senditf("%s%s\n", prompt, command);
+
+ } else {
+ /* Send the intput to the output file if needed */
+ if (teein && output != stdout) {
+ fputs(prompt, output);
+ fputs(command, output);
+ fputs("\n", output);
+ }
}
sock->msglen = pm_strcpy(&sock->msg, command);
actuallyfree(line); /* allocated by readline() malloc */
line = NULL;
}
- return 1;
+ return 1; /* OK */
}
#else /* no readline, do it ourselves */
#if defined(HAVE_WIN32)
return 1;
#else
- fd_set fdset;
- struct timeval tv;
-
- tv.tv_sec = sec;
- tv.tv_usec = 0;
for ( ;; ) {
- FD_ZERO(&fdset);
- FD_SET((unsigned)fd, &fdset);
- switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
+ switch(fd_wait_data(fd, WAIT_READ, sec, 0)) {
case 0: /* timeout */
return 0;
case -1:
}
else
#endif
- if (fgets(sock->msg, len, input) == NULL) {
+ if (bfgets(sock->msg, input) == NULL) {
return -1;
}
}
strip_trailing_junk(sock->msg);
sock->msglen = strlen(sock->msg);
+
+ /* Send input to log file if needed */
+ if (teein && output != stdout) {
+ fputs(sock->msg, output);
+ fputs("\n", output);
+ }
+
return 1;
}
#endif /* ! HAVE_READLINE */
+/* Routine to return true if user types break */
+int usrbrk()
+{
+ return brkflg;
+}
+
+/* Clear break flag */
+void clrbrk()
+{
+ brkflg = 0;
+}
+
+/* Interrupt caught here */
+static void sigintcatcher(int sig)
+{
+ brkflg++;
+ if (brkflg > 3) {
+ terminate_console(sig);
+ }
+ signal(SIGINT, sigintcatcher);
+}
+
+/* Trap Ctl-C */
+void trapctlc()
+{
+ signal(SIGINT, sigintcatcher);
+}
static int console_update_history(const char *histfile)
{
int ret=0;
#ifdef HAVE_READLINE
-/* first, try to truncate the history file, and if it
- * fail, the file is probably not present, and we
+/*
+ * first, try to truncate the history file, and if it
+ * fails, the file is probably not present, and we
* can use write_history to create it
*/
} else {
ret = write_history(histfile);
}
-
#endif
return ret;
int ret=0;
#ifdef HAVE_READLINE
-
using_history();
ret = read_history(histfile);
/* Tell the completer that we want a complete . */
rl_completion_entry_function = dummy_completion_function;
rl_attempted_completion_function = readline_completion;
rl_filename_completion_desired = 0;
+ stifle_history(100);
#endif
return ret;
}
+static bool select_director(const char *director, const char *console,
+ DIRRES **ret_dir, CONRES **ret_cons)
+{
+ int numcon=0, numdir=0;
+ int i=0, item=0;
+ BSOCK *UA_sock;
+ DIRRES *dir = NULL;
+ CONRES *cons = NULL;
+
+ *ret_cons = NULL;
+ *ret_dir = NULL;
+
+ LockRes();
+ numdir = 0;
+ foreach_res(dir, R_DIRECTOR) {
+ numdir++;
+ }
+ numcon = 0;
+ foreach_res(cons, R_CONSOLE) {
+ numcon++;
+ }
+ UnlockRes();
+
+ if (numdir == 1) { /* No choose */
+ dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
+ }
+
+ if (director) { /* Command line choice overwrite the no choose option */
+ LockRes();
+ foreach_res(dir, R_DIRECTOR) {
+ if (bstrcasecmp(dir->hdr.name, director)) {
+ break;
+ }
+ }
+ UnlockRes();
+ if (!dir) { /* Can't find Director used as argument */
+ senditf(_("Can't find %s in Director list\n"), director);
+ return 0;
+ }
+ }
+
+ if (dir == NULL) { /* prompt for director */
+ UA_sock = new_bsock();
+try_again:
+ sendit(_("Available Directors:\n"));
+ LockRes();
+ numdir = 0;
+ foreach_res(dir, R_DIRECTOR) {
+ senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name,
+ dir->address, dir->DIRport);
+ }
+ UnlockRes();
+ if (get_cmd(stdin, _("Select Director by entering a number: "),
+ UA_sock, 600) < 0)
+ {
+ (void)WSACleanup(); /* Cleanup Windows sockets */
+ return 0;
+ }
+ if (!is_a_number(UA_sock->msg)) {
+ senditf(_("%s is not a number. You must enter a number between "
+ "1 and %d\n"),
+ UA_sock->msg, numdir);
+ goto try_again;
+ }
+ item = atoi(UA_sock->msg);
+ if (item < 0 || item > numdir) {
+ senditf(_("You must enter a number between 1 and %d\n"), numdir);
+ goto try_again;
+ }
+ free_bsock(UA_sock);
+ LockRes();
+ for (i=0; i<item; i++) {
+ dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
+ }
+ UnlockRes();
+ }
+ LockRes();
+ /* Look for a console linked to this director */
+ for (i=0; i<numcon; i++) {
+ cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
+ if (console) {
+ if (strcmp(cons->hdr.name, console) == 0) {
+ break;
+ }
+ } else if (cons->director && strcasecmp(cons->director, dir->hdr.name) == 0) {
+ break;
+ }
+ if (i == (numcon - 1)) {
+ cons = NULL;
+ }
+ }
+
+ if (cons == NULL && console != NULL) {
+ UnlockRes();
+ senditf(_("Can't find %s in Console list\n"), console);
+ return 0;
+ }
+
+ /* Look for the first non-linked console */
+ if (cons == NULL) {
+ for (i=0; i<numcon; i++) {
+ cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
+ if (cons->director == NULL) {
+ break;
+ }
+ if (i == (numcon - 1)) {
+ cons = NULL;
+ }
+ }
+ }
+
+ /* If no console, take first one */
+ if (!cons) {
+ cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
+ }
+ UnlockRes();
+
+ *ret_dir = dir;
+ *ret_cons = cons;
+
+ return 1;
+}
+
/*********************************************************************
*
* Main Bacula Console -- User Interface Program
*/
int main(int argc, char *argv[])
{
- int ch, i, item;
+ int ch;
+ char *director = NULL;
+ char *console = NULL;
+ bool list_directors=false, list_consoles=false;
bool no_signals = false;
bool test_config = false;
JCR jcr;
working_directory = "/tmp";
args = get_pool_memory(PM_FNAME);
- while ((ch = getopt(argc, argv, "bc:d:nstu:?")) != -1) {
+ while ((ch = getopt(argc, argv, "D:lc:d:nstu:?C:L")) != -1) {
switch (ch) {
+ case 'D': /* Director */
+ if (director) {
+ free(director);
+ }
+ director = bstrdup(optarg);
+ break;
+
+ case 'C': /* Console */
+ if (console) {
+ free(console);
+ }
+ console = bstrdup(optarg);
+ break;
+
+ case 'L': /* Console */
+ list_consoles = true;
+ test_config = true;
+ break;
+
+ case 'l':
+ list_directors = true;
+ test_config = true;
+ break;
+
case 'c': /* configuration file */
if (configfile != NULL) {
free(configfile);
configfile = bstrdup(CONFIG_FILE);
}
- config = new_config_parser();
+ config = New(CONFIG());
parse_cons_config(config, configfile, M_ERROR_TERM);
if (init_crypto() != 0) {
con_init(stdin);
}
+ if (list_directors) {
+ LockRes();
+ foreach_res(dir, R_DIRECTOR) {
+ senditf("%s\n", dir->hdr.name);
+ }
+ UnlockRes();
+ }
+
+ if (list_consoles) {
+ LockRes();
+ foreach_res(cons, R_CONSOLE) {
+ senditf("%s\n", cons->hdr.name);
+ }
+ UnlockRes();
+ }
+
if (test_config) {
terminate_console(0);
exit(0);
start_watchdog(); /* Start socket watchdog */
- LockRes();
- numdir = 0;
- foreach_res(dir, R_DIRECTOR) {
- numdir++;
- }
- numcon = 0;
- foreach_res(cons, R_CONSOLE) {
- numcon++;
- }
- UnlockRes();
-
- if (numdir > 1) {
- struct sockaddr client_addr;
- memset(&client_addr, 0, sizeof(client_addr));
- UA_sock = init_bsock(NULL, 0, "", "", 0, &client_addr);
-try_again:
- sendit(_("Available Directors:\n"));
- LockRes();
- numdir = 0;
- foreach_res(dir, R_DIRECTOR) {
- senditf( _("%2d: %s at %s:%d\n"), 1+numdir++, dir->hdr.name, dir->address,
- dir->DIRport);
- }
- UnlockRes();
- if (get_cmd(stdin, _("Select Director by entering a number: "), UA_sock, 600) < 0) {
- (void)WSACleanup(); /* Cleanup Windows sockets */
- return 1;
- }
- if (!is_a_number(UA_sock->msg)) {
- senditf(_("%s is not a number. You must enter a number between 1 and %d\n"),
- UA_sock->msg, numdir);
- goto try_again;
- }
- item = atoi(UA_sock->msg);
- if (item < 0 || item > numdir) {
- senditf(_("You must enter a number between 1 and %d\n"), numdir);
- goto try_again;
- }
- term_bsock(UA_sock);
- LockRes();
- for (i=0; i<item; i++) {
- dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir);
- }
- /* Look for a console linked to this director */
- for (i=0; i<numcon; i++) {
- cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
- if (cons->director && strcmp(cons->director, dir->hdr.name) == 0) {
- break;
- }
- cons = NULL;
- }
- /* Look for the first non-linked console */
- if (cons == NULL) {
- for (i=0; i<numcon; i++) {
- cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
- if (cons->director == NULL)
- break;
- cons = NULL;
- }
- }
- UnlockRes();
- }
- /* If no director, take first one */
- if (!dir) {
- LockRes();
- dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
- UnlockRes();
- }
- /* If no console, take first one */
- if (!cons) {
- LockRes();
- cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
- UnlockRes();
+ if (!select_director(director, console, &dir, &cons)) {
+ terminate_console(0);
+ return 1;
}
senditf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
/* Initialize TLS context:
* Args: CA certfile, CA certdir, Certfile, Keyfile,
- * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
+ * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
*/
cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
cons->tls_ca_certdir, cons->tls_certfile,
} else {
heart_beat = 0;
}
- UA_sock = bnet_connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
- NULL, dir->DIRport, 0);
- if (UA_sock == NULL) {
+ if (!UA_sock) {
+ UA_sock = new_bsock();
+ }
+ if (!UA_sock->connect(NULL, 5, 15, heart_beat, "Director daemon", dir->address,
+ NULL, dir->DIRport, 0)) {
+ UA_sock->destroy();
+ UA_sock = NULL;
terminate_console(0);
return 1;
}
jcr.dir_bsock = UA_sock;
/* If cons==NULL, default console will be used */
- if (!authenticate_director(&jcr, dir, cons)) {
+ if (!authenticate_director(UA_sock, dir, cons)) {
terminate_console(0);
return 1;
}
}
already_here = true;
stop_watchdog();
- config->free_resources();
- free(config);
+ delete(config);
config = NULL;
cleanup_crypto();
+ free(res_head);
+ res_head = NULL;
free_pool_memory(args);
+#if defined(HAVE_CONIO)
if (!no_conio) {
con_term();
}
+#elif defined(HAVE_READLINE)
+ rl_cleanup_after_signal();
+#else /* !HAVE_CONIO && !HAVE_READLINE */
+#endif
(void)WSACleanup(); /* Cleanup Windows sockets */
lmgr_cleanup_main();
OK = false;
}
}
-
+
if (numdir == 0) {
Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
"Without that I don't how to speak to the Director :-(\n"), configfile);
return OK;
}
+/* @version */
static int versioncmd(FILE *input, BSOCK *UA_sock)
{
senditf("Version: " VERSION " (" BDATE ") %s %s %s\n",
return 1;
}
+/* @input <input-filename> */
static int inputcmd(FILE *input, BSOCK *UA_sock)
{
FILE *fd;
return 1;
}
-/* Send output to both termina and specified file */
+/* @tall <output-filename> */
+/* Send input/output to both terminal and specified file */
+static int teeallcmd(FILE *input, BSOCK *UA_sock)
+{
+ teeout = true;
+ teein = true;
+ return do_outputcmd(input, UA_sock);
+}
+
+/* @tee <output-filename> */
+/* Send output to both terminal and specified file */
static int teecmd(FILE *input, BSOCK *UA_sock)
{
teeout = true;
+ teein = false;
return do_outputcmd(input, UA_sock);
}
+/* @output <output-filename> */
/* Send output to specified "file" */
static int outputcmd(FILE *input, BSOCK *UA_sock)
{
teeout = false;
+ teein = false;
return do_outputcmd(input, UA_sock);
}
fclose(output);
output = stdout;
teeout = false;
+ teein = false;
}
return 1;
}
}
/*
- * exec "some-command" [wait-seconds]
+ * @exec "some-command" [wait-seconds]
*/
static int execcmd(FILE *input, BSOCK *UA_sock)
{
char line[5000];
int stat;
int wait = 0;
+ char *cmd;
if (argc > 3) {
sendit(_("Too many arguments. Enclose command in double quotes.\n"));
return 1;
}
+
+ /* old syntax */
if (argc == 3) {
wait = atoi(argk[2]);
}
- bpipe = open_bpipe(argk[1], wait, "r");
+ cmd = argk[1];
+
+ /* handle cmd=XXXX and wait=XXXX */
+ for (int i=1; i<argc; i++) {
+ if (strcmp(argk[i], "cmd") == 0) {
+ cmd = argv[i];
+ }
+ if (strcmp(argk[i], "wait") == 0) {
+ wait = atoi(argv[i]);
+ }
+ }
+
+ bpipe = open_bpipe(cmd, wait, "r");
if (!bpipe) {
berrno be;
senditf(_("Cannot popen(\"%s\", \"r\"): ERR=%s\n"),
argk[1], be.bstrerror(errno));
return 1;
}
-
+
while (fgets(line, sizeof(line), bpipe->rfd)) {
senditf("%s", line);
}
if (stat != 0) {
berrno be;
be.set_errno(stat);
- senditf(_("Autochanger error: ERR=%s\n"), be.bstrerror());
+ senditf(_("@exec error: ERR=%s\n"), be.bstrerror());
}
return 1;
}
-
+/* @echo xxx yyy */
static int echocmd(FILE *input, BSOCK *UA_sock)
{
for (int i=1; i < argc; i++) {
return 1;
}
+/* @quit */
static int quitcmd(FILE *input, BSOCK *UA_sock)
{
return 0;
}
+/* @help */
static int helpcmd(FILE *input, BSOCK *UA_sock)
{
int i;
- for (i=0; i<comsize; i++) {
+ for (i=0; i<comsize; i++) {
senditf(" %-10s %s\n", commands[i].key, commands[i].help);
}
- return 1;
+ return 1;
}
+/* @sleep secs */
static int sleepcmd(FILE *input, BSOCK *UA_sock)
{
if (argc > 1) {
return 1;
}
+/* @putfile key /path/to/file
+ *
+ * The Key parameter is needed to use the file on the director side.
+ */
+static int putfilecmd(FILE *input, BSOCK *UA_sock)
+{
+ int i = 0;
+ const char *key = "putfile";
+ const char *fname;
+ FILE *fp;
+
+ if (argc != 3) {
+ sendit("Usage: @putfile key file\n");
+ return 1;
+ }
+
+ key = argk[1];
+ fname = argk[2];
+
+ if (!key || !fname) {
+ senditf("Syntax error in @putfile command\n");
+ return 1;
+ }
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ berrno be;
+ senditf("Unable to open %s. ERR=%s\n", fname, be.bstrerror(errno));
+ return 1;
+ }
+
+ UA_sock->fsend(".putfile key=\"%s\"", key);
+
+ /* Just read the file and send it to the director */
+ while (!feof(fp)) {
+ i = fread(UA_sock->msg, 1, sizeof_pool_memory(UA_sock->msg) - 1, fp);
+ if (i > 0) {
+ UA_sock->msg[i] = 0;
+ UA_sock->msglen = i;
+ UA_sock->send();
+ }
+ }
+
+ UA_sock->signal(BNET_EOD);
+ fclose(fp);
+
+ /* Get the file name associated */
+ while (UA_sock->recv() > 0) {
+ senditf("%s", UA_sock->msg);
+ }
+ return 1;
+}
+/* @time */
static int timecmd(FILE *input, BSOCK *UA_sock)
{
char sdt[50];