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( tr("No Director found.") );
114 mainWin->set_status( tr("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( tr("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( tr(" 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()
341 if (mainWin->m_commDebug) Pmsg0(000, "DisplaytoPrompt\n");
342 while (!m_at_prompt) {
343 if ((stat=read()) > 0) {
345 if (buf.size() >= 8196 || m_messages_pending) {
346 m_console->display_text(buf);
348 m_messages_pending = false;
352 m_console->display_text(buf);
353 if (mainWin->m_commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
356 void DirComm::discardToPrompt()
359 if (mainWin->m_commDebug) Pmsg0(000, "discardToPrompt\n");
360 while (!m_at_prompt) {
361 if ((stat=read()) > 0) {
362 if (mainWin->m_displayAll) m_console->display_text(msg());
365 if (mainWin->m_commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
370 * Blocking read from director
377 stat = bnet_wait_data_intr(m_sock, 1);
381 app->processEvents();
382 if (m_api_set && m_messages_pending) {
383 write_dir(".messages");
384 m_messages_pending = false;
388 stat = m_sock->recv();
390 if (mainWin->m_commDebug) Pmsg1(000, "got: %s\n", m_sock->msg);
392 m_console->display_text("\n");
394 m_at_main_prompt = false;
397 switch (m_sock->msglen) {
398 case BNET_MSGS_PENDING :
399 if (m_notifier->isEnabled()) {
400 if (mainWin->m_commDebug) Pmsg0(000, "MSGS PENDING\n");
401 write_dir(".messages");
403 m_messages_pending = false;
405 m_messages_pending = true;
408 if (mainWin->m_commDebug) Pmsg0(000, "CMD OK\n");
410 m_at_main_prompt = false;
411 mainWin->set_status(_("Command completed ..."));
414 if (mainWin->m_commDebug) Pmsg0(000, "CMD BEGIN\n");
416 m_at_main_prompt = false;
417 mainWin->set_status(_("Processing command ..."));
419 case BNET_MAIN_PROMPT:
420 if (mainWin->m_commDebug) Pmsg0(000, "MAIN PROMPT\n");
422 m_at_main_prompt = true;
423 mainWin->set_status(_("At main prompt waiting for input ..."));
424 QApplication::restoreOverrideCursor();
427 if (mainWin->m_commDebug) Pmsg0(000, "PROMPT\n");
429 m_at_main_prompt = false;
430 mainWin->set_status(_("At prompt waiting for input ..."));
431 QApplication::restoreOverrideCursor();
433 case BNET_CMD_FAILED:
434 if (mainWin->m_commDebug) Pmsg0(000, "CMD FAILED\n");
435 mainWin->set_status(_("Command failed."));
436 QApplication::restoreOverrideCursor();
438 /* We should not get this one */
440 if (mainWin->m_commDebug) Pmsg0(000, "EOD\n");
441 mainWin->set_status_ready();
442 QApplication::restoreOverrideCursor();
447 case BNET_START_SELECT:
448 if (mainWin->m_commDebug) Pmsg0(000, "START SELECT\n");
449 new selectDialog(m_console);
452 if (mainWin->m_commDebug) Pmsg0(000, "YESNO\n");
453 new yesnoPopUp(m_console);
456 if (mainWin->m_commDebug) Pmsg0(000, "RUN CMD\n");
460 if (mainWin->m_commDebug) Pmsg0(000, "ERROR MSG\n");
461 m_sock->recv(); /* get the message */
462 m_console->display_text(msg());
463 QMessageBox::critical(this, "Error", msg(), QMessageBox::Ok);
465 case BNET_WARNING_MSG:
466 if (mainWin->m_commDebug) Pmsg0(000, "WARNING MSG\n");
467 m_sock->recv(); /* get the message */
468 m_console->display_text(msg());
469 QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
472 if (mainWin->m_commDebug) Pmsg0(000, "INFO MSG\n");
473 m_sock->recv(); /* get the message */
474 m_console->display_text(msg());
475 mainWin->set_status(msg());
478 if (is_bnet_stop(m_sock)) { /* error or term request */
479 if (mainWin->m_commDebug) Pmsg0(000, "BNET STOP\n");
483 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
484 QBrush redBrush(Qt::red);
485 QTreeWidgetItem *item = mainWin->getFromHash(this);
486 item->setForeground(0, redBrush);
487 m_notifier->setEnabled(false);
490 mainWin->set_status(_("Director disconnected."));
491 QApplication::restoreOverrideCursor();
499 /* Called by signal when the Director has output for us */
500 void DirComm::read_dir(int /* fd */)
502 if (mainWin->m_commDebug) Pmsg0(000, "read_dir\n");
503 while (read() >= 0) {
504 m_console->display_text(msg());
509 * When the notifier is enabled, read_dir() will automatically be
510 * called by the Qt event loop when ever there is any output
511 * from the Directory, and read_dir() will then display it on
514 * When we are in a bat dialog, we want to control *all* output
515 * from the Directory, so we set notify to off.
516 * m_console->notifiy(false);
518 void DirComm::notify(bool enable)
520 m_notifier->setEnabled(enable);
523 bool DirComm::is_connectedGui()
525 if (is_connected()) {
528 QString message( tr("Director is currently disconnected\n Please reconnect!"));
529 QMessageBox::warning(this, "Bat",
530 message.toUtf8().data(), QMessageBox::Ok );
536 * Call-back for reading a passphrase for an encrypted PEM file
537 * This function uses getpass(),
538 * which uses a static buffer and is NOT thread-safe.
540 static int tls_pem_callback(char *buf, int size, const void *userdata)
545 const char *prompt = (const char *)userdata;
546 # if defined(HAVE_WIN32)
548 if (win32_cgets(buf, size) == NULL) {
557 passwd = getpass(prompt);
558 bstrncpy(buf, passwd, size);