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):
52 m_at_main_prompt = false;
61 /* Terminate any open socket */
62 void DirComm::terminate()
66 m_notifier->setEnabled(false);
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 Pmsg1(000, "DirComm %i BAILING already connected\n", m_conn);
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 Pmsg1(000, "DirComm %i BAILING Failed to initialize TLS context for Console \n", m_conn);
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 Pmsg1(000, "DirComm %i BAILING Failed to initialize TLS context for Director \n", m_conn);
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 Pmsg1(000, "DirComm %i BAILING Connection failed\n", m_conn);
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 Pmsg1(000, "DirComm %i BAILING Connection failed\n", m_conn);
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 Pmsg1(000, "Returning TRUE from DirComm->connect_dir : %i\n", m_conn);
216 if (mainWin->m_connDebug)
217 Pmsg1(000, "Returning FALSE from DirComm->connect_dir : %i\n", m_conn);
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
271 stat = m_sock->wait_data_intr(0, 50000);
275 app->processEvents();
276 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
277 m_console->write_dir(m_conn, ".messages");
278 m_console->messagesPending(false);
284 if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
286 m_console->display_text("\n");
288 m_at_main_prompt = false;
291 switch (m_sock->msglen) {
292 case BNET_MSGS_PENDING :
293 if (is_notify_enabled() && m_console->hasFocus()) {
294 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
295 m_console->write_dir(m_conn, ".messages");
296 m_console->displayToPrompt(m_conn);
297 m_console->messagesPending(false);
299 m_console->messagesPending(true);
302 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
304 m_at_main_prompt = false;
305 // Pmsg1(000, "before dec m_in_command=%d\n", m_in_command);
306 if (--m_in_command < 0) {
307 // Pmsg0(000, "m_in_command < 0\n");
310 mainWin->set_status(_("Command completed ..."));
313 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
315 m_at_main_prompt = false;
317 // Pmsg1(000, "after inc m_in_command=%d\n", m_in_command);
318 mainWin->set_status(_("Processing command ..."));
320 case BNET_MAIN_PROMPT:
321 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
323 m_at_main_prompt = true;
324 mainWin->set_status(_("At main prompt waiting for input ..."));
325 QApplication::restoreOverrideCursor();
328 if (mainWin->m_commDebug) Pmsg1(000, "conn %i PROMPT\n", m_conn);
330 m_at_main_prompt = false;
331 mainWin->set_status(_("At prompt waiting for input ..."));
332 QApplication::restoreOverrideCursor();
334 case BNET_CMD_FAILED:
335 if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
336 if (--m_in_command < 0) {
337 // Pmsg0(000, "m_in_command < 0\n");
340 mainWin->set_status(_("Command failed."));
341 QApplication::restoreOverrideCursor();
343 /* We should not get this one */
345 if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
346 mainWin->set_status_ready();
347 QApplication::restoreOverrideCursor();
352 case BNET_START_SELECT:
353 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
354 new selectDialog(m_console);
357 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
358 new yesnoPopUp(m_console, m_conn);
361 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
364 case BNET_START_RTREE:
365 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
366 new restorePage(m_conn);
369 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
372 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
373 stat = sock_read(); /* get the message */
374 m_console->display_text(msg());
375 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
377 case BNET_WARNING_MSG:
378 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
379 stat = sock_read(); /* get the message */
380 m_console->display_text(msg());
381 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
384 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
385 stat = sock_read(); /* get the message */
386 m_console->display_text(msg());
387 mainWin->set_status(msg());
390 if (is_bnet_stop(m_sock)) { /* error or term request */
391 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
392 m_console->stopTimer();
395 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
396 QBrush redBrush(Qt::red);
397 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
398 item->setForeground(0, redBrush);
400 m_notifier->setEnabled(false);
404 mainWin->set_status(_("Director disconnected."));
405 QApplication::restoreOverrideCursor();
413 /* Called by signal when the Director has output for us */
414 void DirComm::read_dir(int /* fd */)
416 if (mainWin->m_commDebug) Pmsg1(000, "conn %i read_dir\n", m_conn);
417 while (read() >= 0) {
418 m_console->display_text(msg());
423 * When the notifier is enabled, read_dir() will automatically be
424 * called by the Qt event loop when ever there is any output
425 * from the Directory, and read_dir() will then display it on
428 * When we are in a bat dialog, we want to control *all* output
429 * from the Directory, so we set notify to off.
430 * m_console->notifiy(false);
432 bool DirComm::notify(bool enable)
434 bool prev_enabled = false;
436 prev_enabled = m_notifier->isEnabled();
437 m_notifier->setEnabled(enable);
438 if (mainWin->m_connDebug)
439 if (prev_enabled && !enable)
440 Pmsg1(000, "m_notifier Disabling notifier: %i\n", m_conn);
441 else if (!prev_enabled && enable)
442 Pmsg1(000, "m_notifier Enabling notifier: %i\n", m_conn);
443 } else if (mainWin->m_connDebug)
444 Pmsg1(000, "m_notifier does not exist: %i\n", m_conn);
448 bool DirComm::is_notify_enabled() const
450 bool enabled = false;
452 enabled = m_notifier->isEnabled();
457 * Call-back for reading a passphrase for an encrypted PEM file
458 * This function uses getpass(),
459 * which uses a static buffer and is NOT thread-safe.
461 static int tls_pem_callback(char *buf, int size, const void *userdata)
466 const char *prompt = (const char *)userdata;
467 # if defined(HAVE_WIN32)
469 if (win32_cgets(buf, size) == NULL) {
478 passwd = getpass(prompt);
479 bstrncpy(buf, passwd, size);