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
43 static int tls_pem_callback(char *buf, int size, const void *userdata);
45 DirComm::DirComm(Console *parent, int conn): m_notifier(NULL), m_api_set(false)
50 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());
96 memset(jcr, 0, sizeof(JCR));
98 mainWin->set_statusf(_("Connecting to Director %s:%d"), m_console->m_dir->address, m_console->m_dir->DIRport);
100 m_console->display_textf(_("Connecting to Director %s:%d\n\n"), m_console->m_dir->address, m_console->m_dir->DIRport);
103 /* Give GUI a chance */
104 app->processEvents();
107 /* If cons==NULL, default console will be used */
108 cons = (CONRES *)GetNextRes(R_CONSOLE, NULL);
111 /* Initialize Console TLS context once */
112 if (cons && !cons->tls_ctx && (cons->tls_enable || cons->tls_require)) {
113 /* Generate passphrase prompt */
114 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ",
117 /* Initialize TLS context:
118 * Args: CA certfile, CA certdir, Certfile, Keyfile,
119 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
121 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
122 cons->tls_ca_certdir, cons->tls_certfile,
123 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
125 if (!cons->tls_ctx) {
126 m_console->display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
127 m_console->m_dir->name());
128 if (mainWin->m_connDebug)
129 Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Console %s\n", m_conn, m_console->m_dir->name());
134 /* Initialize Director TLS context once */
135 if (!m_console->m_dir->tls_ctx && (m_console->m_dir->tls_enable || m_console->m_dir->tls_require)) {
136 /* Generate passphrase prompt */
137 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
138 m_console->m_dir->name());
140 /* Initialize TLS context:
141 * Args: CA certfile, CA certdir, Certfile, Keyfile,
142 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
143 m_console->m_dir->tls_ctx = new_tls_context(m_console->m_dir->tls_ca_certfile,
144 m_console->m_dir->tls_ca_certdir, m_console->m_dir->tls_certfile,
145 m_console->m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
147 if (!m_console->m_dir->tls_ctx) {
148 m_console->display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
149 m_console->m_dir->name());
150 mainWin->set_status("Connection failed");
151 if (mainWin->m_connDebug)
152 Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Director %s\n", m_conn, m_console->m_dir->name());
157 if (m_console->m_dir->heartbeat_interval) {
158 heart_beat = m_console->m_dir->heartbeat_interval;
160 heart_beat = cons->heartbeat_interval;
165 m_sock = bnet_connect(NULL, 5, 15, heart_beat,
166 _("Director daemon"), m_console->m_dir->address,
167 NULL, m_console->m_dir->DIRport, 0);
168 if (m_sock == NULL) {
169 mainWin->set_status("Connection failed");
170 if (mainWin->m_connDebug)
171 Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
174 /* Update page selector to green to indicate that Console is connected */
175 mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
176 QBrush greenBrush(Qt::green);
177 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
178 item->setForeground(0, greenBrush);
181 jcr->dir_bsock = m_sock;
183 if (!authenticate_director(jcr, m_console->m_dir, cons, buf, sizeof(buf))) {
184 m_console->display_text(buf);
185 if (mainWin->m_connDebug)
186 Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
191 m_console->display_text(buf);
194 /* Give GUI a chance */
195 app->processEvents();
197 mainWin->set_status(_("Initializing ..."));
200 /* Set up input notifier */
201 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
202 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
207 m_console->displayToPrompt(m_conn);
209 m_console->beginNewCommand(m_conn);
211 mainWin->set_status(_("Connected"));
213 if (mainWin->m_connDebug)
214 Pmsg2(000, "Returning TRUE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
218 if (mainWin->m_connDebug)
219 Pmsg2(000, "Returning FALSE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
225 * This should be moved into a bSocket class
235 int DirComm::write(const QString msg)
237 return write(msg.toUtf8().data());
240 int DirComm::write(const char *msg)
245 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
247 m_at_main_prompt = false;
248 if (mainWin->m_commDebug) Pmsg2(000, "conn %i send: %s\n", m_conn, msg);
249 return m_sock->send();
252 int DirComm::sock_read()
256 bool wasEnabled = notify(false);
257 stat = m_sock->recv();
260 stat = m_sock->recv();
266 * Blocking read from director
278 stat = m_sock->wait_data_intr(0, 50000);
282 app->processEvents();
283 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
284 m_console->write_dir(m_conn, ".messages", false);
285 m_console->messagesPending(false);
294 if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
296 m_console->display_text("\n");
298 m_at_main_prompt = false;
301 switch (m_sock->msglen) {
302 case BNET_MSGS_PENDING :
303 if (is_notify_enabled() && m_console->hasFocus()) {
304 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
305 m_console->write_dir(m_conn, ".messages", false);
306 m_console->displayToPrompt(m_conn);
307 m_console->messagesPending(false);
309 m_console->messagesPending(true);
312 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
314 m_at_main_prompt = false;
315 // Pmsg1(000, "before dec m_in_command=%d\n", m_in_command);
316 if (--m_in_command < 0) {
317 // Pmsg0(000, "m_in_command < 0\n");
320 mainWin->set_status(_("Command completed ..."));
323 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
325 m_at_main_prompt = false;
327 // Pmsg1(000, "after inc m_in_command=%d\n", m_in_command);
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) Pmsg1(000, "conn %i PROMPT\n", m_conn);
339 m_at_main_prompt = false;
340 mainWin->set_status(_("At prompt waiting for input ..."));
342 case BNET_CMD_FAILED:
343 if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
344 if (--m_in_command < 0) {
347 mainWin->set_status(_("Command failed."));
349 /* We should not get this one */
351 if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
352 mainWin->set_status_ready();
357 case BNET_START_SELECT:
359 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
360 new selectDialog(m_console, m_conn);
363 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
364 new yesnoPopUp(m_console, m_conn);
367 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
368 new runCmdPage(m_conn);
370 case BNET_START_RTREE:
371 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
372 new restorePage(m_conn);
375 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
378 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
379 stat = sock_read(); /* get the message */
380 m_console->display_text(msg());
381 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
383 case BNET_WARNING_MSG:
384 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
385 stat = sock_read(); /* get the message */
386 if (!m_console->m_warningPrevent) {
387 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
391 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
392 stat = sock_read(); /* get the message */
393 m_console->display_text(msg());
394 mainWin->set_status(msg());
397 if (is_bnet_stop(m_sock)) { /* error or term request */
398 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
399 m_console->stopTimer();
402 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
403 QBrush redBrush(Qt::red);
404 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
405 item->setForeground(0, redBrush);
407 m_notifier->setEnabled(false);
411 mainWin->set_status(_("Director disconnected."));
412 // QApplication::restoreOverrideCursor();
420 /* Called by signal when the Director has output for us */
421 void DirComm::read_dir(int /* fd */)
423 if (mainWin->m_commDebug) Pmsg1(000, "conn %i read_dir\n", m_conn);
424 while (read() >= 0) {
425 m_console->display_text(msg());
430 * When the notifier is enabled, read_dir() will automatically be
431 * called by the Qt event loop when ever there is any output
432 * from the Directory, and read_dir() will then display it on
435 * When we are in a bat dialog, we want to control *all* output
436 * from the Directory, so we set notify to off.
437 * m_console->notifiy(false);
439 bool DirComm::notify(bool enable)
441 bool prev_enabled = false;
443 prev_enabled = m_notifier->isEnabled();
444 m_notifier->setEnabled(enable);
445 if (mainWin->m_connDebug) {
446 if (prev_enabled && !enable)
447 Pmsg2(000, "m_notifier Disabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
448 else if (!prev_enabled && enable)
449 Pmsg2(000, "m_notifier Enabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
451 } else if (mainWin->m_connDebug)
452 Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
456 bool DirComm::is_notify_enabled() const
458 bool enabled = false;
460 enabled = m_notifier->isEnabled();
465 * Call-back for reading a passphrase for an encrypted PEM file
466 * This function uses getpass(),
467 * which uses a static buffer and is NOT thread-safe.
469 static int tls_pem_callback(char *buf, int size, const void *userdata)
474 const char *prompt = (const char *)userdata;
475 # if defined(HAVE_WIN32)
477 if (win32_cgets(buf, size) == NULL) {
486 passwd = getpass(prompt);
487 bstrncpy(buf, passwd, size);