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 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.
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);
337 m_at_main_prompt = true;
338 mainWin->set_status(_("At main prompt waiting for input ..."));
341 if (mainWin->m_commDebug) Pmsg2(000, "conn %i PROMPT m_in_select %i\n", m_conn, m_in_select);
343 m_at_main_prompt = false;
344 mainWin->set_status(_("At prompt waiting for input ..."));
346 /* commented out until the prompt communication issue with the director is resolved
347 * This is where I call a new text input dialog class to prevent the connection issues
348 * when a text input is requited.
349 * if (!m_in_select) {
350 * new textInputDialog(m_console, m_conn);
354 case BNET_CMD_FAILED:
355 if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
356 if (--m_in_command < 0) {
359 mainWin->set_status(_("Command failed."));
361 /* We should not get this one */
363 if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
364 mainWin->set_status_ready();
369 case BNET_START_SELECT:
370 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
372 new selectDialog(m_console, m_conn);
376 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
377 new yesnoPopUp(m_console, m_conn);
380 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
381 new runCmdPage(m_conn);
383 case BNET_START_RTREE:
384 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
385 new restorePage(m_conn);
388 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
391 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
392 stat = sock_read(); /* get the message */
393 m_console->display_text(msg());
394 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
396 case BNET_WARNING_MSG:
397 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
398 stat = sock_read(); /* get the message */
399 if (!m_console->m_warningPrevent) {
400 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
404 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
405 stat = sock_read(); /* get the message */
406 m_console->display_text(msg());
407 mainWin->set_status(msg());
410 if (is_bnet_stop(m_sock)) { /* error or term request */
411 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
412 m_console->stopTimer();
415 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
416 QBrush redBrush(Qt::red);
417 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
418 item->setForeground(0, redBrush);
420 m_notifier->setEnabled(false);
424 mainWin->set_status(_("Director disconnected."));
432 /* Called by signal when the Director has output for us */
433 void DirComm::read_dir(int /* fd */)
436 if (mainWin->m_commDebug) Pmsg1(000, "enter read_dir conn %i read_dir\n", m_conn);
437 stat = m_sock->wait_data(0, 5000);
439 if (mainWin->m_commDebug) Pmsg2(000, "read_dir conn %i stat=%d\n", m_conn, stat);
440 while (read() >= 0) {
441 m_console->display_text(msg());
444 if (mainWin->m_commDebug) Pmsg2(000, "exit read_dir conn %i stat=%d\n", m_conn, stat);
448 * When the notifier is enabled, read_dir() will automatically be
449 * called by the Qt event loop when ever there is any output
450 * from the Directory, and read_dir() will then display it on
453 * When we are in a bat dialog, we want to control *all* output
454 * from the Directory, so we set notify to off.
455 * m_console->notifiy(false);
457 bool DirComm::notify(bool enable)
459 bool prev_enabled = false;
461 prev_enabled = m_notifier->isEnabled();
462 m_notifier->setEnabled(enable);
463 if (mainWin->m_connDebug) {
464 Pmsg3(000, "conn=%i notify=%d prev=%d\n", m_conn, enable, prev_enabled);
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);