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 plus additions
11 that are listed in the file LICENSE.
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
37 #include <QAbstractEventDispatcher>
44 Console::Console(QStackedWidget *parent)
55 m_at_main_prompt = false;
56 m_textEdit = textEdit; /* our console screen */
57 m_cursor = new QTextCursor(m_textEdit->document());
58 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
60 /* Check for messages every 5 seconds */
61 m_timer = new QTimer(this);
62 QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
70 void Console::poll_messages()
72 m_messages_pending = true;
73 if (m_at_main_prompt) {
79 /* Terminate any open socket */
80 void Console::terminate()
90 * Connect to Director. If there are more than one, put up
91 * a modal dialog so that the user chooses one.
93 void Console::connect()
98 m_textEdit = textEdit; /* our console screen */
101 mainWin->set_status("No Director found.");
105 mainWin->set_status("Already connected.");
109 memset(&jcr, 0, sizeof(jcr));
111 mainWin->set_statusf(_("Connecting to Director %s:%d"), m_dir->address, m_dir->DIRport);
112 display_textf(_("Connecting to Director %s:%d\n\n"), m_dir->address, m_dir->DIRport);
114 /* Give GUI a chance */
115 app->processEvents();
118 /* If cons==NULL, default console will be used */
119 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
122 if (m_dir->heartbeat_interval) {
123 heart_beat = m_dir->heartbeat_interval;
125 heart_beat = cons->heartbeat_interval;
130 m_sock = bnet_connect(NULL, 5, 15, heart_beat,
131 _("Director daemon"), m_dir->address,
132 NULL, m_dir->DIRport, 0);
133 if (m_sock == NULL) {
134 mainWin->set_status("Connection failed");
137 /* Update page selector to green to indicate that Console is connected */
138 mainWin->actionConnect->setIcon(QIcon(":images/connected.png"));
139 QBrush greenBrush(Qt::green);
140 QTreeWidgetItem *item = mainWin->getFromHash(this);
141 item->setForeground(0, greenBrush);
144 jcr.dir_bsock = m_sock;
146 if (!authenticate_director(&jcr, m_dir, cons)) {
147 display_text(m_sock->msg);
151 /* Give GUI a chance */
152 app->processEvents();
154 mainWin->set_status(_("Initializing ..."));
156 /* Set up input notifier */
157 m_notifier = new QSocketNotifier(m_sock->m_fd, QSocketNotifier::Read, 0);
158 QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
164 dir_cmd(".jobs", job_list);
165 dir_cmd(".clients", client_list);
166 dir_cmd(".filesets", fileset_list);
167 dir_cmd(".msgs", messages_list);
168 dir_cmd(".pools", pool_list);
169 dir_cmd(".storage", storage_list);
170 dir_cmd(".types", type_list);
171 dir_cmd(".levels", level_list);
173 mainWin->set_status(_("Connected"));
177 bool Console::dir_cmd(QString &cmd, QStringList &results)
179 return dir_cmd(cmd.toUtf8().data(), results);
183 * Send a command to the Director, and return the
184 * results in a QStringList.
186 bool Console::dir_cmd(const char *cmd, QStringList &results)
192 while ((stat = read()) > 0) {
193 if (mainWin->m_displayAll) display_text(msg());
194 strip_trailing_junk(msg());
199 return true; /* ***FIXME*** return any command error */
202 bool Console::sql_cmd(QString &query, QStringList &results)
204 return sql_cmd(query.toUtf8().data(), results);
208 * Send an sql query to the Director, and return the
209 * results in a QStringList.
211 bool Console::sql_cmd(const char *query, QStringList &results)
213 if (!is_connectedGui())
216 POOL_MEM cmd(PM_MESSAGE);
220 pm_strcpy(cmd, ".sql query=\"");
221 pm_strcat(cmd, query);
222 pm_strcat(cmd, "\"");
224 while ((stat = read()) > 0) {
225 if (mainWin->m_displayAll) display_text(msg());
226 strip_trailing_junk(msg());
231 return true; /* ***FIXME*** return any command error */
236 * Send a job name to the director, and read all the resulting
239 bool Console::get_job_defaults(struct job_defaults &job_defs)
247 scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
249 while ((stat = read()) > 0) {
250 if (mainWin->m_displayAll) display_text(msg());
251 def = strchr(msg(), '=');
255 /* Pointer to default value */
257 strip_trailing_junk(def);
259 if (strcmp(msg(), "job") == 0) {
260 if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
265 if (strcmp(msg(), "pool") == 0) {
266 job_defs.pool_name = def;
269 if (strcmp(msg(), "messages") == 0) {
270 job_defs.messages_name = def;
273 if (strcmp(msg(), "client") == 0) {
274 job_defs.client_name = def;
277 if (strcmp(msg(), "storage") == 0) {
278 job_defs.store_name = def;
281 if (strcmp(msg(), "where") == 0) {
282 job_defs.where = def;
285 if (strcmp(msg(), "level") == 0) {
286 job_defs.level = def;
289 if (strcmp(msg(), "type") == 0) {
293 if (strcmp(msg(), "fileset") == 0) {
294 job_defs.fileset_name = def;
297 if (strcmp(msg(), "catalog") == 0) {
298 job_defs.catalog_name = def;
301 if (strcmp(msg(), "enabled") == 0) {
302 job_defs.enabled = *def == '1' ? true : false;
308 bsnprintf(cmd, sizeof(cmd), "job=%s pool=%s client=%s storage=%s where=%s\n"
309 "level=%s type=%s fileset=%s catalog=%s enabled=%d\n",
310 job_defs.job_name.toUtf8().data(), job_defs.pool_name.toUtf8().data(),
311 job_defs.client_name.toUtf8().data(),
312 job_defs.pool_name.toUtf8().data(), job_defs.messages_name.toUtf8().data(),
313 job_defs.store_name.toUtf8().data(),
314 job_defs.where.toUtf8().data(), job_defs.level.toUtf8().data(),
315 job_defs.type.toUtf8().data(), job_defs.fileset_name.toUtf8().data(),
316 job_defs.catalog_name.toUtf8().data(), job_defs.enabled);
329 * Save user settings associated with this console
331 void Console::writeSettings()
333 QFont font = get_font();
335 QSettings settings(m_dir->name(), "bat");
336 settings.beginGroup("Console");
337 settings.setValue("consoleFont", font.family());
338 settings.setValue("consolePointSize", font.pointSize());
339 settings.setValue("consoleFixedPitch", font.fixedPitch());
344 * Read and restore user settings associated with this console
346 void Console::readSettings()
348 QFont font = get_font();
350 QSettings settings(m_dir->name(), "bat");
351 settings.beginGroup("Console");
352 font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
353 font.setPointSize(settings.value("consolePointSize", 10).toInt());
354 font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
356 m_textEdit->setFont(font);
360 * Set the console textEdit font
362 void Console::set_font()
365 QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
367 m_textEdit->setFont(font);
372 * Get the console text edit font
374 const QFont Console::get_font()
376 return m_textEdit->font();
380 * Slot for responding to status dir button on button bar
382 void Console::status_dir()
384 QString cmd("status dir");
389 * Slot for responding to messages button on button bar
391 void Console::messages()
393 QString cmd(".messages");
398 * Put text into the console window
400 void Console::display_textf(const char *fmt, ...)
405 va_start(arg_ptr, fmt);
406 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
411 void Console::display_text(const QString buf)
413 m_cursor->movePosition(QTextCursor::End);
414 m_cursor->insertText(buf);
418 void Console::display_text(const char *buf)
420 m_cursor->movePosition(QTextCursor::End);
421 m_cursor->insertText(buf);
424 void Console::display_html(const QString buf)
426 m_cursor->movePosition(QTextCursor::End);
427 m_cursor->insertHtml(buf);
430 /* Position cursor to end of screen */
431 void Console::update_cursor()
433 QApplication::restoreOverrideCursor();
434 m_textEdit->moveCursor(QTextCursor::End);
435 m_textEdit->ensureCursorVisible();
439 * This should be moved into a bSocket class
449 /* Send a command to the Director */
450 void Console::write_dir(const char *msg)
453 mainWin->set_status(_("Processing command ..."));
454 QApplication::setOverrideCursor(Qt::WaitCursor);
457 mainWin->set_status(" Director not connected. Click on connect button.");
458 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
459 QBrush redBrush(Qt::red);
460 QTreeWidgetItem *item = mainWin->getFromHash(this);
461 item->setForeground(0, redBrush);
463 m_at_main_prompt = false;
467 int Console::write(const QString msg)
469 return write(msg.toUtf8().data());
472 int Console::write(const char *msg)
474 m_sock->msglen = pm_strcpy(m_sock->msg, msg);
476 m_at_main_prompt = false;
477 if (mainWin->m_commDebug) Pmsg1(000, "send: %s\n", msg);
478 return m_sock->send();
482 * Get to main command prompt -- i.e. abort any subcommand
484 void Console::beginNewCommand()
486 for (int i=0; i < 3; i++) {
489 if (mainWin->m_displayAll) display_text(msg());
491 if (m_at_main_prompt) {
498 void Console::displayToPrompt()
501 if (mainWin->m_commDebug) Pmsg0(000, "DisplaytoPrompt\n");
502 while (!m_at_prompt) {
503 if ((stat=read()) > 0) {
507 if (mainWin->m_commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
510 void Console::discardToPrompt()
513 if (mainWin->m_commDebug) Pmsg0(000, "discardToPrompt\n");
514 while (!m_at_prompt) {
515 if ((stat=read()) > 0) {
516 if (mainWin->m_displayAll) display_text(msg());
519 if (mainWin->m_commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
524 * Blocking read from director
531 stat = bnet_wait_data_intr(m_sock, 1);
535 app->processEvents();
536 if (m_api_set && m_messages_pending) {
537 write_dir(".messages");
538 m_messages_pending = false;
541 stat = m_sock->recv();
546 m_at_main_prompt = false;
548 if (mainWin->m_commDebug) Pmsg1(000, "got: %s", m_sock->msg);
550 switch (m_sock->msglen) {
551 case BNET_MSGS_PENDING:
552 if (mainWin->m_commDebug) Pmsg0(000, "MSGS PENDING\n");
553 write_dir(".messages");
555 m_messages_pending = false;
558 if (mainWin->m_commDebug) Pmsg0(000, "CMD OK\n");
560 m_at_main_prompt = false;
563 if (mainWin->m_commDebug) Pmsg0(000, "CMD BEGIN\n");
565 m_at_main_prompt = false;
567 case BNET_MAIN_PROMPT:
568 if (mainWin->m_commDebug) Pmsg0(000, "MAIN PROMPT\n");
570 m_at_main_prompt = true;
571 mainWin->set_status(_("At prompt waiting for input ..."));
573 QApplication::restoreOverrideCursor();
576 if (mainWin->m_commDebug) Pmsg0(000, "PROMPT\n");
578 m_at_main_prompt = false;
579 mainWin->set_status(_("At prompt waiting for input ..."));
581 QApplication::restoreOverrideCursor();
583 case BNET_CMD_FAILED:
584 if (mainWin->m_commDebug) Pmsg0(000, "CMD FAILED\n");
585 mainWin->set_status(_("Command failed. At prompt waiting for input ..."));
587 QApplication::restoreOverrideCursor();
589 /* We should not get this one */
591 if (mainWin->m_commDebug) Pmsg0(000, "EOD\n");
592 mainWin->set_status_ready();
594 QApplication::restoreOverrideCursor();
599 case BNET_START_SELECT:
600 if (mainWin->m_commDebug) Pmsg0(000, "START SELECT\n");
601 new selectDialog(this);
604 if (mainWin->m_commDebug) Pmsg0(000, "RUN CMD\n");
608 if (mainWin->m_commDebug) Pmsg0(000, "ERROR MSG\n");
609 m_sock->recv(); /* get the message */
611 QMessageBox::critical(this, "Error", msg(), QMessageBox::Ok);
613 case BNET_WARNING_MSG:
614 if (mainWin->m_commDebug) Pmsg0(000, "WARNING MSG\n");
615 m_sock->recv(); /* get the message */
617 QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
620 if (mainWin->m_commDebug) Pmsg0(000, "INFO MSG\n");
621 m_sock->recv(); /* get the message */
623 mainWin->set_status(msg());
626 if (is_bnet_stop(m_sock)) { /* error or term request */
627 if (mainWin->m_commDebug) Pmsg0(000, "BNET STOP\n");
630 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
631 QBrush redBrush(Qt::red);
632 QTreeWidgetItem *item = mainWin->getFromHash(this);
633 item->setForeground(0, redBrush);
634 m_notifier->setEnabled(false);
637 mainWin->set_status(_("Director disconnected."));
638 QApplication::restoreOverrideCursor();
646 /* Called by signal when the Director has output for us */
647 void Console::read_dir(int /* fd */)
649 if (mainWin->m_commDebug) Pmsg0(000, "read_dir\n");
650 while (read() >= 0) {
656 * When the notifier is enabled, read_dir() will automatically be
657 * called by the Qt event loop when ever there is any output
658 * from the Directory, and read_dir() will then display it on
661 * When we are in a bat dialog, we want to control *all* output
662 * from the Directory, so we set notify to off.
663 * m_console->notifiy(false);
665 void Console::notify(bool enable)
667 m_notifier->setEnabled(enable);
670 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
672 m_directorTreeItem = item;
675 void Console::setDirRes(DIRRES *dir)
681 * To have the ability to get the name of the director resource.
683 void Console::getDirResName(QString &name_returned)
685 name_returned = m_dir->name();
688 bool Console::is_connectedGui()
690 if (is_connected()) {
693 QString message("Director ");
694 message += " is curerntly disconnected\n Please reconnect!!";
695 QMessageBox::warning(this, tr("Bat"),
696 tr(message.toUtf8().data()), QMessageBox::Ok );
702 * A temporary function to prevent connecting to the director if the director
703 * is busy with a restore.
705 bool Console::preventInUseConnect()
707 if (!is_connected()) {
708 QString message("Director ");
709 message += m_dir->name();
710 message += " is curerntly disconnected\n Please reconnect!!";
711 QMessageBox::warning(this, tr("Bat"),
712 tr(message.toUtf8().data()), QMessageBox::Ok );
714 } else if (!m_at_main_prompt){
715 QString message("Director ");
716 message += m_dir->name();
717 message += " is curerntly busy\n Please complete restore or other "
718 " operation !! This is a limitation that will be resolved before a beta"
719 " release. This is currently an alpa release.";
720 QMessageBox::warning(this, tr("Bat"),
721 tr(message.toUtf8().data()), QMessageBox::Ok );
723 } else if (!m_at_prompt){
724 QString message("Director ");
725 message += m_dir->name();
726 message += " is curerntly not at a prompt\n Please try again!!";
727 QMessageBox::warning(this, tr("Bat"),
728 tr(message.toUtf8().data()), QMessageBox::Ok );