]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/qt-console/bcomm/dircomm.cpp
Fix invalid .mod command in BAT during restore
[bacula/bacula] / bacula / src / qt-console / bcomm / dircomm.cpp
index 88fd594af125bda16d475c5bc37ab9418f066835..33500b00c81e9e9005cd4711d5c749af5bc0ed81 100644 (file)
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2007-2007 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 John Walker.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
+   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.
+
+   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.
 */
 /*
- *   Version $Id$
+ *  DirComm, Director communications,class
  *
- *  Bacula Communications class that is at a higher level than BSOCK
+ *   Kern Sibbald, January MMVII
  *
- *   Kern Sibbald, May MMVII
- *
- */ 
+ */
 
 #include "bat.h"
 #include "console.h"
 #include "restore.h"
 #include "select.h"
+#include "textinput.h"
 #include "run/run.h"
-#include "dircomm.h"
 
 static int tls_pem_callback(char *buf, int size, const void *userdata);
 
-
-DirComm::DirComm(Console *console)
+DirComm::DirComm(Console *parent, int conn):  m_notifier(NULL),  m_api_set(false)
 {
-   m_console = console;
+   m_console = parent;
    m_sock = NULL;
    m_at_prompt = false;
    m_at_main_prompt = false;
-   m_timer = NULL;
+   m_sent_blank = false;
+   m_conn = conn;
+   m_in_command = 0;
+   m_in_select = false;
+   m_notify = false;
 }
 
 DirComm::~DirComm()
 {
-   if (m_timer) {
-      stopTimer();
-   }
-}
-
-void DirComm::startTimer()
-{
-   m_timer = new QTimer(m_console);
-   QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
-   m_timer->start(mainWin->m_checkMessagesInterval*1000);
-}
-
-void DirComm::stopTimer()
-{
-   if (m_timer) {
-      QWidget::disconnect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
-      m_timer->stop();
-      delete m_timer;
-      m_timer = NULL;
-   }
-}
-      
-void DirComm::poll_messages()
-{
-   m_messages_pending = true;
-   if ((m_at_main_prompt) && (mainWin->m_checkMessages)){
-      write(".messages");
-      displayToPrompt();
-   }
 }
 
 /* Terminate any open socket */
 void DirComm::terminate()
 {
    if (m_sock) {
-      stopTimer();
-      m_sock->close();
-      m_sock = NULL;
+      if (m_notifier) {
+         m_notifier->setEnabled(false);
+         delete m_notifier;
+         m_notifier = NULL;
+         m_notify = false;
+      }
+      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.
  */
-void DirComm::connect_dir(DIRRES *dir, CONRES *cons)
+bool DirComm::connect_dir()
 {
-   JCR jcr;
+   JCR *jcr = new JCR;
    utime_t heart_beat;
    char buf[1024];
+   CONRES *cons;
+   int numcon = 0;
+   int i = 0;
 
-   m_dir = dir;
-   if (!m_dir) {          
-      mainWin->set_status( tr("No Director found.") );
-      return;
+   buf[0] = 0;
+
+   foreach_res(cons, R_CONSOLE) {
+      numcon++;
    }
-   if (m_sock) {
-      mainWin->set_status( tr("Already connected.") );
-      return;
+
+   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) {
+         Pmsg2(000, "DirComm %i BAILING already connected %s\n", m_conn, m_console->m_dir->name());
+      }
+      goto bail_out;
    }
 
-   memset(&jcr, 0, sizeof(jcr));
+   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_dir->address, m_dir->DIRport);
-   m_console->display_textf(_("Connecting to Director %s:%d\n\n"), m_dir->address, m_dir->DIRport);
+   mainWin->set_statusf(_("Connecting to Director %s:%d"), 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 */
+   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,
@@ -139,60 +148,81 @@ void DirComm::connect_dir(DIRRES *dir, CONRES *cons)
 
       if (!cons->tls_ctx) {
          m_console->display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
-            m_dir->name());
-         return;
+            m_console->m_dir->name());
+         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_dir->tls_ctx && (m_dir->tls_enable || m_dir->tls_require)) {
+   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: ", 
-                m_dir->name());
+      bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
+                m_console->m_dir->name());
 
       /* Initialize TLS context:
        * Args: CA certfile, CA certdir, Certfile, Keyfile,
        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
-      m_dir->tls_ctx = new_tls_context(m_dir->tls_ca_certfile,
-                          m_dir->tls_ca_certdir, m_dir->tls_certfile,
-                          m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
+      m_console->m_dir->tls_ctx = new_tls_context(m_console->m_dir->tls_ca_certfile,
+                          m_console->m_dir->tls_ca_certdir, m_console->m_dir->tls_certfile,
+                          m_console->m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
 
-      if (!m_dir->tls_ctx) {
+      if (!m_console->m_dir->tls_ctx) {
          m_console->display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
-            m_dir->name());
+            m_console->m_dir->name());
          mainWin->set_status("Connection failed");
-         return;
+         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;
       }
    }
 
-   if (m_dir->heartbeat_interval) {
-      heart_beat = m_dir->heartbeat_interval;
+   if (m_console->m_dir->heartbeat_interval) {
+      heart_beat = m_console->m_dir->heartbeat_interval;
    } else if (cons) {
       heart_beat = cons->heartbeat_interval;
    } else {
       heart_beat = 0;
-   }        
+   }
 
-   m_sock = bnet_connect(NULL, 5, 15, heart_beat,
-                          _("Director daemon"), m_dir->address,
-                          NULL, m_dir->DIRport, 0);
+   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)) {
+      m_sock->destroy();
+      m_sock = NULL;
+   }
    if (m_sock == NULL) {
-     mainWin->set_status( tr("Connection failed") );
-      return;
+      mainWin->set_status("Connection failed");
+      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(this);
-      item->setForeground(0, greenBrush);
+      QTreeWidgetItem *item = mainWin->getFromHash(m_console);
+      if (item) {
+         item->setForeground(0, greenBrush);
+      }
    }
 
-   jcr.dir_bsock = m_sock;
+   jcr->dir_bsock = m_sock;
 
-   if (!authenticate_director(&jcr, m_dir, cons, buf, sizeof(buf))) {
+   if (!authenticate_director(jcr, m_console->m_dir, cons, buf, sizeof(buf))) {
       m_console->display_text(buf);
-      return;
+      if (mainWin->m_connDebug) {
+         Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
+      }
+      goto bail_out;
    }
+
    if (buf[0]) {
       m_console->display_text(buf);
    }
@@ -202,76 +232,37 @@ void DirComm::connect_dir(DIRRES *dir, CONRES *cons)
 
    mainWin->set_status(_("Initializing ..."));
 
-   /* 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)));
-
-   mainWin->set_status(_("Connected"));
-   startTimer();                      /* start message timer */
-   return;
-}
-
-bool DirComm::dir_cmd(QString &cmd, QStringList &results)
-{
-   return dir_cmd(cmd.toUtf8().data(), results);
-}
+   QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(notify_read_dir(int)));
+   m_notifier->setEnabled(true);
+   m_notify = true;
 
