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);
182 item->setForeground(0, greenBrush);
185 jcr->dir_bsock = m_sock;
187 if (!authenticate_director(jcr, m_console->m_dir, cons, buf, sizeof(buf))) {
188 m_console->display_text(buf);
189 if (mainWin->m_connDebug)
190 Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
195 m_console->display_text(buf);
198 /* Give GUI a chance */
199 app->processEvents();
201 mainWin->set_status(_("Initializing ..."));
204 * Set up input notifier
206 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
207 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
208 m_notifier->setEnabled(false);
212 m_console->displayToPrompt(m_conn);
214 m_console->beginNewCommand(m_conn);
216 mainWin->set_status(_("Connected"));
218 if (mainWin->m_connDebug) {
219 Pmsg2(000, "Returning TRUE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
224 if (mainWin->m_connDebug) {
225 Pmsg2(000, "Returning FALSE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
232 * This should be moved into a bSocket class
242 int DirComm::write(const QString msg)
244 return write(msg.toUtf8().data());
247 int DirComm::write(const char *msg)
252 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
254 m_at_main_prompt = false;
255 if (mainWin->m_commDebug) Pmsg2(000, "conn %i send: %s\n", m_conn, msg);
256 return m_sock->send();
259 int DirComm::sock_read()
263 bool wasEnabled = notify(false);
264 stat = m_sock->recv();
267 stat = m_sock->recv();
273 * Blocking read from director
285 stat = m_sock->wait_data_intr(0, 50000);
289 app->processEvents();
290 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
291 m_console->write_dir(m_conn, ".messages", false);
292 m_console->messagesPending(false);
301 if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
303 m_console->display_text("\n");
305 m_at_main_prompt = false;
308 switch (m_sock->msglen) {
309 case BNET_MSGS_PENDING :
310 if (is_notify_enabled() && m_console->hasFocus()) {
311 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
312 m_console->write_dir(m_conn, ".messages", false);
313 m_console->displayToPrompt(m_conn);
314 m_console->messagesPending(false);
316 m_console->messagesPending(true);
319 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
321 m_at_main_prompt = false;
322 if (--m_in_command < 0) {
325 mainWin->set_status(_("Command completed ..."));
328 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
330 m_at_main_prompt = false;
332 mainWin->set_status(_("Processing command ..."));
334 case BNET_MAIN_PROMPT:
335 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
336 if (!m_at_prompt && ! m_at_main_prompt) {
338 m_at_main_prompt = true;
339 mainWin->set_status(_("At main prompt waiting for input ..."));
343 if (mainWin->m_commDebug) Pmsg2(000, "conn %i PROMPT m_in_select %i\n", m_conn, m_in_select);
345 m_at_main_prompt = false;
346 mainWin->set_status(_("At prompt waiting for input ..."));
348 /* commented out until the prompt communication issue with the director is resolved
349 * This is where I call a new text input dialog class to prevent the connection issues
350 * when a text input is requited.
351 * if (!m_in_select) {
352 * new textInputDialog(m_console, m_conn);
356 case BNET_CMD_FAILED:
357 if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
358 if (--m_in_command < 0) {
361 mainWin->set_status(_("Command failed."));
363 /* We should not get this one */
365 if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
366 mainWin->set_status_ready();
371 case BNET_START_SELECT:
372 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
374 new selectDialog(m_console, m_conn);
378 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
379 new yesnoPopUp(m_console, m_conn);
382 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
383 new runCmdPage(m_conn);
385 case BNET_START_RTREE:
386 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
387 new restorePage(m_conn);
390 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
393 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
394 stat = sock_read(); /* get the message */
395 m_console->display_text(msg());
396 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
397 m_console->beginNewCommand(m_conn);
400 case BNET_WARNING_MSG:
401 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
402 stat = sock_read(); /* get the message */
403 if (!m_console->m_warningPrevent) {
404 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
408 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
409 stat = sock_read(); /* get the message */
410 m_console->display_text(msg());
411 mainWin->set_status(msg());
414 if (is_bnet_stop(m_sock)) { /* error or term request */
415 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
416 m_console->stopTimer();
419 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
420 QBrush redBrush(Qt::red);
421 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
422 item->setForeground(0, redBrush);
424 m_notifier->setEnabled(false);
428 mainWin->set_status(_("Director disconnected."));
436 /* Called by signal when the Director has output for us */
437 void DirComm::read_dir(int /* fd */)
440 if (mainWin->m_commDebug) Pmsg1(000, "enter read_dir conn %i read_dir\n", m_conn);
441 stat = m_sock->wait_data(0, 5000);
443 if (mainWin->m_commDebug) Pmsg2(000, "read_dir conn %i stat=%d\n", m_conn, stat);
444 while (read() >= 0) {
445 m_console->display_text(msg());
448 if (mainWin->m_commDebug) Pmsg2(000, "exit read_dir conn %i stat=%d\n", m_conn, stat);
452 * When the notifier is enabled, read_dir() will automatically be
453 * called by the Qt event loop when ever there is any output
454 * from the Director, and read_dir() will then display it on
457 * When we are in a bat dialog, we want to control *all* output
458 * from the Directory, so we set notify to off.
459 * m_console->notifiy(false);
461 bool DirComm::notify(bool enable)
463 bool prev_enabled = false;
465 prev_enabled = m_notifier->isEnabled();
466 m_notifier->setEnabled(enable);
467 if (mainWin->m_connDebug) {
468 Pmsg3(000, "conn=%i notify=%d prev=%d\n", m_conn, enable, prev_enabled);
470 } else if (mainWin->m_connDebug)
471 Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
475 bool DirComm::is_notify_enabled() const
477 bool enabled = false;
479 enabled = m_notifier->isEnabled();
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 # if defined(HAVE_WIN32)
496 if (win32_cgets(buf, size) == NULL) {
503 const char *prompt = (const char *)userdata;
506 passwd = getpass(prompt);
507 bstrncpy(buf, passwd, size);