2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2007 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 John Walker.
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.
31 * Bacula Communications class that is at a higher level than BSOCK
33 * Kern Sibbald, May MMVII
44 static int tls_pem_callback(char *buf, int size, const void *userdata);
47 DirComm::DirComm(Console *console)
52 m_at_main_prompt = false;
63 void DirComm::startTimer()
65 m_timer = new QTimer(m_console);
66 QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
67 m_timer->start(mainWin->m_checkMessagesInterval*1000);
70 void DirComm::stopTimer()
73 QWidget::disconnect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
80 void DirComm::poll_messages()
82 m_messages_pending = true;
83 if ((m_at_main_prompt) && (mainWin->m_checkMessages)){
89 /* Terminate any open socket */
90 void DirComm::terminate()
100 * Connect to Director.
102 void DirComm::connect_dir(DIRRES *dir, CONRES *cons)
110 mainWin->set_status("No Director found.");
114 mainWin->set_status("Already connected.");
118 memset(&jcr, 0, sizeof(jcr));
120 mainWin->set_statusf(_("Connecting to Director %s:%d"), m_dir->address, m_dir->DIRport);
121 m_console->display_textf(_("Connecting to Director %s:%d\n\n"), m_dir->address, m_dir->DIRport);
123 /* Give GUI a chance */
124 app->processEvents();
126 /* Initialize Console TLS context once */
127 if (cons && !cons->tls_ctx && (cons->tls_enable || cons->tls_require)) {
128 /* Generate passphrase prompt */
129 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ",
132 /* Initialize TLS context:
133 * Args: CA certfile, CA certdir, Certfile, Keyfile,
134 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
136 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
137 cons->tls_ca_certdir, cons->tls_certfile,
138 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
140 if (!cons->tls_ctx) {
141 m_console->display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
147 /* Initialize Director TLS context once */
148 if (!m_dir->tls_ctx && (m_dir->tls_enable || m_dir->tls_require)) {
149 /* Generate passphrase prompt */
150 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
153 /* Initialize TLS context:
154 * Args: CA certfile, CA certdir, Certfile, Keyfile,
155 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
156 m_dir->tls_ctx = new_tls_context(m_dir->tls_ca_certfile,
157 m_dir->tls_ca_certdir, m_dir->tls_certfile,
158 m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
160 if (!m_dir->tls_ctx) {
161 m_console->display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
163 mainWin->set_status("Connection failed");
168 if (m_dir->heartbeat_interval) {
169 heart_beat = m_dir->heartbeat_interval;
171 heart_beat = cons->heartbeat_interval;
176 m_sock = bnet_connect(NULL, 5, 15, heart_beat,
177 _("Director daemon"), m_dir->address,
178 NULL, m_dir->DIRport, 0);
179 if (m_sock == NULL) {
180 mainWin->set_status("Connection failed");
183 /* Update page selector to green to indicate that Console is connected */
184 mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
185 QBrush greenBrush(Qt::green);
186 QTreeWidgetItem *item = mainWin->getFromHash(this);
187 item->setForeground(0, greenBrush);
190 jcr.dir_bsock = m_sock;
192 if (!authenticate_director(&jcr, m_dir, cons, buf, sizeof(buf))) {
193 m_console->display_text(buf);
197 m_console->display_text(buf);
200 /* Give GUI a chance */
201 app->processEvents();
203 mainWin->set_status(_("Initializing ..."));
205 /* Set up input notifier */
206 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
207 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
209 mainWin->set_status(_("Connected"));
210 startTimer(); /* start message timer */
214 bool DirComm::dir_cmd(QString &cmd, QStringList &results)
216 return dir_cmd(cmd.toUtf8().data(), results);
220 * Send a command to the Director, and return the
221 * results in a QStringList.
223 bool DirComm::dir_cmd(const char *cmd, QStringList &results)
229 while ((stat = read()) > 0) {
230 if (mainWin->m_displayAll) m_console->display_text(msg());
231 strip_trailing_junk(msg());
236 return true; /* ***FIXME*** return any command error */
239 bool DirComm::sql_cmd(QString &query, QStringList &results)
241 return sql_cmd(query.toUtf8().data(), results);
245 * Send an sql query to the Director, and return the
246 * results in a QStringList.
248 bool DirComm::sql_cmd(const char *query, QStringList &results)
251 POOL_MEM cmd(PM_MESSAGE);
253 if (!is_connectedGui()) {
259 pm_strcpy(cmd, ".sql query=\"");
260 pm_strcat(cmd, query);
261 pm_strcat(cmd, "\"");
263 while ((stat = read()) > 0) {
264 if (mainWin->m_displayAll) m_console->display_text(msg());
265 strip_trailing_junk(msg());
270 return true; /* ***FIXME*** return any command error */
274 * This should be moved into a bSocket class
284 /* Send a command to the Director */
285 void DirComm::write_dir(const char *msg)
288 mainWin->set_status(_("Processing command ..."));
289 QApplication::setOverrideCursor(Qt::WaitCursor);
292 mainWin->set_status(" Director not connected. Click on connect button.");
293 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
294 QBrush redBrush(Qt::red);
295 QTreeWidgetItem *item = mainWin->getFromHash(this);
296 item->setForeground(0, redBrush);
298 m_at_main_prompt = false;
302 int DirComm::write(const QString msg)
304 return write(msg.toUtf8().data());
307 int DirComm::write(const char *msg)
312 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
314 m_at_main_prompt = false;
315 if (mainWin->m_commDebug) Pmsg1(000, "send: %s\n", msg);
316 return m_sock->send();
321 * Get to main command prompt -- i.e. abort any suDirCommand
323 void DirComm::beginNewCommand()
325 for (int i=0; i < 3; i++) {
328 if (mainWin->m_displayAll) m_console->display_text(msg());
330 if (m_at_main_prompt) {
334 m_console->display_text("\n");
337 void DirComm::displayToPrompt()
340 if (mainWin->m_commDebug) Pmsg0(000, "DisplaytoPrompt\n");
341 while (!m_at_prompt) {
342 if ((stat=read()) > 0) {
343 m_console->display_text(msg());
346 if (mainWin->m_commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
349 void DirComm::discardToPrompt()
352 if (mainWin->m_commDebug) Pmsg0(000, "discardToPrompt\n");
353 while (!m_at_prompt) {
354 if ((stat=read()) > 0) {
355 if (mainWin->m_displayAll) m_console->display_text(msg());
358 if (mainWin->m_commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
363 * Blocking read from director
370 stat = bnet_wait_data_intr(m_sock, 1);
374 app->processEvents();
375 if (m_api_set && m_messages_pending) {
376 write_dir(".messages");
377 m_messages_pending = false;
381 stat = m_sock->recv();
383 if (mainWin->m_commDebug) Pmsg1(000, "got: %s\n", m_sock->msg);
385 m_console->display_text("\n");
387 m_at_main_prompt = false;
390 switch (m_sock->msglen) {
391 case BNET_MSGS_PENDING :
392 if (m_notifier->isEnabled()) {
393 if (mainWin->m_commDebug) Pmsg0(000, "MSGS PENDING\n");
394 write_dir(".messages");
396 m_messages_pending = false;
398 m_messages_pending = true;
401 if (mainWin->m_commDebug) Pmsg0(000, "CMD OK\n");
403 m_at_main_prompt = false;
404 mainWin->set_status(_("Command completed ..."));
407 if (mainWin->m_commDebug) Pmsg0(000, "CMD BEGIN\n");
409 m_at_main_prompt = false;
410 mainWin->set_status(_("Processing command ..."));
412 case BNET_MAIN_PROMPT:
413 if (mainWin->m_commDebug) Pmsg0(000, "MAIN PROMPT\n");
415 m_at_main_prompt = true;
416 mainWin->set_status(_("At main prompt waiting for input ..."));
417 QApplication::restoreOverrideCursor();
420 if (mainWin->m_commDebug) Pmsg0(000, "PROMPT\n");
422 m_at_main_prompt = false;
423 mainWin->set_status(_("At prompt waiting for input ..."));
424 QApplication::restoreOverrideCursor();
426 case BNET_CMD_FAILED:
427 if (mainWin->m_commDebug) Pmsg0(000, "CMD FAILED\n");
428 mainWin->set_status(_("Command failed."));
429 QApplication::restoreOverrideCursor();
431 /* We should not get this one */
433 if (mainWin->m_commDebug) Pmsg0(000, "EOD\n");
434 mainWin->set_status_ready();
435 QApplication::restoreOverrideCursor();
440 case BNET_START_SELECT:
441 if (mainWin->m_commDebug) Pmsg0(000, "START SELECT\n");
442 new selectDialog(m_console);
445 if (mainWin->m_commDebug) Pmsg0(000, "YESNO\n");
446 new yesnoPopUp(m_console);
449 if (mainWin->m_commDebug) Pmsg0(000, "RUN CMD\n");
453 if (mainWin->m_commDebug) Pmsg0(000, "ERROR MSG\n");
454 m_sock->recv(); /* get the message */
455 m_console->display_text(msg());
456 QMessageBox::critical(this, "Error", msg(), QMessageBox::Ok);
458 case BNET_WARNING_MSG:
459 if (mainWin->m_commDebug) Pmsg0(000, "WARNING MSG\n");
460 m_sock->recv(); /* get the message */
461 m_console->display_text(msg());
462 QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
465 if (mainWin->m_commDebug) Pmsg0(000, "INFO MSG\n");
466 m_sock->recv(); /* get the message */
467 m_console->display_text(msg());
468 mainWin->set_status(msg());
471 if (is_bnet_stop(m_sock)) { /* error or term request */
472 if (mainWin->m_commDebug) Pmsg0(000, "BNET STOP\n");
476 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
477 QBrush redBrush(Qt::red);
478 QTreeWidgetItem *item = mainWin->getFromHash(this);
479 item->setForeground(0, redBrush);
480 m_notifier->setEnabled(false);
483 mainWin->set_status(_("Director disconnected."));
484 QApplication::restoreOverrideCursor();
492 /* Called by signal when the Director has output for us */
493 void DirComm::read_dir(int /* fd */)
495 if (mainWin->m_commDebug) Pmsg0(000, "read_dir\n");
496 while (read() >= 0) {
497 m_console->display_text(msg());
502 * When the notifier is enabled, read_dir() will automatically be
503 * called by the Qt event loop when ever there is any output
504 * from the Directory, and read_dir() will then display it on
507 * When we are in a bat dialog, we want to control *all* output
508 * from the Directory, so we set notify to off.
509 * m_console->notifiy(false);
511 void DirComm::notify(bool enable)
513 m_notifier->setEnabled(enable);
516 bool DirComm::is_connectedGui()
518 if (is_connected()) {
521 QString message("Director ");
522 message += " is curerntly disconnected\n Please reconnect!!";
523 QMessageBox::warning(this, tr("Bat"),
524 tr(message.toUtf8().data()), QMessageBox::Ok );
530 * Call-back for reading a passphrase for an encrypted PEM file
531 * This function uses getpass(),
532 * which uses a static buffer and is NOT thread-safe.
534 static int tls_pem_callback(char *buf, int size, const void *userdata)
539 const char *prompt = (const char *)userdata;
540 # if defined(HAVE_WIN32)
542 if (win32_cgets(buf, size) == NULL) {
551 passwd = getpass(prompt);
552 bstrncpy(buf, passwd, size);