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);
70 if (mainWin->m_connDebug)
71 Pmsg2(000, "DirComm %i terminating connections %s\n", m_conn, m_console->m_dir->name());
78 * Connect to Director.
80 bool DirComm::connect_dir()
90 mainWin->set_status( tr("Already connected."));
91 m_console->display_textf(_("Already connected\"%s\".\n"),
92 m_console->m_dir->name());
93 if (mainWin->m_connDebug)
94 Pmsg2(000, "DirComm %i BAILING already connected %s\n", m_conn, m_console->m_dir->name());
98 memset(jcr, 0, sizeof(JCR));
100 mainWin->set_statusf(_("Connecting to Director %s:%d"), m_console->m_dir->address, m_console->m_dir->DIRport);
101 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
273 stat = m_sock->wait_data_intr(0, 50000);
277 app->processEvents();
278 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
279 m_console->write_dir(m_conn, ".messages");
280 m_console->messagesPending(false);
286 if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
288 m_console->display_text("\n");
290 m_at_main_prompt = false;
293 switch (m_sock->msglen) {
294 case BNET_MSGS_PENDING :
295 if (is_notify_enabled() && m_console->hasFocus()) {
296 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
297 m_console->write_dir(m_conn, ".messages");
298 m_console->displayToPrompt(m_conn);
299 m_console->messagesPending(false);
301 m_console->messagesPending(true);
304 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
306 m_at_main_prompt = false;
307 // Pmsg1(000, "before dec m_in_command=%d\n", m_in_command);
308 if (--m_in_command < 0) {
309 // Pmsg0(000, "m_in_command < 0\n");
312 mainWin->set_status(_("Command completed ..."));
315 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
317 m_at_main_prompt = false;
319 // Pmsg1(000, "after inc m_in_command=%d\n", m_in_command);
320 mainWin->set_status(_("Processing command ..."));
322 case BNET_MAIN_PROMPT:
323 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
325 m_at_main_prompt = true;
326 mainWin->set_status(_("At main prompt waiting for input ..."));
329 if (mainWin->m_commDebug) Pmsg1(000, "conn %i PROMPT\n", m_conn);
331 m_at_main_prompt = false;
332 mainWin->set_status(_("At prompt waiting for input ..."));
334 case BNET_CMD_FAILED:
335 if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
336 if (--m_in_command < 0) {
339 mainWin->set_status(_("Command failed."));
341 /* We should not get this one */
343 if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
344 mainWin->set_status_ready();
349 case BNET_START_SELECT:
350 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
351 new selectDialog(m_console);
354 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
355 new yesnoPopUp(m_console, m_conn);
358 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
359 new runCmdPage(m_conn);
361 case BNET_START_RTREE:
362 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
363 new restorePage(m_conn);
366 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
369 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
370 stat = sock_read(); /* get the message */
371 m_console->display_text(msg());
372 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
374 case BNET_WARNING_MSG:
375 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
376 stat = sock_read(); /* get the message */
377 m_console->display_text(msg());
378 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
381 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
382 stat = sock_read(); /* get the message */
383 m_console->display_text(msg());
384 mainWin->set_status(msg());
387 if (is_bnet_stop(m_sock)) { /* error or term request */
388 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
389 m_console->stopTimer();
392 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
393 QBrush redBrush(Qt::red);
394 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
395 item->setForeground(0, redBrush);
397 m_notifier->setEnabled(false);
401 mainWin->set_status(_("Director disconnected."));
402 // QApplication::restoreOverrideCursor();
410 /* Called by signal when the Director has output for us */
411 void DirComm::read_dir(int /* fd */)
413 if (mainWin->m_commDebug) Pmsg1(000, "conn %i read_dir\n", m_conn);
414 while (read() >= 0) {
415 m_console->display_text(msg());
420 * When the notifier is enabled, read_dir() will automatically be
421 * called by the Qt event loop when ever there is any output
422 * from the Directory, and read_dir() will then display it on
425 * When we are in a bat dialog, we want to control *all* output
426 * from the Directory, so we set notify to off.
427 * m_console->notifiy(false);
429 bool DirComm::notify(bool enable)
431 bool prev_enabled = false;
433 prev_enabled = m_notifier->isEnabled();
434 m_notifier->setEnabled(enable);
435 if (mainWin->m_connDebug) {
436 if (prev_enabled && !enable)
437 Pmsg2(000, "m_notifier Disabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
438 else if (!prev_enabled && enable)
439 Pmsg2(000, "m_notifier Enabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
441 } else if (mainWin->m_connDebug)
442 Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
446 bool DirComm::is_notify_enabled() const
448 bool enabled = false;
450 enabled = m_notifier->isEnabled();
455 * Call-back for reading a passphrase for an encrypted PEM file
456 * This function uses getpass(),
457 * which uses a static buffer and is NOT thread-safe.
459 static int tls_pem_callback(char *buf, int size, const void *userdata)
464 const char *prompt = (const char *)userdata;
465 # if defined(HAVE_WIN32)
467 if (win32_cgets(buf, size) == NULL) {
476 passwd = getpass(prompt);
477 bstrncpy(buf, passwd, size);