-/*
- * Send a command to the Director, and return the
- *  results in a QStringList.  
- */
-bool DirComm::dir_cmd(const char *cmd, QStringList &results)
-{
-   int stat;
+   write(".api 1");
+   m_api_set = true;
+   m_console->displayToPrompt(m_conn);
 
-   notify(false);
-   write(cmd);
-   while ((stat = read()) > 0) {
-      if (mainWin->m_displayAll) m_console->display_text(msg());
-      strip_trailing_junk(msg());
-      results << msg();
-   }
-   notify(true);
-   discardToPrompt();
-   return true;              /* ***FIXME*** return any command error */
-}
-
-bool DirComm::sql_cmd(QString &query, QStringList &results)
-{
-   return sql_cmd(query.toUtf8().data(), results);
-}
+   m_console->beginNewCommand(m_conn);
 
-/*
- * Send an sql query to the Director, and return the
- *  results in a QStringList.  
- */
-bool DirComm::sql_cmd(const char *query, QStringList &results)
-{
-   int stat;
-   POOL_MEM cmd(PM_MESSAGE);
+   mainWin->set_status(_("Connected"));
 
-   if (!is_connectedGui()) {
-      return false;
+   if (mainWin->m_connDebug) {
+      Pmsg2(000, "Returning TRUE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
    }
+   return true;
 
-   notify(false);
-   
-   pm_strcpy(cmd, ".sql query=\"");
-   pm_strcat(cmd, query);
-   pm_strcat(cmd, "\"");
-   write(cmd.c_str());
-   while ((stat = read()) > 0) {
-      if (mainWin->m_displayAll) m_console->display_text(msg());
-      strip_trailing_junk(msg());
-      results << msg();
+bail_out:
+   if (mainWin->m_connDebug) {
+      Pmsg2(000, "Returning FALSE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
    }
-   notify(true);
-   discardToPrompt();
-   return true;              /* ***FIXME*** return any command error */
+   delete jcr;
+   return false;
 }
 
-/* 
- * This should be moved into a bSocket class 
+/*
+ * This should be moved into a bSocket class
  */
 char *DirComm::msg()
 {
@@ -281,24 +272,6 @@ char *DirComm::msg()
    return NULL;
 }
 
-/* Send a command to the Director */
-void DirComm::write_dir(const char *msg)
-{
-   if (m_sock) {
-      mainWin->set_status(_("Processing command ..."));
-      QApplication::setOverrideCursor(Qt::WaitCursor);
-      write(msg);
-   } else {
-      mainWin->set_status( tr(" Director not connected. Click on connect button.") );
-      mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
-      QBrush redBrush(Qt::red);
-      QTreeWidgetItem *item = mainWin->getFromHash(this);
-      item->setForeground(0, redBrush);
-      m_at_prompt = false;
-      m_at_main_prompt = false;
-   }
-}
-
 int DirComm::write(const QString msg)
 {
    return write(msg.toUtf8().data());
@@ -312,75 +285,67 @@ int DirComm::write(const char *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);
-   return m_sock->send();
-
-}
-
-/*
- * Get to main command prompt -- i.e. abort any suDirCommand
- */
-void DirComm::beginNewCommand()
-{
-   for (int i=0; i < 3; i++) {
-      write(".\n");
-      while (read() > 0) {
-         if (mainWin->m_displayAll) m_console->display_text(msg());
-      }
-      if (m_at_main_prompt) {
-         break;
+   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_console->display_text("\n");
-}
-
-void DirComm::displayToPrompt()
-{ 
-   int stat = 0;
-   if (mainWin->m_commDebug) Pmsg0(000, "DisplaytoPrompt\n");
-   while (!m_at_prompt) {
-      if ((stat=read()) > 0) {
-         m_console->display_text(msg());
-      }
-   }
-   if (mainWin->m_commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
+   m_sent_blank = false;        /* clear flag */
+   return m_sock->send();
 }
 
-void DirComm::discardToPrompt()
-{ 
-   int stat = 0;
-   if (mainWin->m_commDebug) Pmsg0(000, "discardToPrompt\n");
-   while (!m_at_prompt) {
-      if ((stat=read()) > 0) {
-         if (mainWin->m_displayAll) m_console->display_text(msg());
-      }
-   }
-   if (mainWin->m_commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
+int DirComm::sock_read()
+{
+   int stat;
+#ifdef HAVE_WIN32
+   bool wasEnabled = notify(false);
+   stat = m_sock->recv();
+   notify(wasEnabled);
+#else
+   stat = m_sock->recv();
+#endif
+   return stat;
 }
 
-
-/* 
+/*
  * Blocking read from director
  */
 int DirComm::read()
 {
-   int stat = 0;
+   int stat = -1;
+
+   if (!m_sock) {
+      return -1;
+   }
    while (m_sock) {
       for (;;) {
-         stat = bnet_wait_data_intr(m_sock, 1);
+         if (!m_sock) break;
+         stat = m_sock->wait_data_intr(0, 50000);
          if (stat > 0) {
             break;
-         } 
+         }
          app->processEvents();
-         if (m_api_set && m_messages_pending) {
-            write_dir(".messages");
-            m_messages_pending = false;
+         if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
+            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 = m_sock->recv();
+      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;
@@ -389,145 +354,193 @@ int DirComm::read()
       }
       switch (m_sock->msglen) {
       case BNET_MSGS_PENDING :
-         if (m_notifier->isEnabled()) {
-            if (mainWin->m_commDebug) Pmsg0(000, "MSGS PENDING\n");
-            write_dir(".messages");
-            displayToPrompt();
-            m_messages_pending = false;
+         if (is_notify_enabled() && m_console->hasFocus()) {
+            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_messages_pending = true;
+         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;
+         if (--m_in_command < 0) {
+            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++;
          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) {
+            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");
-         new yesnoPopUp(m_console);
+         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) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
+         new restorePage(m_conn);
+         break;
+      case BNET_END_RTREE:
+         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");
-         m_sock->recv();              /* get the message */
+         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(this, "Error", msg(), QMessageBox::Ok);
+         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");
-         m_sock->recv();              /* get the message */
-         m_console->display_text(msg());
-         QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
+         if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
+         stat = sock_read();          /* get the message */
+         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");
-         m_sock->recv();              /* get the message */
+         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");
-         stopTimer();
-         m_sock->close();
-         m_sock = NULL;
+
+      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();
+         free_bsock(m_sock);
          mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
          QBrush redBrush(Qt::red);
-         QTreeWidgetItem *item = mainWin->getFromHash(this);
+         QTreeWidgetItem *item = mainWin->getFromHash(m_console);
          item->setForeground(0, redBrush);
-         m_notifier->setEnabled(false);
-         delete m_notifier;
-         m_notifier = NULL;
+         if (m_notifier) {
+            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);
  */
-void DirComm::notify(bool enable) 
-{ 
-   m_notifier->setEnabled(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();
+      m_notifier->setEnabled(enable);
+      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_connectedGui()
+bool DirComm::is_notify_enabled() const
 {
-   if (is_connected()) {
-      return true;
-   } else {
-     QString message( tr("Director is currently disconnected\n  Please reconnect!"));
-     QMessageBox::warning(this, "Bat",
-         message.toUtf8().data(), QMessageBox::Ok );
-      return false;
-   }
+   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)
@@ -535,9 +548,8 @@ 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);
+   //sendit(prompt);
    if (win32_cgets(buf, size) == NULL) {
       buf[0] = 0;
       return 0;
@@ -545,6 +557,7 @@ static int tls_pem_callback(char *buf, int size, const void *userdata)
       return strlen(buf);
    }
 # else
+   const char *prompt = (const char *)userdata;
    char *passwd;
 
    passwd = getpass(prompt);