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.
33 * Kern Sibbald, January MMVII
43 static int tls_pem_callback(char *buf, int size, const void *userdata);
46 Console::Console(QStackedWidget *parent)
56 m_at_main_prompt = false;
57 m_textEdit = textEdit; /* our console screen */
58 m_cursor = new QTextCursor(m_textEdit->document());
59 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
62 m_contextActions.append(actionStatusDir);
63 m_contextActions.append(actionConsoleHelp);
64 m_contextActions.append(actionRequestMessages);
65 m_contextActions.append(actionConsoleReload);
66 connect(actionStatusDir, SIGNAL(triggered()), this, SLOT(status_dir()));
67 connect(actionConsoleHelp, SIGNAL(triggered()), this, SLOT(consoleHelp()));
68 connect(actionConsoleReload, SIGNAL(triggered()), this, SLOT(consoleReload()));
69 connect(actionRequestMessages, SIGNAL(triggered()), this, SLOT(messages()));
76 void Console::startTimer()
78 m_timer = new QTimer(this);
79 QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
80 m_timer->start(mainWin->m_checkMessagesInterval*1000);
83 void Console::stopTimer()
86 QWidget::disconnect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
93 void Console::poll_messages()
95 m_messages_pending = true;
96 if ((m_at_main_prompt) && (mainWin->m_checkMessages)){
102 /* Terminate any open socket */
103 void Console::terminate()
113 * Connect to Director.
115 void Console::connect_dir()
124 m_textEdit = textEdit; /* our console screen */
127 mainWin->set_status("No Director found.");
131 mainWin->set_status("Already connected.");
135 memset(jcr, 0, sizeof(JCR));
137 mainWin->set_statusf(_("Connecting to Director %s:%d"), m_dir->address, m_dir->DIRport);
138 display_textf(_("Connecting to Director %s:%d\n\n"), m_dir->address, m_dir->DIRport);
140 /* Give GUI a chance */
141 app->processEvents();
144 /* If cons==NULL, default console will be used */
145 cons = (CONRES *)GetNextRes(R_CONSOLE, NULL);
148 /* Initialize Console TLS context once */
149 if (cons && !cons->tls_ctx && (cons->tls_enable || cons->tls_require)) {
150 /* Generate passphrase prompt */
151 bsnprintf(buf, sizeof(buf), "Passphrase for Console \"%s\" TLS private key: ",
154 /* Initialize TLS context:
155 * Args: CA certfile, CA certdir, Certfile, Keyfile,
156 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer
158 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
159 cons->tls_ca_certdir, cons->tls_certfile,
160 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
162 if (!cons->tls_ctx) {
163 display_textf(_("Failed to initialize TLS context for Console \"%s\".\n"),
169 /* Initialize Director TLS context once */
170 if (!m_dir->tls_ctx && (m_dir->tls_enable || m_dir->tls_require)) {
171 /* Generate passphrase prompt */
172 bsnprintf(buf, sizeof(buf), "Passphrase for Director \"%s\" TLS private key: ",
175 /* Initialize TLS context:
176 * Args: CA certfile, CA certdir, Certfile, Keyfile,
177 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
178 m_dir->tls_ctx = new_tls_context(m_dir->tls_ca_certfile,
179 m_dir->tls_ca_certdir, m_dir->tls_certfile,
180 m_dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
182 if (!m_dir->tls_ctx) {
183 display_textf(_("Failed to initialize TLS context for Director \"%s\".\n"),
185 mainWin->set_status("Connection failed");
190 if (m_dir->heartbeat_interval) {
191 heart_beat = m_dir->heartbeat_interval;
193 heart_beat = cons->heartbeat_interval;
198 m_sock = bnet_connect(NULL, 5, 15, heart_beat,
199 _("Director daemon"), m_dir->address,
200 NULL, m_dir->DIRport, 0);
201 if (m_sock == NULL) {
202 mainWin->set_status("Connection failed");
205 /* Update page selector to green to indicate that Console is connected */
206 mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
207 QBrush greenBrush(Qt::green);
208 QTreeWidgetItem *item = mainWin->getFromHash(this);
209 item->setForeground(0, greenBrush);
212 jcr->dir_bsock = m_sock;
214 if (!authenticate_director(jcr, m_dir, cons, buf, sizeof(buf))) {
223 /* Give GUI a chance */
224 app->processEvents();
226 mainWin->set_status(_("Initializing ..."));
228 /* Set up input notifier */
229 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
230 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
238 fileset_list.clear();
239 fileset_list.clear();
240 messages_list.clear();
242 storage_list.clear();
245 dir_cmd(".jobs", job_list);
246 dir_cmd(".clients", client_list);
247 dir_cmd(".filesets", fileset_list);
248 dir_cmd(".msgs", messages_list);
249 dir_cmd(".pools", pool_list);
250 dir_cmd(".storage", storage_list);
251 dir_cmd(".types", type_list);
252 dir_cmd(".levels", level_list);
254 mainWin->set_status(_("Connected"));
255 startTimer(); /* start message timer */
262 bool Console::dir_cmd(QString &cmd, QStringList &results)
264 return dir_cmd(cmd.toUtf8().data(), results);
268 * Send a command to the Director, and return the
269 * results in a QStringList.
271 bool Console::dir_cmd(const char *cmd, QStringList &results)
277 while ((stat = read()) > 0) {
278 if (mainWin->m_displayAll) display_text(msg());
279 strip_trailing_junk(msg());
284 return true; /* ***FIXME*** return any command error */
287 bool Console::sql_cmd(QString &query, QStringList &results)
289 return sql_cmd(query.toUtf8().data(), results);
293 * Send an sql query to the Director, and return the
294 * results in a QStringList.
296 bool Console::sql_cmd(const char *query, QStringList &results)
299 POOL_MEM cmd(PM_MESSAGE);
301 if (!is_connectedGui()) {
307 pm_strcpy(cmd, ".sql query=\"");
308 pm_strcat(cmd, query);
309 pm_strcat(cmd, "\"");
311 while ((stat = read()) > 0) {
312 if (mainWin->m_displayAll) display_text(msg());
313 strip_trailing_junk(msg());
318 return true; /* ***FIXME*** return any command error */
323 * Send a job name to the director, and read all the resulting
326 bool Console::get_job_defaults(struct job_defaults &job_defs)
334 scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
336 while ((stat = read()) > 0) {
337 if (mainWin->m_displayAll) display_text(msg());
338 def = strchr(msg(), '=');
342 /* Pointer to default value */
344 strip_trailing_junk(def);
346 if (strcmp(msg(), "job") == 0) {
347 if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
352 if (strcmp(msg(), "pool") == 0) {
353 job_defs.pool_name = def;
356 if (strcmp(msg(), "messages") == 0) {
357 job_defs.messages_name = def;
360 if (strcmp(msg(), "client") == 0) {
361 job_defs.client_name = def;
364 if (strcmp(msg(), "storage") == 0) {
365 job_defs.store_name = def;
368 if (strcmp(msg(), "where") == 0) {
369 job_defs.where = def;
372 if (strcmp(msg(), "level") == 0) {
373 job_defs.level = def;
376 if (strcmp(msg(), "type") == 0) {
380 if (strcmp(msg(), "fileset") == 0) {
381 job_defs.fileset_name = def;
384 if (strcmp(msg(), "catalog") == 0) {
385 job_defs.catalog_name = def;
388 if (strcmp(msg(), "enabled") == 0) {
389 job_defs.enabled = *def == '1' ? true : false;
404 * Save user settings associated with this console
406 void Console::writeSettings()
408 QFont font = get_font();
410 QSettings settings(m_dir->name(), "bat");
411 settings.beginGroup("Console");
412 settings.setValue("consoleFont", font.family());
413 settings.setValue("consolePointSize", font.pointSize());
414 settings.setValue("consoleFixedPitch", font.fixedPitch());
419 * Read and restore user settings associated with this console
421 void Console::readSettings()
423 QFont font = get_font();
425 QSettings settings(m_dir->name(), "bat");
426 settings.beginGroup("Console");
427 font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
428 font.setPointSize(settings.value("consolePointSize", 10).toInt());
429 font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
431 m_textEdit->setFont(font);
435 * Set the console textEdit font
437 void Console::set_font()
440 QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
442 m_textEdit->setFont(font);
447 * Get the console text edit font
449 const QFont Console::get_font()
451 return m_textEdit->font();
455 * Slot for responding to status dir button on button bar
457 void Console::status_dir()
459 QString cmd("status dir");
464 * Slot for responding to messages button on button bar
466 void Console::messages()
468 QString cmd(".messages");
473 * Put text into the console window
475 void Console::display_textf(const char *fmt, ...)
480 va_start(arg_ptr, fmt);
481 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
486 void Console::display_text(const QString buf)
488 m_cursor->insertText(buf);
493 void Console::display_text(const char *buf)
495 m_cursor->insertText(buf);
499 void Console::display_html(const QString buf)
501 m_cursor->insertHtml(buf);
505 /* Position cursor to end of screen */
506 void Console::update_cursor()
508 QApplication::restoreOverrideCursor();
509 m_textEdit->moveCursor(QTextCursor::End);
510 m_textEdit->ensureCursorVisible();
514 * This should be moved into a bSocket class
524 /* Send a command to the Director */
525 void Console::write_dir(const char *msg)
528 mainWin->set_status(_("Processing command ..."));
529 QApplication::setOverrideCursor(Qt::WaitCursor);
532 mainWin->set_status(" Director not connected. Click on connect button.");
533 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
534 QBrush redBrush(Qt::red);
535 QTreeWidgetItem *item = mainWin->getFromHash(this);
536 item->setForeground(0, redBrush);
538 m_at_main_prompt = false;
542 int Console::write(const QString msg)
544 return write(msg.toUtf8().data());
547 int Console::write(const char *msg)
552 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
554 m_at_main_prompt = false;
555 if (mainWin->m_commDebug) Pmsg1(000, "send: %s\n", msg);
556 return m_sock->send();
561 * Get to main command prompt -- i.e. abort any subcommand
563 void Console::beginNewCommand()
565 for (int i=0; i < 3; i++) {
568 if (mainWin->m_displayAll) display_text(msg());
570 if (m_at_main_prompt) {
577 void Console::displayToPrompt()
580 if (mainWin->m_commDebug) Pmsg0(000, "DisplaytoPrompt\n");
581 while (!m_at_prompt) {
582 if ((stat=read()) > 0) {
586 if (mainWin->m_commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
589 void Console::discardToPrompt()
592 if (mainWin->m_commDebug) Pmsg0(000, "discardToPrompt\n");
593 while (!m_at_prompt) {
594 if ((stat=read()) > 0) {
595 if (mainWin->m_displayAll) display_text(msg());
598 if (mainWin->m_commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
603 * Blocking read from director
610 stat = bnet_wait_data_intr(m_sock, 1);
614 app->processEvents();
615 if (m_api_set && m_messages_pending) {
616 write_dir(".messages");
617 m_messages_pending = false;
621 stat = m_sock->recv();
623 if (mainWin->m_commDebug) Pmsg1(000, "got: %s\n", m_sock->msg);
627 m_at_main_prompt = false;
630 switch (m_sock->msglen) {
631 case BNET_MSGS_PENDING :
632 if (m_notifier->isEnabled()) {
633 if (mainWin->m_commDebug) Pmsg0(000, "MSGS PENDING\n");
634 write_dir(".messages");
636 m_messages_pending = false;
638 m_messages_pending = true;
641 if (mainWin->m_commDebug) Pmsg0(000, "CMD OK\n");
643 m_at_main_prompt = false;
644 mainWin->set_status(_("Command completed ..."));
647 if (mainWin->m_commDebug) Pmsg0(000, "CMD BEGIN\n");
649 m_at_main_prompt = false;
650 mainWin->set_status(_("Processing command ..."));
652 case BNET_MAIN_PROMPT:
653 if (mainWin->m_commDebug) Pmsg0(000, "MAIN PROMPT\n");
655 m_at_main_prompt = true;
656 mainWin->set_status(_("At main prompt waiting for input ..."));
657 QApplication::restoreOverrideCursor();
660 if (mainWin->m_commDebug) Pmsg0(000, "PROMPT\n");
662 m_at_main_prompt = false;
663 mainWin->set_status(_("At prompt waiting for input ..."));
664 QApplication::restoreOverrideCursor();
666 case BNET_CMD_FAILED:
667 if (mainWin->m_commDebug) Pmsg0(000, "CMD FAILED\n");
668 mainWin->set_status(_("Command failed."));
669 QApplication::restoreOverrideCursor();
671 /* We should not get this one */
673 if (mainWin->m_commDebug) Pmsg0(000, "EOD\n");
674 mainWin->set_status_ready();
675 QApplication::restoreOverrideCursor();
680 case BNET_START_SELECT:
681 if (mainWin->m_commDebug) Pmsg0(000, "START SELECT\n");
682 new selectDialog(this);
685 if (mainWin->m_commDebug) Pmsg0(000, "YESNO\n");
686 new yesnoPopUp(this);
689 if (mainWin->m_commDebug) Pmsg0(000, "RUN CMD\n");
693 if (mainWin->m_commDebug) Pmsg0(000, "ERROR MSG\n");
694 m_sock->recv(); /* get the message */
696 QMessageBox::critical(this, "Error", msg(), QMessageBox::Ok);
698 case BNET_WARNING_MSG:
699 if (mainWin->m_commDebug) Pmsg0(000, "WARNING MSG\n");
700 m_sock->recv(); /* get the message */
702 QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
705 if (mainWin->m_commDebug) Pmsg0(000, "INFO MSG\n");
706 m_sock->recv(); /* get the message */
708 mainWin->set_status(msg());
711 if (is_bnet_stop(m_sock)) { /* error or term request */
712 if (mainWin->m_commDebug) Pmsg0(000, "BNET STOP\n");
716 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
717 QBrush redBrush(Qt::red);
718 QTreeWidgetItem *item = mainWin->getFromHash(this);
719 item->setForeground(0, redBrush);
720 m_notifier->setEnabled(false);
723 mainWin->set_status(_("Director disconnected."));
724 QApplication::restoreOverrideCursor();
732 /* Called by signal when the Director has output for us */
733 void Console::read_dir(int /* fd */)
735 if (mainWin->m_commDebug) Pmsg0(000, "read_dir\n");
736 while (read() >= 0) {
742 * When the notifier is enabled, read_dir() will automatically be
743 * called by the Qt event loop when ever there is any output
744 * from the Directory, and read_dir() will then display it on
747 * When we are in a bat dialog, we want to control *all* output
748 * from the Directory, so we set notify to off.
749 * m_console->notifiy(false);
751 void Console::notify(bool enable)
753 m_notifier->setEnabled(enable);
756 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
758 m_directorTreeItem = item;
761 void Console::setDirRes(DIRRES *dir)
767 * To have the ability to get the name of the director resource.
769 void Console::getDirResName(QString &name_returned)
771 name_returned = m_dir->name();
774 bool Console::is_connectedGui()
776 if (is_connected()) {
779 QString message("Director ");
780 message += " is currently disconnected\n Please reconnect!!";
781 QMessageBox::warning(this, "Bat",
782 tr(message.toUtf8().data()), QMessageBox::Ok );
788 * A temporary function to prevent connecting to the director if the director
789 * is busy with a restore.
791 bool Console::preventInUseConnect()
793 if (!is_connected()) {
794 QString message("Director ");
795 message += m_dir->name();
796 message += " is currently disconnected\n Please reconnect!!";
797 QMessageBox::warning(this, "Bat",
798 tr(message.toUtf8().data()), QMessageBox::Ok );
800 } else if (!m_at_main_prompt){
801 QString message("Director ");
802 message += m_dir->name();
803 message += " is currently busy\n Please complete restore or other "
804 " operation !! This is a limitation that will be resolved before a beta"
805 " release. This is currently an alpha release.";
806 QMessageBox::warning(this, "Bat",
807 tr(message.toUtf8().data()), QMessageBox::Ok );
809 } else if (!m_at_prompt){
810 QString message("Director ");
811 message += m_dir->name();
812 message += " is currently not at a prompt\n Please try again!!";
813 QMessageBox::warning(this, "Bat",
814 tr(message.toUtf8().data()), QMessageBox::Ok );
822 * Call-back for reading a passphrase for an encrypted PEM file
823 * This function uses getpass(),
824 * which uses a static buffer and is NOT thread-safe.
826 static int tls_pem_callback(char *buf, int size, const void *userdata)
831 const char *prompt = (const char *)userdata;
832 # if defined(HAVE_WIN32)
834 if (win32_cgets(buf, size) == NULL) {
843 passwd = getpass(prompt);
844 bstrncpy(buf, passwd, size);
853 /* Slot for responding to page selectors status help command */
854 void Console::consoleHelp()
860 /* Slot for responding to page selectors reload bacula-dir.conf */
861 void Console::consoleReload()
863 QString cmd("reload");
867 /* Function to get a list of volumes */
868 void Console::getVolumeList(QStringList &volumeList)
870 QString query("SELECT VolumeName AS Media FROM Media ORDER BY Media");
871 if (mainWin->m_sqlDebug) {
872 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
875 if (sql_cmd(query, results)) {
877 QStringList fieldlist;
878 /* Iterate through the lines of results. */
879 foreach (QString resultline, results) {
880 fieldlist = resultline.split("\t");
881 volumeList.append(fieldlist[0]);
882 } /* foreach resultline */
883 } /* if results from query */
886 /* Function to get a list of volumes */
887 void Console::getStatusList(QStringList &statusLongList)
889 QString statusQuery("SELECT JobStatusLong FROM Status");
890 if (mainWin->m_sqlDebug) {
891 Pmsg1(000, "Query cmd : %s\n",statusQuery.toUtf8().data());
893 QStringList statusResults;
894 if (sql_cmd(statusQuery, statusResults)) {
896 QStringList fieldlist;
897 /* Iterate through the lines of results. */
898 foreach (QString resultline, statusResults) {
899 fieldlist = resultline.split("\t");
900 statusLongList.append(fieldlist[0]);
901 } /* foreach resultline */
902 } /* if results from statusquery */