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);
99 m_console->display_textf(_("Connecting to Director %s:%d\n\n"), m_console->m_dir->address, m_console->m_dir->DIRport);
101 /* Give GUI a chance */
102 app->processEvents();
105 /* If cons==NULL, default console will be used */
106 cons = (CONRES *)GetNextRes(R_CONSOLE, NULL);
109 /* Initialize Console TLS context once */
110 if (cons && !cons->tls_ctx && (cons->tls_enable || cons->tls_require)) {
111 /* Generate passphrase prompt */
112 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ",
115 /* Initialize TLS context:
116 * Args: CA certfile, CA certdir, Certfile, Keyfile,
117 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
119 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
120 cons->tls_ca_certdir, cons->tls_certfile,
121 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
123 if (!cons->tls_ctx) {
124 m_console->display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
125 m_console->m_dir->name());
126 if (mainWin->m_connDebug)
127 Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Console %s\n", m_conn, m_console->m_dir->name());
132 /* Initialize Director TLS context once */
133 if (!m_console->m_dir->tls_ctx && (m_console->m_dir->tls_enable || m_console->m_dir->tls_require)) {
134 /* Generate passphrase prompt */
135 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
136 m_console->m_dir->name());
138 /* Initialize TLS context:
139 * Args: CA certfile, CA certdir, Certfile, Keyfile,
140 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
141 m_console->m_dir->tls_ctx = new_tls_context(m_console->m_dir->tls_ca_certfile,
142 m_console->m_dir->tls_ca_certdir, m_console->m_dir->tls_certfile,
143 m_console->m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
145 if (!m_console->m_dir->tls_ctx) {
146 m_console->display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
147 m_console->m_dir->name());
148 mainWin->set_status("Connection failed");
149 if (mainWin->m_connDebug)
150 Pmsg2(000, "DirComm %i BAILING Failed to initialize TLS context for Director %s\n", m_conn, m_console->m_dir->name());
155 if (m_console->m_dir->heartbeat_interval) {
156 heart_beat = m_console->m_dir->heartbeat_interval;
158 heart_beat = cons->heartbeat_interval;
163 m_sock = bnet_connect(NULL, 5, 15, heart_beat,
164 _("Director daemon"), m_console->m_dir->address,
165 NULL, m_console->m_dir->DIRport, 0);
166 if (m_sock == NULL) {
167 mainWin->set_status("Connection failed");
168 if (mainWin->m_connDebug)
169 Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
172 /* Update page selector to green to indicate that Console is connected */
173 mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
174 QBrush greenBrush(Qt::green);
175 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
176 item->setForeground(0, greenBrush);
179 jcr->dir_bsock = m_sock;
181 if (!authenticate_director(jcr, m_console->m_dir, cons, buf, sizeof(buf))) {
182 m_console->display_text(buf);
183 if (mainWin->m_connDebug)
184 Pmsg2(000, "DirComm %i BAILING Connection failed %s\n", m_conn, m_console->m_dir->name());
189 m_console->display_text(buf);
192 /* Give GUI a chance */
193 app->processEvents();
195 mainWin->set_status(_("Initializing ..."));
198 /* Set up input notifier */
199 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
200 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
205 m_console->displayToPrompt(m_conn);
207 m_console->beginNewCommand(m_conn);
209 mainWin->set_status(_("Connected"));
211 if (mainWin->m_connDebug)
212 Pmsg2(000, "Returning TRUE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
216 if (mainWin->m_connDebug)
217 Pmsg2(000, "Returning FALSE from DirComm->connect_dir : %i %s\n", m_conn, m_console->m_dir->name());
223 * This should be moved into a bSocket class
233 int DirComm::write(const QString msg)
235 return write(msg.toUtf8().data());
238 int DirComm::write(const char *msg)
243 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
245 m_at_main_prompt = false;
246 if (mainWin->m_commDebug) Pmsg2(000, "conn %i send: %s\n", m_conn, msg);
247 return m_sock->send();
250 int DirComm::sock_read()
254 bool wasEnabled = notify(false);
255 stat = m_sock->recv();
258 stat = m_sock->recv();
264 * Blocking read from director
276 stat = m_sock->wait_data_intr(0, 50000);
280 app->processEvents();
281 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
282 m_console->write_dir(m_conn, ".messages", false);
283 m_console->messagesPending(false);
292 if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
294 m_console->display_text("\n");
296 m_at_main_prompt = false;
299 switch (m_sock->msglen) {
300 case BNET_MSGS_PENDING :
301 if (is_notify_enabled() && m_console->hasFocus()) {
302 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
303 m_console->write_dir(m_conn, ".messages", false);
304 m_console->displayToPrompt(m_conn);
305 m_console->messagesPending(false);
307 m_console->messagesPending(true);
310 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
312 m_at_main_prompt = false;
313 // Pmsg1(000, "before dec m_in_command=%d\n", m_in_command);
314 if (--m_in_command < 0) {
315 // Pmsg0(000, "m_in_command < 0\n");
318 mainWin->set_status(_("Command completed ..."));
321 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
323 m_at_main_prompt = false;
325 // Pmsg1(000, "after inc m_in_command=%d\n", m_in_command);
326 mainWin->set_status(_("Processing command ..."));
328 case BNET_MAIN_PROMPT:
329 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
331 m_at_main_prompt = true;
332 mainWin->set_status(_("At main prompt waiting for input ..."));
335 if (mainWin->m_commDebug) Pmsg1(000, "conn %i PROMPT\n", m_conn);
337 m_at_main_prompt = false;
338 mainWin->set_status(_("At prompt waiting for input ..."));
340 case BNET_CMD_FAILED:
341 if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
342 if (--m_in_command < 0) {
345 mainWin->set_status(_("Command failed."));
347 /* We should not get this one */
349 if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
350 mainWin->set_status_ready();
355 case BNET_START_SELECT:
357 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
358 new selectDialog(m_console, m_conn);
361 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
362 new yesnoPopUp(m_console, m_conn);
365 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
366 new runCmdPage(m_conn);
368 case BNET_START_RTREE:
369 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
370 new restorePage(m_conn);
373 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
376 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
377 stat = sock_read(); /* get the message */
378 m_console->display_text(msg());
379 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
381 case BNET_WARNING_MSG:
382 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
383 stat = sock_read(); /* get the message */
384 if (!m_console->m_warningPrevent) {
385 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
389 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
390 stat = sock_read(); /* get the message */
391 m_console->display_text(msg());
392 mainWin->set_status(msg());
395 if (is_bnet_stop(m_sock)) { /* error or term request */
396 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
397 m_console->stopTimer();
400 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
401 QBrush redBrush(Qt::red);
402 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
403 item->setForeground(0, redBrush);
405 m_notifier->setEnabled(false);
409 mainWin->set_status(_("Director disconnected."));
410 // QApplication::restoreOverrideCursor();
418 /* Called by signal when the Director has output for us */
419 void DirComm::read_dir(int /* fd */)
421 if (mainWin->m_commDebug) Pmsg1(000, "conn %i read_dir\n", m_conn);
422 while (read() >= 0) {
423 m_console->display_text(msg());
428 * When the notifier is enabled, read_dir() will automatically be
429 * called by the Qt event loop when ever there is any output
430 * from the Directory, and read_dir() will then display it on
433 * When we are in a bat dialog, we want to control *all* output
434 * from the Directory, so we set notify to off.
435 * m_console->notifiy(false);
437 bool DirComm::notify(bool enable)
439 bool prev_enabled = false;
441 prev_enabled = m_notifier->isEnabled();
442 m_notifier->setEnabled(enable);
443 if (mainWin->m_connDebug) {
444 if (prev_enabled && !enable)
445 Pmsg2(000, "m_notifier Disabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
446 else if (!prev_enabled && enable)
447 Pmsg2(000, "m_notifier Enabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
449 } else if (mainWin->m_connDebug)
450 Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
454 bool DirComm::is_notify_enabled() const
456 bool enabled = false;
458 enabled = m_notifier->isEnabled();
463 * Call-back for reading a passphrase for an encrypted PEM file
464 * This function uses getpass(),
465 * which uses a static buffer and is NOT thread-safe.
467 static int tls_pem_callback(char *buf, int size, const void *userdata)
472 const char *prompt = (const char *)userdata;
473 # if defined(HAVE_WIN32)
475 if (win32_cgets(buf, size) == NULL) {
484 passwd = getpass(prompt);
485 bstrncpy(buf, passwd, size);