2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2010 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 three of the GNU Affero 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 Affero 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.
29 * DirComm, Director communications,class
31 * Kern Sibbald, January MMVII
39 #include "textinput.h"
42 static int tls_pem_callback(char *buf, int size, const void *userdata);
44 DirComm::DirComm(Console *parent, int conn): m_notifier(NULL), m_api_set(false)
49 m_at_main_prompt = false;
59 /* Terminate any open socket */
60 void DirComm::terminate()
64 m_notifier->setEnabled(false);
68 if (mainWin->m_connDebug)
69 Pmsg2(000, "DirComm %i terminating connections %s\n", m_conn, m_console->m_dir->name());
76 * Connect to Director.
78 bool DirComm::connect_dir()
88 mainWin->set_status( tr("Already connected."));
89 m_console->display_textf(_("Already connected\"%s\".\n"),
90 m_console->m_dir->name());
91 if (mainWin->m_connDebug) {
92 Pmsg2(000, "DirComm %i BAILING already connected %s\n", m_conn, m_console->m_dir->name());
97 if (mainWin->m_connDebug) {
98 Pmsg2(000, "DirComm %i connecting %s\n", m_conn, m_console->m_dir->name());
100 memset(jcr, 0, sizeof(JCR));
102 mainWin->set_statusf(_("Connecting to Director %s:%d"), m_console->m_dir->address, m_console->m_dir->DIRport);
104 m_console->display_textf(_("Connecting to Director %s:%d\n\n"), m_console->m_dir->address, m_console->m_dir->DIRport);
107 /* Give GUI a chance */
108 app->processEvents();
111 /* If cons==NULL, default console will be used */
112 cons = (CONRES *)GetNextRes(R_CONSOLE, NULL);
115 /* Initialize Console TLS context once */
116 if (cons && !cons->tls_ctx && (cons->tls_enable || cons->tls_require)) {
117 /* Generate passphrase prompt */
118 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ",
121 /* Initialize TLS context:
122 * Args: CA certfile, CA certdir, Certfile, Keyfile,
123 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
125 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
126 cons->tls_ca_certdir, cons->tls_certfile,
127 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
129 if (!cons->tls_ctx) {
130 m_console->display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
131 m_console->m_dir->name());
132 if (mainWin->m_connDebug)
133 Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Console %s\n", m_conn, m_console->m_dir->name());
138 /* Initialize Director TLS context once */
139 if (!m_console->m_dir->tls_ctx && (m_console->m_dir->tls_enable || m_console->m_dir->tls_require)) {
140 /* Generate passphrase prompt */
141 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
142 m_console->m_dir->name());
144 /* Initialize TLS context:
145 * Args: CA certfile, CA certdir, Certfile, Keyfile,
146 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
147 m_console->m_dir->tls_ctx = new_tls_context(m_console->m_dir->tls_ca_certfile,
148 m_console->m_dir->tls_ca_certdir, m_console->m_dir->tls_certfile,
149 m_console->m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
151 if (!m_console->m_dir->tls_ctx) {
152 m_console->display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
153 m_console->m_dir->name());
154 mainWin->set_status("Connection failed");
155 if (mainWin->m_connDebug)
156 Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Director %s\n", m_conn, m_console->m_dir->name());
161 if (m_console->m_dir->heartbeat_interval) {
162 heart_beat = m_console->m_dir->heartbeat_interval;
164 heart_beat = cons->heartbeat_interval;
169 m_sock = bnet_connect(NULL, 5, 15, heart_beat,
170 _("Director daemon"), m_console->m_dir->address,
171 NULL, m_console->m_dir->DIRport, 0);
172 if (m_sock == NULL) {
173 mainWin->set_status("Connection failed");
174 if (mainWin->m_connDebug)
175 Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
178 /* Update page selector to green to indicate that Console is connected */
179 mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
180 QBrush greenBrush(Qt::green);
181 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
183 item->setForeground(0, greenBrush);
187 jcr->dir_bsock = m_sock;
189 if (!authenticate_director(jcr, m_console->m_dir, cons, buf, sizeof(buf))) {
190 m_console->display_text(buf);
191 if (mainWin->m_connDebug)
192 Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
197 m_console->display_text(buf);
200 /* Give GUI a chance */
201 app->processEvents();
203 mainWin->set_status(_("Initializing ..."));
206 * Set up input notifier
208 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
209 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
210 m_notifier->setEnabled(false);
214 m_console->displayToPrompt(m_conn);
216 m_console->beginNewCommand(m_conn);
218 mainWin->set_status(_("Connected"));
220 if (mainWin->m_connDebug) {
221 Pmsg2(000, "Returning TRUE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
226 if (mainWin->m_connDebug) {
227 Pmsg2(000, "Returning FALSE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
234 * This should be moved into a bSocket class
244 int DirComm::write(const QString msg)
246 return write(msg.toUtf8().data());
249 int DirComm::write(const char *msg)
254 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
256 m_at_main_prompt = false;
257 if (mainWin->m_commDebug) Pmsg2(000, "conn %i send: %s\n", m_conn, msg);
258 return m_sock->send();
261 int DirComm::sock_read()
265 bool wasEnabled = notify(false);
266 stat = m_sock->recv();
269 stat = m_sock->recv();
275 * Blocking read from director
287 stat = m_sock->wait_data_intr(0, 50000);
291 app->processEvents();
292 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
293 m_console->write_dir(m_conn, ".messages", false);
294 m_console->messagesPending(false);
303 if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
305 m_console->display_text("\n");
307 m_at_main_prompt = false;
310 switch (m_sock->msglen) {
311 case BNET_MSGS_PENDING :
312 if (is_notify_enabled() && m_console->hasFocus()) {
313 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
314 m_console->write_dir(m_conn, ".messages", false);
315 m_console->displayToPrompt(m_conn);
316 m_console->messagesPending(false);
318 m_console->messagesPending(true);
321 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
323 m_at_main_prompt = false;
324 if (--m_in_command < 0) {
327 mainWin->set_status(_("Command completed ..."));
330 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
332 m_at_main_prompt = false;
334 mainWin->set_status(_("Processing command ..."));
336 case BNET_MAIN_PROMPT:
337 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
338 if (!m_at_prompt && ! m_at_main_prompt) {
340 m_at_main_prompt = true;
341 mainWin->set_status(_("At main prompt waiting for input ..."));
345 if (mainWin->m_commDebug) Pmsg2(000, "conn %i PROMPT m_in_select %i\n", m_conn, m_in_select);
347 m_at_main_prompt = false;
348 mainWin->set_status(_("At prompt waiting for input ..."));
350 /* commented out until the prompt communication issue with the director is resolved
351 * This is where I call a new text input dialog class to prevent the connection issues
352 * when a text input is requited.
353 * if (!m_in_select) {
354 * new textInputDialog(m_console, m_conn);
358 case BNET_CMD_FAILED:
359 if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
360 if (--m_in_command < 0) {
363 mainWin->set_status(_("Command failed."));
365 /* We should not get this one */
367 if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
368 mainWin->set_status_ready();
373 case BNET_START_SELECT:
374 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
376 new selectDialog(m_console, m_conn);
380 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
381 new yesnoPopUp(m_console, m_conn);
384 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
385 new runCmdPage(m_conn);
387 case BNET_START_RTREE:
388 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
389 new restorePage(m_conn);
392 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
395 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
396 stat = sock_read(); /* get the message */
397 m_console->display_text(msg());
398 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
399 m_console->beginNewCommand(m_conn);
402 case BNET_WARNING_MSG:
403 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
404 stat = sock_read(); /* get the message */
405 if (!m_console->m_warningPrevent) {
406 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
410 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
411 stat = sock_read(); /* get the message */
412 m_console->display_text(msg());
413 mainWin->set_status(msg());
416 if (is_bnet_stop(m_sock)) { /* error or term request */
417 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
418 m_console->stopTimer();
421 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
422 QBrush redBrush(Qt::red);
423 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
424 item->setForeground(0, redBrush);
426 m_notifier->setEnabled(false);
430 mainWin->set_status(_("Director disconnected."));
438 /* Called by signal when the Director has output for us */
439 void DirComm::read_dir(int /* fd */)
442 if (mainWin->m_commDebug) Pmsg1(000, "enter read_dir conn %i read_dir\n", m_conn);
443 stat = m_sock->wait_data(0, 5000);
445 if (mainWin->m_commDebug) Pmsg2(000, "read_dir conn %i stat=%d\n", m_conn, stat);
446 while (read() >= 0) {
447 m_console->display_text(msg());
450 if (mainWin->m_commDebug) Pmsg2(000, "exit read_dir conn %i stat=%d\n", m_conn, stat);
454 * When the notifier is enabled, read_dir() will automatically be
455 * called by the Qt event loop when ever there is any output
456 * from the Director, and read_dir() will then display it on
459 * When we are in a bat dialog, we want to control *all* output
460 * from the Directory, so we set notify to off.
461 * m_console->notifiy(false);
463 bool DirComm::notify(bool enable)
465 bool prev_enabled = false;
467 prev_enabled = m_notifier->isEnabled();
468 m_notifier->setEnabled(enable);
469 if (mainWin->m_connDebug) {
470 Pmsg3(000, "conn=%i notify=%d prev=%d\n", m_conn, enable, prev_enabled);
472 } else if (mainWin->m_connDebug)
473 Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
477 bool DirComm::is_notify_enabled() const
479 bool enabled = false;
481 enabled = m_notifier->isEnabled();
487 * Call-back for reading a passphrase for an encrypted PEM file
488 * This function uses getpass(),
489 * which uses a static buffer and is NOT thread-safe.
491 static int tls_pem_callback(char *buf, int size, const void *userdata)
496 # if defined(HAVE_WIN32)
498 if (win32_cgets(buf, size) == NULL) {
505 const char *prompt = (const char *)userdata;
508 passwd = getpass(prompt);
509 bstrncpy(buf, passwd, size);