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->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(".messages", 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 (g_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 (g_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 (g_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 (g_commDebug) Pmsg1(000, "send: %s\n", msg);
478 return m_sock->send();
482 * Get to main command prompt
484 void Console::beginNewCommand()
488 if (g_displayAll) display_text(msg());
492 if (g_displayAll) display_text(msg());
496 if (g_displayAll) display_text(msg());
501 void Console::displayToPrompt()
504 if (g_commDebug) Pmsg0(000, "DisplaytoPrompt\n");
505 while (!m_at_prompt) {
506 if ((stat=read()) > 0) {
510 if (g_commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
513 void Console::discardToPrompt()
516 if (g_commDebug) Pmsg0(000, "discardToPrompt\n");
517 while (!m_at_prompt) {
518 if ((stat=read()) > 0) {
519 if (g_displayAll) display_text(msg());
522 if (g_commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
527 * Blocking read from director
534 stat = bnet_wait_data_intr(m_sock, 1);
538 app->processEvents();
539 if (m_api_set && m_messages_pending) {
540 write_dir(".messages");
541 m_messages_pending = false;
544 stat = m_sock->recv();
549 m_at_main_prompt = false;
551 if (g_commDebug) Pmsg1(000, "got: %s", m_sock->msg);
553 switch (m_sock->msglen) {
554 case BNET_MSGS_PENDING:
555 if (g_commDebug) Pmsg0(000, "MSGS PENDING\n");
556 write_dir(".messages");
558 m_messages_pending = false;
561 if (g_commDebug) Pmsg0(000, "CMD OK\n");
563 m_at_main_prompt = false;
566 if (g_commDebug) Pmsg0(000, "CMD BEGIN\n");
568 m_at_main_prompt = false;
570 case BNET_MAIN_PROMPT:
571 if (g_commDebug) Pmsg0(000, "PROMPT\n");
573 m_at_main_prompt = true;
574 mainWin->set_status(_("At prompt waiting for input ..."));
576 QApplication::restoreOverrideCursor();
579 if (g_commDebug) Pmsg0(000, "PROMPT\n");
581 m_at_main_prompt = false;
582 mainWin->set_status(_("At prompt waiting for input ..."));
584 QApplication::restoreOverrideCursor();
586 case BNET_CMD_FAILED:
587 if (g_commDebug) Pmsg0(000, "CMD FAIL\n");
588 mainWin->set_status(_("Command failed. At prompt waiting for input ..."));
590 QApplication::restoreOverrideCursor();
592 /* We should not get this one */
594 if (g_commDebug) Pmsg0(000, "EOD\n");
595 mainWin->set_status_ready();
597 QApplication::restoreOverrideCursor();
602 case BNET_START_SELECT:
603 new selectDialog(this);
609 m_sock->recv(); /* get the message */
611 QMessageBox::critical(this, "Error", msg(), QMessageBox::Ok);
613 case BNET_WARNING_MSG:
614 m_sock->recv(); /* get the message */
616 QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
619 m_sock->recv(); /* get the message */
621 mainWin->set_status(msg());
624 if (is_bnet_stop(m_sock)) { /* error or term request */
627 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
628 QBrush redBrush(Qt::red);
629 QTreeWidgetItem *item = mainWin->getFromHash(this);
630 item->setForeground(0, redBrush);
631 m_notifier->setEnabled(false);
634 mainWin->set_status(_("Director disconnected."));
635 QApplication::restoreOverrideCursor();
643 /* Called by signal when the Director has output for us */
644 void Console::read_dir(int fd)
649 if (g_commDebug) Pmsg0(000, "read_dir\n");
650 while ((stat = 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 );