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
274 stat = m_sock->wait_data_intr(0, 50000);
278 app->processEvents();
279 if (m_api_set && m_console->is_messagesPending() && is_notify_enabled() && m_console->hasFocus()) {
280 m_console->write_dir(m_conn, ".messages");
281 m_console->messagesPending(false);
290 if (mainWin->m_commDebug) Pmsg2(000, "conn %i got: %s\n", m_conn, m_sock->msg);
292 m_console->display_text("\n");
294 m_at_main_prompt = false;
297 switch (m_sock->msglen) {
298 case BNET_MSGS_PENDING :
299 if (is_notify_enabled() && m_console->hasFocus()) {
300 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MSGS PENDING\n", m_conn);
301 m_console->write_dir(m_conn, ".messages");
302 m_console->displayToPrompt(m_conn);
303 m_console->messagesPending(false);
305 m_console->messagesPending(true);
308 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD OK\n", m_conn);
310 m_at_main_prompt = false;
311 // Pmsg1(000, "before dec m_in_command=%d\n", m_in_command);
312 if (--m_in_command < 0) {
313 // Pmsg0(000, "m_in_command < 0\n");
316 mainWin->set_status(_("Command completed ..."));
319 if (mainWin->m_commDebug) Pmsg1(000, "conn %i CMD BEGIN\n", m_conn);
321 m_at_main_prompt = false;
323 // Pmsg1(000, "after inc m_in_command=%d\n", m_in_command);
324 mainWin->set_status(_("Processing command ..."));
326 case BNET_MAIN_PROMPT:
327 if (mainWin->m_commDebug) Pmsg1(000, "conn %i MAIN PROMPT\n", m_conn);
329 m_at_main_prompt = true;
330 mainWin->set_status(_("At main prompt waiting for input ..."));
333 if (mainWin->m_commDebug) Pmsg1(000, "conn %i PROMPT\n", m_conn);
335 m_at_main_prompt = false;
336 mainWin->set_status(_("At prompt waiting for input ..."));
338 case BNET_CMD_FAILED:
339 if (mainWin->m_commDebug) Pmsg1(000, "CMD FAILED\n", m_conn);
340 if (--m_in_command < 0) {
343 mainWin->set_status(_("Command failed."));
345 /* We should not get this one */
347 if (mainWin->m_commDebug) Pmsg1(000, "conn %i EOD\n", m_conn);
348 mainWin->set_status_ready();
353 case BNET_START_SELECT:
354 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START SELECT\n", m_conn);
355 new selectDialog(m_console);
358 if (mainWin->m_commDebug) Pmsg1(000, "conn %i YESNO\n", m_conn);
359 new yesnoPopUp(m_console, m_conn);
362 if (mainWin->m_commDebug) Pmsg1(000, "conn %i RUN CMD\n", m_conn);
363 new runCmdPage(m_conn);
365 case BNET_START_RTREE:
366 if (mainWin->m_commDebug) Pmsg1(000, "conn %i START RTREE CMD\n", m_conn);
367 new restorePage(m_conn);
370 if (mainWin->m_commDebug) Pmsg1(000, "conn %i END RTREE CMD\n", m_conn);
373 if (mainWin->m_commDebug) Pmsg1(000, "conn %i ERROR MSG\n", m_conn);
374 stat = sock_read(); /* get the message */
375 m_console->display_text(msg());
376 QMessageBox::critical(m_console, "Error", msg(), QMessageBox::Ok);
378 case BNET_WARNING_MSG:
379 if (mainWin->m_commDebug) Pmsg1(000, "conn %i WARNING MSG\n", m_conn);
380 stat = sock_read(); /* get the message */
381 m_console->display_text(msg());
382 QMessageBox::critical(m_console, "Warning", msg(), QMessageBox::Ok);
385 if (mainWin->m_commDebug) Pmsg1(000, "conn %i INFO MSG\n", m_conn);
386 stat = sock_read(); /* get the message */
387 m_console->display_text(msg());
388 mainWin->set_status(msg());
391 if (is_bnet_stop(m_sock)) { /* error or term request */
392 if (mainWin->m_commDebug) Pmsg1(000, "conn %i BNET STOP\n", m_conn);
393 m_console->stopTimer();
396 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
397 QBrush redBrush(Qt::red);
398 QTreeWidgetItem *item = mainWin->getFromHash(m_console);
399 item->setForeground(0, redBrush);
401 m_notifier->setEnabled(false);
405 mainWin->set_status(_("Director disconnected."));
406 // QApplication::restoreOverrideCursor();
414 /* Called by signal when the Director has output for us */
415 void DirComm::read_dir(int /* fd */)
417 if (mainWin->m_commDebug) Pmsg1(000, "conn %i read_dir\n", m_conn);
418 while (read() >= 0) {
419 m_console->display_text(msg());
424 * When the notifier is enabled, read_dir() will automatically be
425 * called by the Qt event loop when ever there is any output
426 * from the Directory, and read_dir() will then display it on
429 * When we are in a bat dialog, we want to control *all* output
430 * from the Directory, so we set notify to off.
431 * m_console->notifiy(false);
433 bool DirComm::notify(bool enable)
435 bool prev_enabled = false;
437 prev_enabled = m_notifier->isEnabled();
438 m_notifier->setEnabled(enable);
439 if (mainWin->m_connDebug) {
440 if (prev_enabled && !enable)
441 Pmsg2(000, "m_notifier Disabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
442 else if (!prev_enabled && enable)
443 Pmsg2(000, "m_notifier Enabling notifier: %i %s\n", m_conn, m_console->m_dir->name());
445 } else if (mainWin->m_connDebug)
446 Pmsg2(000, "m_notifier does not exist: %i %s\n", m_conn, m_console->m_dir->name());
450 bool DirComm::is_notify_enabled() const
452 bool enabled = false;
454 enabled = m_notifier->isEnabled();
459 * Call-back for reading a passphrase for an encrypted PEM file
460 * This function uses getpass(),
461 * which uses a static buffer and is NOT thread-safe.
463 static int tls_pem_callback(char *buf, int size, const void *userdata)
468 const char *prompt = (const char *)userdata;
469 # if defined(HAVE_WIN32)
471 if (win32_cgets(buf, size) == NULL) {
480 passwd = getpass(prompt);
481 bstrncpy(buf, passwd, size);