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");
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");
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:
356 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
357 new selectDialog(m_console);
360 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
361 new yesnoPopUp(m_console, m_conn);
364 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
365 new runCmdPage(m_conn);
367 case BNET_START_RTREE:
368 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
369 new restorePage(m_conn);
372 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
375 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
376 stat = sock_read(); /* get the message */
377 m_console->display_text(msg());
378 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
380 case BNET_WARNING_MSG:
381 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
382 stat = sock_read(); /* get the message */
383 m_console->display_text(msg());
384 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
387 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
388 stat = sock_read(); /* get the message */
389 m_console->display_text(msg());
390 mainWin->set_status(msg());
393 if (is_bnet_stop(m_sock)) { /* error or term request */
394 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
395 m_console->stopTimer();
398 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
399 QBrush redBrush(Qt::red);
400 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
401 item->setForeground(0, redBrush);
403 m_notifier->setEnabled(false);
407 mainWin->set_status(_("Director disconnected."));
408 // QApplication::restoreOverrideCursor();
416 /* Called by signal when the Director has output for us */
417 void DirComm::read_dir(int /* fd */)
419 if (mainWin->m_commDebug) Pmsg1(000, "conn %i read_dir\n", m_conn);
420 while (read() >= 0) {
421 m_console->display_text(msg());
426 * When the notifier is enabled, read_dir() will automatically be
427 * called by the Qt event loop when ever there is any output
428 * from the Directory, and read_dir() will then display it on
431 * When we are in a bat dialog, we want to control *all* output
432 * from the Directory, so we set notify to off.
433 * m_console->notifiy(false);
435 bool DirComm::notify(bool enable)
437 bool prev_enabled = false;
439 prev_enabled = m_notifier->isEnabled();
440 m_notifier->setEnabled(enable);
441 if (mainWin->m_connDebug) {
442 if (prev_enabled && !enable)
443 Pmsg2(000, "m_notifier Disabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
444 else if (!prev_enabled && enable)
445 Pmsg2(000, "m_notifier Enabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
447 } else if (mainWin->m_connDebug)
448 Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
452 bool DirComm::is_notify_enabled() const
454 bool enabled = false;
456 enabled = m_notifier->isEnabled();
461 * Call-back for reading a passphrase for an encrypted PEM file
462 * This function uses getpass(),
463 * which uses a static buffer and is NOT thread-safe.
465 static int tls_pem_callback(char *buf, int size, const void *userdata)
470 const char *prompt = (const char *)userdata;
471 # if defined(HAVE_WIN32)
473 if (win32_cgets(buf, size) == NULL) {
482 passwd = getpass(prompt);
483 bstrncpy(buf, passwd, size);