2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2008 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;
60 /* Terminate any open socket */
61 void DirComm::terminate()
65 m_notifier->setEnabled(false);
75 * Connect to Director.
77 bool DirComm::connect_dir()
87 mainWin->set_status( tr("Already connected."));
88 m_console->display_textf(_("Already connected\"%s\".\n"),
89 m_console->m_dir->name());
90 if (mainWin->m_connDebug)
91 Pmsg1(000, "DirComm %i BAILING already connected\n", m_conn);
95 memset(jcr, 0, sizeof(JCR));
97 mainWin->set_statusf(_("Connecting to Director %s:%d"), m_console->m_dir->address, m_console->m_dir->DIRport);
98 m_console->display_textf(_("Connecting to Director %s:%d\n\n"), m_console->m_dir->address, m_console->m_dir->DIRport);
100 /* Give GUI a chance */
101 app->processEvents();
104 /* If cons==NULL, default console will be used */
105 cons = (CONRES *)GetNextRes(R_CONSOLE, NULL);
108 /* Initialize Console TLS context once */
109 if (cons && !cons->tls_ctx && (cons->tls_enable || cons->tls_require)) {
110 /* Generate passphrase prompt */
111 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ",
114 /* Initialize TLS context:
115 * Args: CA certfile, CA certdir, Certfile, Keyfile,
116 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
118 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
119 cons->tls_ca_certdir, cons->tls_certfile,
120 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
122 if (!cons->tls_ctx) {
123 m_console->display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
124 m_console->m_dir->name());
125 if (mainWin->m_connDebug)
126 Pmsg1(000, "DirComm %i BAILING Failed to initialize TLS context for Console \n", m_conn);
131 /* Initialize Director TLS context once */
132 if (!m_console->m_dir->tls_ctx && (m_console->m_dir->tls_enable || m_console->m_dir->tls_require)) {
133 /* Generate passphrase prompt */
134 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
135 m_console->m_dir->name());
137 /* Initialize TLS context:
138 * Args: CA certfile, CA certdir, Certfile, Keyfile,
139 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
140 m_console->m_dir->tls_ctx = new_tls_context(m_console->m_dir->tls_ca_certfile,
141 m_console->m_dir->tls_ca_certdir, m_console->m_dir->tls_certfile,
142 m_console->m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
144 if (!m_console->m_dir->tls_ctx) {
145 m_console->display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
146 m_console->m_dir->name());
147 mainWin->set_status("Connection failed");
148 if (mainWin->m_connDebug)
149 Pmsg1(000, "DirComm %i BAILING Failed to initialize TLS context for Director \n", m_conn);
154 if (m_console->m_dir->heartbeat_interval) {
155 heart_beat = m_console->m_dir->heartbeat_interval;
157 heart_beat = cons->heartbeat_interval;
162 m_sock = bnet_connect(NULL, 5, 15, heart_beat,
163 _("Director daemon"), m_console->m_dir->address,
164 NULL, m_console->m_dir->DIRport, 0);
165 if (m_sock == NULL) {
166 mainWin->set_status("Connection failed");
167 if (mainWin->m_connDebug)
168 Pmsg1(000, "DirComm %i BAILING Connection failed\n", m_conn);
171 /* Update page selector to green to indicate that Console is connected */
172 mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
173 QBrush greenBrush(Qt::green);
174 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
175 item->setForeground(0, greenBrush);
178 jcr->dir_bsock = m_sock;
180 if (!authenticate_director(jcr, m_console->m_dir, cons, buf, sizeof(buf))) {
181 m_console->display_text(buf);
182 if (mainWin->m_connDebug)
183 Pmsg1(000, "DirComm %i BAILING Connection failed\n", m_conn);
188 m_console->display_text(buf);
191 /* Give GUI a chance */
192 app->processEvents();
194 mainWin->set_status(_("Initializing ..."));
197 /* Set up input notifier */
198 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
199 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
204 m_console->displayToPrompt(m_conn);
206 m_console->beginNewCommand(m_conn);
208 mainWin->set_status(_("Connected"));
210 if (mainWin->m_connDebug)
211 Pmsg1(000, "Returning TRUE from DirComm->connect_dir : %i\n", m_conn);
215 if (mainWin->m_connDebug)
216 Pmsg1(000, "Returning FALSE from DirComm->connect_dir : %i\n", m_conn);
222 * This should be moved into a bSocket class
232 int DirComm::write(const QString msg)
234 return write(msg.toUtf8().data());
237 int DirComm::write(const char *msg)
242 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
244 m_at_main_prompt = false;
245 if (mainWin->m_commDebug) Pmsg1(000, "send: %s\n", msg);
246 return m_sock->send();
249 int DirComm::sock_read()
253 bool wasEnabled = notify(false);
254 stat = m_sock->recv();
257 stat = m_sock->recv();
263 * Blocking read from director
270 stat = m_sock->wait_data_intr(0, 50000);
274 app->processEvents();
275 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
276 m_console->write_dir(m_conn, ".messages");
277 m_console->messagesPending(false);
283 if (mainWin->m_commDebug) Pmsg1(000, "got: %s\n", m_sock->msg);
285 m_console->display_text("\n");
287 m_at_main_prompt = false;
290 switch (m_sock->msglen) {
291 case BNET_MSGS_PENDING :
292 if (is_notify_enabled() && m_console->hasFocus()) {
293 if (mainWin->m_commDebug) Pmsg0(000, "MSGS PENDING\n");
294 m_console->write_dir(m_conn, ".messages");
295 m_console->displayToPrompt(m_conn);
296 m_console->messagesPending(false);
298 m_console->messagesPending(true);
301 if (mainWin->m_commDebug) Pmsg0(000, "CMD OK\n");
303 m_at_main_prompt = false;
304 mainWin->set_status(_("Command completed ..."));
307 if (mainWin->m_commDebug) Pmsg0(000, "CMD BEGIN\n");
309 m_at_main_prompt = false;
310 mainWin->set_status(_("Processing command ..."));
312 case BNET_MAIN_PROMPT:
313 if (mainWin->m_commDebug) Pmsg0(000, "MAIN PROMPT\n");
315 m_at_main_prompt = true;
316 mainWin->set_status(_("At main prompt waiting for input ..."));
317 QApplication::restoreOverrideCursor();
320 if (mainWin->m_commDebug) Pmsg0(000, "PROMPT\n");
322 m_at_main_prompt = false;
323 mainWin->set_status(_("At prompt waiting for input ..."));
324 QApplication::restoreOverrideCursor();
326 case BNET_CMD_FAILED:
327 if (mainWin->m_commDebug) Pmsg0(000, "CMD FAILED\n");
328 mainWin->set_status(_("Command failed."));
329 QApplication::restoreOverrideCursor();
331 /* We should not get this one */
333 if (mainWin->m_commDebug) Pmsg0(000, "EOD\n");
334 mainWin->set_status_ready();
335 QApplication::restoreOverrideCursor();
340 case BNET_START_SELECT:
341 if (mainWin->m_commDebug) Pmsg0(000, "START SELECT\n");
342 new selectDialog(m_console);
345 if (mainWin->m_commDebug) Pmsg0(000, "YESNO\n");
346 new yesnoPopUp(m_console, m_conn);
349 if (mainWin->m_commDebug) Pmsg0(000, "RUN CMD\n");
352 case BNET_START_RTREE:
353 if (mainWin->m_commDebug) Pmsg0(000, "START RTREE CMD\n");
354 new restorePage(m_conn);
357 if (mainWin->m_commDebug) Pmsg0(000, "END RTREE CMD\n");
360 if (mainWin->m_commDebug) Pmsg0(000, "ERROR MSG\n");
361 stat = sock_read(); /* get the message */
362 m_console->display_text(msg());
363 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
365 case BNET_WARNING_MSG:
366 if (mainWin->m_commDebug) Pmsg0(000, "WARNING MSG\n");
367 stat = sock_read(); /* get the message */
368 m_console->display_text(msg());
369 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
372 if (mainWin->m_commDebug) Pmsg0(000, "INFO MSG\n");
373 stat = sock_read(); /* get the message */
374 m_console->display_text(msg());
375 mainWin->set_status(msg());
378 if (is_bnet_stop(m_sock)) { /* error or term request */
379 if (mainWin->m_commDebug) Pmsg0(000, "BNET STOP\n");
380 m_console->stopTimer();
383 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
384 QBrush redBrush(Qt::red);
385 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
386 item->setForeground(0, redBrush);
388 m_notifier->setEnabled(false);
392 mainWin->set_status(_("Director disconnected."));
393 QApplication::restoreOverrideCursor();
401 /* Called by signal when the Director has output for us */
402 void DirComm::read_dir(int /* fd */)
404 if (mainWin->m_commDebug) Pmsg0(000, "read_dir\n");
405 while (read() >= 0) {
406 m_console->display_text(msg());
411 * When the notifier is enabled, read_dir() will automatically be
412 * called by the Qt event loop when ever there is any output
413 * from the Directory, and read_dir() will then display it on
416 * When we are in a bat dialog, we want to control *all* output
417 * from the Directory, so we set notify to off.
418 * m_console->notifiy(false);
420 bool DirComm::notify(bool enable)
422 bool prev_enabled = false;
424 prev_enabled = m_notifier->isEnabled();
425 m_notifier->setEnabled(enable);
426 if (mainWin->m_connDebug)
427 if (prev_enabled && !enable)
428 Pmsg1(000, "m_notifier Disabling notifier: %i\n", m_conn);
429 else if (!prev_enabled && enable)
430 Pmsg1(000, "m_notifier Enabling notifier: %i\n", m_conn);
431 } else if (mainWin->m_connDebug)
432 Pmsg1(000, "m_notifier does not exist: %i\n", m_conn);
436 bool DirComm::is_notify_enabled() const
438 bool enabled = false;
440 enabled = m_notifier->isEnabled();
445 * Call-back for reading a passphrase for an encrypted PEM file
446 * This function uses getpass(),
447 * which uses a static buffer and is NOT thread-safe.
449 static int tls_pem_callback(char *buf, int size, const void *userdata)
454 const char *prompt = (const char *)userdata;
455 # if defined(HAVE_WIN32)
457 if (win32_cgets(buf, size) == NULL) {
466 passwd = getpass(prompt);
467 bstrncpy(buf, passwd, size);