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
204 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
205 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
206 m_notifier->setEnabled(false);
210 m_console->displayToPrompt(m_conn);
212 m_console->beginNewCommand(m_conn);
214 mainWin->set_status(_("Connected"));
216 if (mainWin->m_connDebug)
217 Pmsg2(000, "Returning TRUE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
221 if (mainWin->m_connDebug)
222 Pmsg2(000, "Returning FALSE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
228 * This should be moved into a bSocket class
238 int DirComm::write(const QString msg)
240 return write(msg.toUtf8().data());
243 int DirComm::write(const char *msg)
248 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
250 m_at_main_prompt = false;
251 if (mainWin->m_commDebug) Pmsg2(000, "conn %i send: %s\n", m_conn, msg);
252 return m_sock->send();
255 int DirComm::sock_read()
259 bool wasEnabled = notify(false);
260 stat = m_sock->recv();
263 stat = m_sock->recv();
269 * Blocking read from director
281 stat = m_sock->wait_data_intr(0, 50000);
285 app->processEvents();
286 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
287 m_console->write_dir(m_conn, ".messages", false);
288 m_console->messagesPending(false);
297 if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
299 m_console->display_text("\n");
301 m_at_main_prompt = false;
304 switch (m_sock->msglen) {
305 case BNET_MSGS_PENDING :
306 if (is_notify_enabled() && m_console->hasFocus()) {
307 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
308 m_console->write_dir(m_conn, ".messages", false);
309 m_console->displayToPrompt(m_conn);
310 m_console->messagesPending(false);
312 m_console->messagesPending(true);
315 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
317 m_at_main_prompt = false;
318 if (--m_in_command < 0) {
321 mainWin->set_status(_("Command completed ..."));
324 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
326 m_at_main_prompt = false;
328 mainWin->set_status(_("Processing command ..."));
330 case BNET_MAIN_PROMPT:
331 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
333 m_at_main_prompt = true;
334 mainWin->set_status(_("At main prompt waiting for input ..."));
337 if (mainWin->m_commDebug) Pmsg2(000, "conn %i PROMPT m_in_select %i\n", m_conn, m_in_select);
339 m_at_main_prompt = false;
340 mainWin->set_status(_("At prompt waiting for input ..."));
342 /* commented out until the prompt communication issue with the director is resolved
343 * This is where I call a new text input dialog class to prevent the connection issues
344 * when a text input is requited.
345 * if (!m_in_select) {
346 * 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."));
429 /* Called by signal when the Director has output for us */
430 void DirComm::read_dir(int /* fd */)
433 if (mainWin->m_commDebug) Pmsg1(000, "enter read_dir conn %i read_dir\n", m_conn);
434 stat = m_sock->wait_data(0, 5000);
436 if (mainWin->m_commDebug) Pmsg2(000, "read_dir conn %i stat=%d\n", m_conn, stat);
437 while (read() >= 0) {
438 m_console->display_text(msg());
441 if (mainWin->m_commDebug) Pmsg2(000, "exit read_dir conn %i stat=%d\n", m_conn, stat);
445 * When the notifier is enabled, read_dir() will automatically be
446 * called by the Qt event loop when ever there is any output
447 * from the Directory, and read_dir() will then display it on
450 * When we are in a bat dialog, we want to control *all* output
451 * from the Directory, so we set notify to off.
452 * m_console->notifiy(false);
454 bool DirComm::notify(bool enable)
456 bool prev_enabled = false;
458 prev_enabled = m_notifier->isEnabled();
459 m_notifier->setEnabled(enable);
460 if (mainWin->m_connDebug) {
461 if (prev_enabled && !enable)
462 Pmsg2(000, "m_notifier Disabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
463 else if (!prev_enabled && enable)
464 Pmsg2(000, "m_notifier Enabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
466 } else if (mainWin->m_connDebug)
467 Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
471 bool DirComm::is_notify_enabled() const
473 bool enabled = false;
475 enabled = m_notifier->isEnabled();
481 * Call-back for reading a passphrase for an encrypted PEM file
482 * This function uses getpass(),
483 * which uses a static buffer and is NOT thread-safe.
485 static int tls_pem_callback(char *buf, int size, const void *userdata)
490 # if defined(HAVE_WIN32)
492 if (win32_cgets(buf, size) == NULL) {
499 const char *prompt = (const char *)userdata;
502 passwd = getpass(prompt);
503 bstrncpy(buf, passwd, size);