2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
33 * Kern Sibbald, January MMVII
41 #include "textinput.h"
44 static int tls_pem_callback(char *buf, int size, const void *userdata);
46 DirComm::DirComm(Console *parent, int conn): m_notifier(NULL), m_api_set(false)
51 m_at_main_prompt = false;
61 /* Terminate any open socket */
62 void DirComm::terminate()
66 m_notifier->setEnabled(false);
70 if (mainWin->m_connDebug)
71 Pmsg2(000, "DirComm %i terminating connections %s\n", m_conn, m_console->m_dir->name());
78 * Connect to Director.
80 bool DirComm::connect_dir()
90 mainWin->set_status( tr("Already connected."));
91 m_console->display_textf(_("Already connected\"%s\".\n"),
92 m_console->m_dir->name());
93 if (mainWin->m_connDebug)
94 Pmsg2(000, "DirComm %i BAILING already connected %s\n", m_conn, m_console->m_dir->name());
98 memset(jcr, 0, sizeof(JCR));
100 mainWin->set_statusf(_("Connecting to Director %s:%d"), m_console->m_dir->address, m_console->m_dir->DIRport);
102 m_console->display_textf(_("Connecting to Director %s:%d\n\n"), m_console->m_dir->address, m_console->m_dir->DIRport);
105 /* Give GUI a chance */
106 app->processEvents();
109 /* If cons==NULL, default console will be used */
110 cons = (CONRES *)GetNextRes(R_CONSOLE, NULL);
113 /* Initialize Console TLS context once */
114 if (cons && !cons->tls_ctx && (cons->tls_enable || cons->tls_require)) {
115 /* Generate passphrase prompt */
116 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ",
119 /* Initialize TLS context:
120 * Args: CA certfile, CA certdir, Certfile, Keyfile,
121 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
123 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
124 cons->tls_ca_certdir, cons->tls_certfile,
125 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
127 if (!cons->tls_ctx) {
128 m_console->display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
129 m_console->m_dir->name());
130 if (mainWin->m_connDebug)
131 Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Console %s\n", m_conn, m_console->m_dir->name());
136 /* Initialize Director TLS context once */
137 if (!m_console->m_dir->tls_ctx && (m_console->m_dir->tls_enable || m_console->m_dir->tls_require)) {
138 /* Generate passphrase prompt */
139 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
140 m_console->m_dir->name());
142 /* Initialize TLS context:
143 * Args: CA certfile, CA certdir, Certfile, Keyfile,
144 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
145 m_console->m_dir->tls_ctx = new_tls_context(m_console->m_dir->tls_ca_certfile,
146 m_console->m_dir->tls_ca_certdir, m_console->m_dir->tls_certfile,
147 m_console->m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
149 if (!m_console->m_dir->tls_ctx) {
150 m_console->display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
151 m_console->m_dir->name());
152 mainWin->set_status("Connection failed");
153 if (mainWin->m_connDebug)
154 Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Director %s\n", m_conn, m_console->m_dir->name());
159 if (m_console->m_dir->heartbeat_interval) {
160 heart_beat = m_console->m_dir->heartbeat_interval;
162 heart_beat = cons->heartbeat_interval;
167 m_sock = bnet_connect(NULL, 5, 15, heart_beat,
168 _("Director daemon"), m_console->m_dir->address,
169 NULL, m_console->m_dir->DIRport, 0);
170 if (m_sock == NULL) {
171 mainWin->set_status("Connection failed");
172 if (mainWin->m_connDebug)
173 Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
176 /* Update page selector to green to indicate that Console is connected */
177 mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
178 QBrush greenBrush(Qt::green);
179 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
180 item->setForeground(0, greenBrush);
183 jcr->dir_bsock = m_sock;
185 if (!authenticate_director(jcr, m_console->m_dir, cons, buf, sizeof(buf))) {
186 m_console->display_text(buf);
187 if (mainWin->m_connDebug)
188 Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
193 m_console->display_text(buf);
196 /* Give GUI a chance */
197 app->processEvents();
199 mainWin->set_status(_("Initializing ..."));
202 /* Set up input notifier */
203 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
204 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
209 m_console->displayToPrompt(m_conn);
211 m_console->beginNewCommand(m_conn);
213 mainWin->set_status(_("Connected"));
215 if (mainWin->m_connDebug)
216 Pmsg2(000, "Returning TRUE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
220 if (mainWin->m_connDebug)
221 Pmsg2(000, "Returning FALSE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
227 * This should be moved into a bSocket class
237 int DirComm::write(const QString msg)
239 return write(msg.toUtf8().data());
242 int DirComm::write(const char *msg)
247 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
249 m_at_main_prompt = false;
250 if (mainWin->m_commDebug) Pmsg2(000, "conn %i send: %s\n", m_conn, msg);
251 return m_sock->send();
254 int DirComm::sock_read()
258 bool wasEnabled = notify(false);
259 stat = m_sock->recv();
262 stat = m_sock->recv();
268 * Blocking read from director
280 stat = m_sock->wait_data_intr(0, 50000);
284 app->processEvents();
285 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
286 m_console->write_dir(m_conn, ".messages", false);
287 m_console->messagesPending(false);
296 if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
298 m_console->display_text("\n");
300 m_at_main_prompt = false;
303 switch (m_sock->msglen) {
304 case BNET_MSGS_PENDING :
305 if (is_notify_enabled() && m_console->hasFocus()) {
306 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
307 m_console->write_dir(m_conn, ".messages", false);
308 m_console->displayToPrompt(m_conn);
309 m_console->messagesPending(false);
311 m_console->messagesPending(true);
314 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
316 m_at_main_prompt = false;
317 // Pmsg1(000, "before dec m_in_command=%d\n", m_in_command);
318 if (--m_in_command < 0) {
319 // Pmsg0(000, "m_in_command < 0\n");
322 mainWin->set_status(_("Command completed ..."));
325 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
327 m_at_main_prompt = false;
329 // Pmsg1(000, "after inc m_in_command=%d\n", m_in_command);
330 mainWin->set_status(_("Processing command ..."));
332 case BNET_MAIN_PROMPT:
333 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
335 m_at_main_prompt = true;
336 mainWin->set_status(_("At main prompt waiting for input ..."));
339 if (mainWin->m_commDebug) Pmsg2(000, "conn %i PROMPT m_in_select %i\n", m_conn, m_in_select);
341 m_at_main_prompt = false;
342 mainWin->set_status(_("At prompt waiting for input ..."));
343 /* commented out until the prompt communication issue with the director is resolved
344 This is where I call a new text input dialog class to prevent the connection issues
345 when a text input is requited.
347 new textInputDialog(m_console, m_conn);
350 case BNET_CMD_FAILED:
351 if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
352 if (--m_in_command < 0) {
355 mainWin->set_status(_("Command failed."));
357 /* We should not get this one */
359 if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
360 mainWin->set_status_ready();
365 case BNET_START_SELECT:
367 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
369 new selectDialog(m_console, m_conn);
373 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
374 new yesnoPopUp(m_console, m_conn);
377 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
378 new runCmdPage(m_conn);
380 case BNET_START_RTREE:
381 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
382 new restorePage(m_conn);
385 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
388 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
389 stat = sock_read(); /* get the message */
390 m_console->display_text(msg());
391 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
393 case BNET_WARNING_MSG:
394 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
395 stat = sock_read(); /* get the message */
396 if (!m_console->m_warningPrevent) {
397 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
401 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
402 stat = sock_read(); /* get the message */
403 m_console->display_text(msg());
404 mainWin->set_status(msg());
407 if (is_bnet_stop(m_sock)) { /* error or term request */
408 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
409 m_console->stopTimer();
412 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
413 QBrush redBrush(Qt::red);
414 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
415 item->setForeground(0, redBrush);
417 m_notifier->setEnabled(false);
421 mainWin->set_status(_("Director disconnected."));
422 // QApplication::restoreOverrideCursor();
430 /* Called by signal when the Director has output for us */
431 void DirComm::read_dir(int /* fd */)
433 if (mainWin->m_commDebug) Pmsg1(000, "conn %i read_dir\n", m_conn);
434 while (read() >= 0) {
435 m_console->display_text(msg());
440 * When the notifier is enabled, read_dir() will automatically be
441 * called by the Qt event loop when ever there is any output
442 * from the Directory, and read_dir() will then display it on
445 * When we are in a bat dialog, we want to control *all* output
446 * from the Directory, so we set notify to off.
447 * m_console->notifiy(false);
449 bool DirComm::notify(bool enable)
451 bool prev_enabled = false;
453 prev_enabled = m_notifier->isEnabled();
454 if (prev_enabled != enable) {
455 m_notifier->setEnabled(enable);
457 if (mainWin->m_connDebug) {
458 if (prev_enabled && !enable)
459 Pmsg2(000, "m_notifier Disabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
460 else if (!prev_enabled && enable)
461 Pmsg2(000, "m_notifier Enabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
463 } else if (mainWin->m_connDebug)
464 Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
468 bool DirComm::is_notify_enabled() const
470 bool enabled = false;
472 enabled = m_notifier->isEnabled();
474 /* TODO: Windows doesn't support notifier without some kludge
475 * This cheat seems to work, waiting for a cleaner solution.
485 * Call-back for reading a passphrase for an encrypted PEM file
486 * This function uses getpass(),
487 * which uses a static buffer and is NOT thread-safe.
489 static int tls_pem_callback(char *buf, int size, const void *userdata)
494 const char *prompt = (const char *)userdata;
495 # if defined(HAVE_WIN32)
497 if (win32_cgets(buf, size) == NULL) {
506 passwd = getpass(prompt);
507 bstrncpy(buf, passwd, size);