/*
Bacula® - The Network Backup Solution
- Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
+ Copyright (C) 2007-2011 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.
+ 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 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.
+ 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.
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.
*/
/*
- * Version $Id$
- *
- * Console Class
+ * DirComm, Director communications,class
*
* Kern Sibbald, January MMVII
*
- */
+ */
#include "bat.h"
#include "console.h"
#include "restore.h"
#include "select.h"
+#include "textinput.h"
#include "run/run.h"
static int tls_pem_callback(char *buf, int size, const void *userdata);
-DirComm::DirComm(Console *parent, int conn):
-m_notifier(NULL),
-m_api_set(false)
+DirComm::DirComm(Console *parent, int conn): m_notifier(NULL), m_api_set(false)
{
m_console = parent;
m_sock = NULL;
m_at_prompt = false;
m_at_main_prompt = false;
+ m_sent_blank = false;
m_conn = conn;
m_in_command = 0;
+ m_in_select = false;
+ m_notify = false;
}
DirComm::~DirComm()
m_notifier->setEnabled(false);
delete m_notifier;
m_notifier = NULL;
+ m_notify = false;
}
- m_sock->close();
- m_sock = NULL;
+ if (mainWin->m_connDebug)
+ Pmsg2(000, "DirComm %i terminating connections %s\n", m_conn, m_console->m_dir->name());
+ free_bsock(m_sock);
}
}
/*
- * Connect to Director.
+ * Connect to Director.
*/
bool DirComm::connect_dir()
{
utime_t heart_beat;
char buf[1024];
CONRES *cons;
-
+ int numcon = 0;
+ int i = 0;
+
buf[0] = 0;
- if (m_sock) {
+ foreach_res(cons, R_CONSOLE) {
+ numcon++;
+ }
+
+ if (m_sock && !is_bsock_open(m_sock)) {
mainWin->set_status( tr("Already connected."));
m_console->display_textf(_("Already connected\"%s\".\n"),
m_console->m_dir->name());
- if (mainWin->m_connDebug)
- Pmsg1(000, "DirComm %i BAILING already connected\n", m_conn);
+ if (mainWin->m_connDebug) {
+ Pmsg2(000, "DirComm %i BAILING already connected %s\n", m_conn, m_console->m_dir->name());
+ }
goto bail_out;
}
+ if (mainWin->m_connDebug)Pmsg2(000, "DirComm %i connecting %s\n", m_conn, m_console->m_dir->name());
memset(jcr, 0, sizeof(JCR));
mainWin->set_statusf(_("Connecting to Director %s:%d"), m_console->m_dir->address, m_console->m_dir->DIRport);
- m_console->display_textf(_("Connecting to Director %s:%d\n\n"), m_console->m_dir->address, m_console->m_dir->DIRport);
+ if (m_conn == 0) {
+ m_console->display_textf(_("Connecting to Director %s:%d\n\n"), m_console->m_dir->address, m_console->m_dir->DIRport);
+ }
/* Give GUI a chance */
app->processEvents();
-
+
LockRes();
/* If cons==NULL, default console will be used */
- cons = (CONRES *)GetNextRes(R_CONSOLE, NULL);
+ for (i=0; i<numcon; i++) {
+ cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)cons);
+ if (cons->director && strcasecmp(cons->director, m_console->m_dir->name()) == 0) {
+ break;
+ }
+ if (i == (numcon - 1)) {
+ 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;
+ }
+ if (i == (numcon - 1)) {
+ cons = NULL;
+ }
+ }
+ }
+
+ /* If no console, take first one */
+ if (!cons) {
+ cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
+ }
UnlockRes();
/* Initialize Console TLS context once */
if (cons && !cons->tls_ctx && (cons->tls_enable || cons->tls_require)) {
/* Generate passphrase prompt */
- bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ",
+ bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ",
cons->name());
/* 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,
if (!cons->tls_ctx) {
m_console->display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
m_console->m_dir->name());
- if (mainWin->m_connDebug)
- Pmsg1(000, "DirComm %i BAILING Failed to initialize TLS context for Console \n", m_conn);
+ if (mainWin->m_connDebug) {
+ Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Console %s\n", m_conn, m_console->m_dir->name());
+ }
goto bail_out;
}
}
/* Initialize Director TLS context once */
if (!m_console->m_dir->tls_ctx && (m_console->m_dir->tls_enable || m_console->m_dir->tls_require)) {
/* Generate passphrase prompt */
- bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
+ bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
m_console->m_dir->name());
/* Initialize TLS context:
m_console->display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
m_console->m_dir->name());
mainWin->set_status("Connection failed");
- if (mainWin->m_connDebug)
- Pmsg1(000, "DirComm %i BAILING Failed to initialize TLS context for Director \n", m_conn);
+ if (mainWin->m_connDebug) {
+ Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Director %s\n", m_conn, m_console->m_dir->name());
+ }
goto bail_out;
}
}
heart_beat = cons->heartbeat_interval;
} else {
heart_beat = 0;
- }
+ }
- m_sock = bnet_connect(NULL, 5, 15, heart_beat,
+ if (!m_sock) {
+ m_sock = new_bsock();
+ }
+ if (!m_sock->connect(NULL, 5, 15, heart_beat,
_("Director daemon"), m_console->m_dir->address,
- NULL, m_console->m_dir->DIRport, 0);
+ NULL, m_console->m_dir->DIRport, 0)) {
+ m_sock->destroy();
+ m_sock = NULL;
+ }
if (m_sock == NULL) {
mainWin->set_status("Connection failed");
- if (mainWin->m_connDebug)
- Pmsg1(000, "DirComm %i BAILING Connection failed\n", m_conn);
+ if (mainWin->m_connDebug) {
+ Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
+ }
goto bail_out;
} else {
/* Update page selector to green to indicate that Console is connected */
mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
QBrush greenBrush(Qt::green);
QTreeWidgetItem *item = mainWin->getFromHash(m_console);
- item->setForeground(0, greenBrush);
+ if (item) {
+ item->setForeground(0, greenBrush);
+ }
}
jcr->dir_bsock = m_sock;
if (!authenticate_director(jcr, m_console->m_dir, cons, buf, sizeof(buf))) {
m_console->display_text(buf);
- if (mainWin->m_connDebug)
- Pmsg1(000, "DirComm %i BAILING Connection failed\n", m_conn);
+ if (mainWin->m_connDebug) {
+ Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
+ }
goto bail_out;
}
mainWin->set_status(_("Initializing ..."));
-#ifndef HAVE_WIN32
- /* Set up input notifier */
+ /*
+ * Set up input notifier
+ */
m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
- QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
-#endif
+ QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(notify_read_dir(int)));
+ m_notifier->setEnabled(true);
+ m_notify = true;
write(".api 1");
m_api_set = true;
mainWin->set_status(_("Connected"));
- if (mainWin->m_connDebug)
- Pmsg1(000, "Returning TRUE from DirComm->connect_dir : %i\n", m_conn);
+ if (mainWin->m_connDebug) {
+ Pmsg2(000, "Returning TRUE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
+ }
return true;
bail_out:
- if (mainWin->m_connDebug)
- Pmsg1(000, "Returning FALSE from DirComm->connect_dir : %i\n", m_conn);
+ if (mainWin->m_connDebug) {
+ Pmsg2(000, "Returning FALSE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
+ }
delete jcr;
return false;
}
-/*
- * This should be moved into a bSocket class
+/*
+ * This should be moved into a bSocket class
*/
char *DirComm::msg()
{
m_sock->msglen = pm_strcpy(m_sock->msg, msg);
m_at_prompt = false;
m_at_main_prompt = false;
- if (mainWin->m_commDebug) Pmsg1(000, "send: %s\n", msg);
+ if (mainWin->m_commDebug) Pmsg2(000, "conn %i send: %s\n", m_conn, msg);
+ /*
+ * Ensure we send only one blank line. Multiple blank lines are
+ * simply discarded, it keeps the console output looking nicer.
+ */
+ if (m_sock->msglen == 0 || (m_sock->msglen == 1 && *m_sock->msg == '\n')) {
+ if (!m_sent_blank) {
+ m_sent_blank = true;
+ return m_sock->send();
+ } else {
+ return -1; /* discard multiple blanks */
+ }
+ }
+ m_sent_blank = false; /* clear flag */
return m_sock->send();
}
return stat;
}
-/*
+/*
* Blocking read from director
*/
int DirComm::read()
{
- int stat = 0;
+ int stat = -1;
+
+ if (!m_sock) {
+ return -1;
+ }
while (m_sock) {
for (;;) {
+ if (!m_sock) break;
stat = m_sock->wait_data_intr(0, 50000);
if (stat > 0) {
break;
- }
+ }
app->processEvents();
if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
- m_console->write_dir(m_conn, ".messages");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i process_events\n", m_conn);
m_console->messagesPending(false);
+ m_console->write_dir(m_conn, ".messages", false);
}
}
+ if (!m_sock) {
+ return -1;
+ }
m_sock->msg[0] = 0;
stat = sock_read();
if (stat >= 0) {
- if (mainWin->m_commDebug) Pmsg1(000, "got: %s\n", m_sock->msg);
+ if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
if (m_at_prompt) {
m_console->display_text("\n");
m_at_prompt = false;
switch (m_sock->msglen) {
case BNET_MSGS_PENDING :
if (is_notify_enabled() && m_console->hasFocus()) {
- if (mainWin->m_commDebug) Pmsg0(000, "MSGS PENDING\n");
- m_console->write_dir(m_conn, ".messages");
- m_console->displayToPrompt(m_conn);
m_console->messagesPending(false);
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
+ m_console->write_dir(m_conn, ".messages", false);
+ m_console->displayToPrompt(m_conn);
+ continue;
}
m_console->messagesPending(true);
continue;
case BNET_CMD_OK:
- if (mainWin->m_commDebug) Pmsg0(000, "CMD OK\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
m_at_prompt = false;
m_at_main_prompt = false;
-// Pmsg1(000, "before dec m_in_command=%d\n", m_in_command);
if (--m_in_command < 0) {
-// Pmsg0(000, "m_in_command < 0\n");
m_in_command = 0;
}
mainWin->set_status(_("Command completed ..."));
continue;
case BNET_CMD_BEGIN:
- if (mainWin->m_commDebug) Pmsg0(000, "CMD BEGIN\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
m_at_prompt = false;
m_at_main_prompt = false;
m_in_command++;
-// Pmsg1(000, "after inc m_in_command=%d\n", m_in_command);
mainWin->set_status(_("Processing command ..."));
continue;
case BNET_MAIN_PROMPT:
- if (mainWin->m_commDebug) Pmsg0(000, "MAIN PROMPT\n");
- m_at_prompt = true;
- m_at_main_prompt = true;
- mainWin->set_status(_("At main prompt waiting for input ..."));
- QApplication::restoreOverrideCursor();
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
+ if (!m_at_prompt && ! m_at_main_prompt) {
+ m_at_prompt = true;
+ m_at_main_prompt = true;
+ mainWin->set_status(_("At main prompt waiting for input ..."));
+ }
break;
- case BNET_PROMPT:
- if (mainWin->m_commDebug) Pmsg0(000, "PROMPT\n");
+ case BNET_SUB_PROMPT:
+ if (mainWin->m_commDebug) Pmsg2(000, "conn %i SUB_PROMPT m_in_select=%d\n", m_conn, m_in_select);
m_at_prompt = true;
m_at_main_prompt = false;
mainWin->set_status(_("At prompt waiting for input ..."));
- QApplication::restoreOverrideCursor();
+ break;
+ case BNET_TEXT_INPUT:
+ if (mainWin->m_commDebug) Pmsg4(000, "conn %i TEXT_INPUT at_prompt=%d m_in_select=%d notify=%d\n",
+ m_conn, m_at_prompt, m_in_select, is_notify_enabled());
+ if (!m_in_select && is_notify_enabled()) {
+ new textInputDialog(m_console, m_conn);
+ if (mainWin->m_commDebug) Pmsg0(000, "!m_in_select && is_notify_enabled\n");
+ m_at_prompt = true;
+ m_at_main_prompt = false;
+ mainWin->set_status(_("At prompt waiting for input ..."));
+ }
break;
case BNET_CMD_FAILED:
- if (mainWin->m_commDebug) Pmsg0(000, "CMD FAILED\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
if (--m_in_command < 0) {
- Pmsg0(000, "m_in_command < 0\n");
m_in_command = 0;
}
mainWin->set_status(_("Command failed."));
- QApplication::restoreOverrideCursor();
break;
/* We should not get this one */
case BNET_EOD:
- if (mainWin->m_commDebug) Pmsg0(000, "EOD\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
mainWin->set_status_ready();
- QApplication::restoreOverrideCursor();
if (!m_api_set) {
break;
}
continue;
case BNET_START_SELECT:
- if (mainWin->m_commDebug) Pmsg0(000, "START SELECT\n");
- new selectDialog(m_console);
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
+ m_in_select = true;
+ new selectDialog(m_console, m_conn);
+ m_in_select = false;
break;
case BNET_YESNO:
- if (mainWin->m_commDebug) Pmsg0(000, "YESNO\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
new yesnoPopUp(m_console, m_conn);
break;
case BNET_RUN_CMD:
- if (mainWin->m_commDebug) Pmsg0(000, "RUN CMD\n");
- new runCmdPage();
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
+ new runCmdPage(m_conn);
break;
case BNET_START_RTREE:
- if (mainWin->m_commDebug) Pmsg0(000, "START RTREE CMD\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
new restorePage(m_conn);
break;
case BNET_END_RTREE:
- if (mainWin->m_commDebug) Pmsg0(000, "END RTREE CMD\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
break;
case BNET_ERROR_MSG:
- if (mainWin->m_commDebug) Pmsg0(000, "ERROR MSG\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
stat = sock_read(); /* get the message */
m_console->display_text(msg());
QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
+ m_console->beginNewCommand(m_conn);
+ mainWin->waitExit();
break;
case BNET_WARNING_MSG:
- if (mainWin->m_commDebug) Pmsg0(000, "WARNING MSG\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
stat = sock_read(); /* get the message */
- m_console->display_text(msg());
- QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
+ if (!m_console->m_warningPrevent) {
+ QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
+ }
break;
case BNET_INFO_MSG:
- if (mainWin->m_commDebug) Pmsg0(000, "INFO MSG\n");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
stat = sock_read(); /* get the message */
m_console->display_text(msg());
mainWin->set_status(msg());
break;
}
- if (is_bnet_stop(m_sock)) { /* error or term request */
- if (mainWin->m_commDebug) Pmsg0(000, "BNET STOP\n");
+
+ if (!m_sock) {
+ stat = BNET_HARDEOF;
+ return stat;
+ }
+ if (m_sock->is_stop()) { /* error or term request */
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
m_console->stopTimer();
- m_sock->close();
- m_sock = NULL;
+ free_bsock(m_sock);
mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
QBrush redBrush(Qt::red);
QTreeWidgetItem *item = mainWin->getFromHash(m_console);
m_notifier->setEnabled(false);
delete m_notifier;
m_notifier = NULL;
+ m_notify = false;
}
mainWin->set_status(_("Director disconnected."));
- QApplication::restoreOverrideCursor();
stat = BNET_HARDEOF;
}
break;
- }
+ }
return stat;
}
/* Called by signal when the Director has output for us */
-void DirComm::read_dir(int /* fd */)
+void DirComm::notify_read_dir(int /* fd */)
{
- if (mainWin->m_commDebug) Pmsg0(000, "read_dir\n");
- while (read() >= 0) {
- m_console->display_text(msg());
+ int stat;
+ if (!mainWin->m_notify) {
+ return;
}
+ if (mainWin->m_commDebug) Pmsg1(000, "enter read_dir conn %i read_dir\n", m_conn);
+ stat = m_sock->wait_data(0, 5000);
+ if (stat > 0) {
+ if (mainWin->m_commDebug) Pmsg2(000, "read_dir conn %i stat=%d\n", m_conn, stat);
+ while (read() >= 0) {
+ m_console->display_text(msg());
+ }
+ }
+ if (mainWin->m_commDebug) Pmsg2(000, "exit read_dir conn %i stat=%d\n", m_conn, stat);
}
/*
* When the notifier is enabled, read_dir() will automatically be
- * called by the Qt event loop when ever there is any output
- * from the Directory, and read_dir() will then display it on
+ * called by the Qt event loop when ever there is any output
+ * from the Director, and read_dir() will then display it on
* the console.
*
* When we are in a bat dialog, we want to control *all* output
* from the Directory, so we set notify to off.
- * m_console->notifiy(false);
+ * m_console->notify(false);
*/
-bool DirComm::notify(bool enable)
-{
+bool DirComm::notify(bool enable)
+{
bool prev_enabled = false;
+ /* Set global flag */
+ mainWin->m_notify = enable;
if (m_notifier) {
- prev_enabled = m_notifier->isEnabled();
+ prev_enabled = m_notifier->isEnabled();
m_notifier->setEnabled(enable);
- if (mainWin->m_connDebug)
- if (prev_enabled && !enable)
- Pmsg1(000, "m_notifier Disabling notifier: %i\n", m_conn);
- else if (!prev_enabled && enable)
- Pmsg1(000, "m_notifier Enabling notifier: %i\n", m_conn);
- } else if (mainWin->m_connDebug)
- Pmsg1(000, "m_notifier does not exist: %i\n", m_conn);
+ m_notify = enable;
+ if (mainWin->m_connDebug) Pmsg3(000, "conn=%i set_notify=%d prev=%d\n", m_conn, enable, prev_enabled);
+ } else if (mainWin->m_connDebug) {
+ Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
+ }
return prev_enabled;
}
bool DirComm::is_notify_enabled() const
{
- bool enabled = false;
- if (m_notifier)
- enabled = m_notifier->isEnabled();
- return enabled;
+ return m_notify;
}
/*
* 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)
(void)size;
(void)userdata;
#ifdef HAVE_TLS
- const char *prompt = (const char *)userdata;
# if defined(HAVE_WIN32)
//sendit(prompt);
if (win32_cgets(buf, size) == NULL) {
return strlen(buf);
}
# else
+ const char *prompt = (const char *)userdata;
char *passwd;
passwd = getpass(prompt);