/*
Bacula® - The Network Backup Solution
- Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
+ Copyright (C) 2007-2010 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.
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
- Bacula® is a registered trademark of John Walker.
+ 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$
+ * DirComm, Director communications,class
*
- * Bacula Communications class that is at a higher level than BSOCK
- *
- * Kern Sibbald, May MMVII
+ * Kern Sibbald, January MMVII
*
*/
#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_conn = conn;
+ m_in_command = 0;
+ m_in_select = 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();
+ if (m_notifier) {
+ m_notifier->setEnabled(false);
+ delete m_notifier;
+ m_notifier = NULL;
+ }
+ if (mainWin->m_connDebug)
+ Pmsg2(000, "DirComm %i terminating connections %s\n", m_conn, m_console->m_dir->name());
m_sock->close();
m_sock = NULL;
}
/*
* 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;
+
+ buf[0] = 0;
- m_dir = dir;
- if (!m_dir) {
- mainWin->set_status( tr("No Director found.") );
- return;
- }
if (m_sock) {
- mainWin->set_status( tr("Already connected.") );
- return;
+ 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 */
+ cons = (CONRES *)GetNextRes(R_CONSOLE, NULL);
+ UnlockRes();
+
/* Initialize Console TLS context once */
if (cons && !cons->tls_ctx && (cons->tls_enable || cons->tls_require)) {
/* Generate passphrase prompt */
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());
+ 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 {
}
m_sock = bnet_connect(NULL, 5, 15, heart_beat,
- _("Director daemon"), m_dir->address,
- NULL, m_dir->DIRport, 0);
+ _("Director daemon"), m_console->m_dir->address,
+ NULL, m_console->m_dir->DIRport, 0);
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);
+ QTreeWidgetItem *item = mainWin->getFromHash(m_console);
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);
}
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)));
+ m_notifier->setEnabled(false);
- mainWin->set_status(_("Connected"));
- startTimer(); /* start message timer */
- return;
-}
-
-bool DirComm::dir_cmd(QString &cmd, QStringList &results)
-{
- return dir_cmd(cmd.toUtf8().data(), results);
-}
-
-/*
- * Send a command to the Director, and return the
- * results in a QStringList.
- */
-bool DirComm::dir_cmd(const char *cmd, QStringList &results)
-{
- int stat;
-
- 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 */
-}
+ write(".api 1");
+ m_api_set = true;
+ m_console->displayToPrompt(m_conn);
-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;
}
/*
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());
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);
return m_sock->send();
-
}
-/*
- * Get to main command prompt -- i.e. abort any suDirCommand
- */
-void DirComm::beginNewCommand()
+int DirComm::sock_read()
{
- 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;
- }
- }
- m_console->display_text("\n");
-}
-
-void DirComm::displayToPrompt()
-{
- int stat = 0;
- QString buf;
- if (mainWin->m_commDebug) Pmsg0(000, "DisplaytoPrompt\n");
- while (!m_at_prompt) {
- if ((stat=read()) > 0) {
- buf += msg();
- if (buf.size() >= 8196 || m_messages_pending) {
- m_console->display_text(buf);
- buf.clear();
- m_messages_pending = false;
- }
- }
- }
- m_console->display_text(buf);
- if (mainWin->m_commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
-}
-
-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 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()) {
+ m_console->write_dir(m_conn, ".messages", false);
+ m_console->messagesPending(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;
}
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()) {
+ 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);
+ m_console->messagesPending(false);
}
- 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");
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
m_at_prompt = true;
m_at_main_prompt = true;
mainWin->set_status(_("At main prompt waiting for input ..."));
- QApplication::restoreOverrideCursor();
break;
case BNET_PROMPT:
- if (mainWin->m_commDebug) Pmsg0(000, "PROMPT\n");
+ if (mainWin->m_commDebug) Pmsg2(000, "conn %i PROMPT m_in_select %i\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();
+ /***** FIXME *****/
+ /* commented out until the prompt communication issue with the director is resolved
+ * This is where I call a new text input dialog class to prevent the connection issues
+ * when a text input is requited.
+ * if (!m_in_select) {
+ * new textInputDialog(m_console, m_conn);
+ * }
+ */
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);
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();
+ if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
+ m_console->stopTimer();
m_sock->close();
m_sock = NULL;
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;
+ }
mainWin->set_status(_("Director disconnected."));
- QApplication::restoreOverrideCursor();
stat = BNET_HARDEOF;
}
break;
/* Called by signal when the Director has output for us */
void DirComm::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_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);
}
/*
* from the Directory, so we set notify to off.
* m_console->notifiy(false);
*/
-void DirComm::notify(bool enable)
+bool DirComm::notify(bool enable)
{
- m_notifier->setEnabled(enable);
+ bool prev_enabled = false;
+ if (m_notifier) {
+ prev_enabled = m_notifier->isEnabled();
+ m_notifier->setEnabled(enable);
+ if (mainWin->m_connDebug) {
+ Pmsg3(000, "conn=%i 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;
+ bool enabled = false;
+ if (m_notifier) {
+ enabled = m_notifier->isEnabled();
}
+ return enabled;
}
/*
(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);