2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2010 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 three of the GNU Affero 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 Affero 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 Kern Sibbald.
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 * Kern Sibbald, January MMVII
41 Console::Console(QTabWidget *parent)
44 m_name = tr("Console");
45 m_messages_pending = false;
49 m_warningPrevent = false;
53 * Create a connection to the Director and put it in a hash table
55 m_dircommHash.insert(m_dircommCounter, new DirComm(this, m_dircommCounter));
58 m_textEdit = textEdit; /* our console screen */
59 m_cursor = new QTextCursor(m_textEdit->document());
60 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
63 m_contextActions.append(actionStatusDir);
64 m_contextActions.append(actionConsoleHelp);
65 m_contextActions.append(actionRequestMessages);
66 m_contextActions.append(actionConsoleReload);
67 connect(actionStatusDir, SIGNAL(triggered()), this, SLOT(status_dir()));
68 connect(actionConsoleHelp, SIGNAL(triggered()), this, SLOT(consoleHelp()));
69 connect(actionConsoleReload, SIGNAL(triggered()), this, SLOT(consoleReload()));
70 connect(actionRequestMessages, SIGNAL(triggered()), this, SLOT(messages()));
77 void Console::startTimer()
79 m_timer = new QTimer(this);
80 QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
81 m_timer->start(mainWin->m_checkMessagesInterval*1000);
84 void Console::stopTimer()
87 QWidget::disconnect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
94 /* slot connected to the timer
95 * requires preferences of check messages and operates at interval */
96 void Console::poll_messages()
100 /* Do not poll if notifier off */
101 if (!mainWin->m_notify) {
106 * Note if we call getDirComm here, we continuously consume
109 if (!findDirComm(conn)) { /* find a free DirComm */
110 return; /* try later */
113 DirComm *dircomm = m_dircommHash.value(conn);
114 if (mainWin->m_checkMessages && dircomm->m_at_main_prompt && hasFocus() && !mainWin->getWaitState()){
115 messagesPending(true);
116 dircomm->write(".messages");
117 displayToPrompt(conn);
118 messagesPending(false);
123 * Connect to Director. This does not connect to the director, dircomm does.
124 * This creates the first and possibly 2nd dircomm instance
126 void Console::connect_dir()
128 DirComm *dircomm = m_dircommHash.value(0);
130 if (!m_console->m_dir) {
131 mainWin->set_status( tr("No Director found."));
135 m_textEdit = textEdit; /* our console screen */
137 if (dircomm->connect_dir()) {
138 if (mainWin->m_connDebug) {
139 Pmsg1(000, "DirComm 0 Seems to have Connected %s\n", m_dir->name());
143 mainWin->set_status(_("Connected"));
145 startTimer(); /* start message timer */
149 * A function created to separate out the population of the lists
150 * from the Console::connect_dir function
152 void Console::populateLists(bool /*forcenew*/)
155 if (!getDirComm(conn)) {
156 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
157 if (!newDirComm(conn)) {
158 Emsg1(M_ABORT, 0, "Failed to connect to %s for populateLists.\n", m_dir->name());
165 void Console::populateLists(int conn)
169 fileset_list.clear();
170 messages_list.clear();
172 storage_list.clear();
175 volstatus_list.clear();
176 mediatype_list.clear();
177 dir_cmd(conn, ".jobs", job_list);
178 dir_cmd(conn, ".clients", client_list);
179 dir_cmd(conn, ".filesets", fileset_list);
180 dir_cmd(conn, ".msgs", messages_list);
181 dir_cmd(conn, ".pools", pool_list);
182 dir_cmd(conn, ".storage", storage_list);
183 dir_cmd(conn, ".types", type_list);
184 dir_cmd(conn, ".levels", level_list);
185 dir_cmd(conn, ".volstatus", volstatus_list);
186 dir_cmd(conn, ".mediatypes", mediatype_list);
187 dir_cmd(conn, ".locations", location_list);
189 if (mainWin->m_connDebug) {
190 QString dbgmsg = QString("jobs=%1 clients=%2 filesets=%3 msgs=%4 pools=%5 storage=%6 types=%7 levels=%8 conn=%9 %10\n")
191 .arg(job_list.count()).arg(client_list.count()).arg(fileset_list.count()).arg(messages_list.count())
192 .arg(pool_list.count()).arg(storage_list.count()).arg(type_list.count()).arg(level_list.count())
193 .arg(conn).arg(m_dir->name());
194 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
199 messages_list.sort();
207 * Overload function for dir_cmd with a QString
210 bool Console::dir_cmd(QString &cmd, QStringList &results)
212 return dir_cmd(cmd.toUtf8().data(), results);
216 * Overload function for dir_cmd, this is if connection is not worried about
218 bool Console::dir_cmd(const char *cmd, QStringList &results)
221 if (getDirComm(conn)) {
222 dir_cmd(conn, cmd, results);
225 Pmsg1(000, "dir_cmd failed to connect to %s\n", m_dir->name());
231 * Send a command to the Director, and return the
232 * results in a QStringList.
234 bool Console::dir_cmd(int conn, const char *cmd, QStringList &results)
236 mainWin->waitEnter();
237 DirComm *dircomm = m_dircommHash.value(conn);
239 bool prev_notify = mainWin->m_notify;
241 if (mainWin->m_connDebug) {
242 QString dbgmsg = QString("dir_cmd conn %1 %2 %3\n").arg(conn).arg(m_dir->name()).arg(cmd);
243 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
247 while ((stat = dircomm->read()) > 0 && dircomm->is_in_command()) {
248 if (mainWin->m_displayAll) display_text(dircomm->msg());
249 strip_trailing_junk(dircomm->msg());
250 results << dircomm->msg();
252 if (stat > 0 && mainWin->m_displayAll) display_text(dircomm->msg());
254 notify(conn, true); /* turn it back on */
256 discardToPrompt(conn);
258 return true; /* ***FIXME*** return any command error */
262 * OverLoads for sql_cmd
264 bool Console::sql_cmd(int &conn, QString &query, QStringList &results)
266 return sql_cmd(conn, query.toUtf8().data(), results, false);
269 bool Console::sql_cmd(QString &query, QStringList &results)
272 if (!getDirComm(conn)) {
275 return sql_cmd(conn, query.toUtf8().data(), results, true);
278 bool Console::sql_cmd(const char *query, QStringList &results)
281 if (!getDirComm(conn)) {
284 return sql_cmd(conn, query, results, true);
288 * Send an sql query to the Director, and return the
289 * results in a QStringList.
291 bool Console::sql_cmd(int &conn, const char *query, QStringList &results, bool donotify)
293 DirComm *dircomm = m_dircommHash.value(conn);
295 POOL_MEM cmd(PM_MESSAGE);
296 bool prev_notify = mainWin->m_notify;
298 if (!is_connectedGui()) {
302 if (mainWin->m_connDebug) Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
304 dircomm->notify(false);
306 mainWin->waitEnter();
308 pm_strcpy(cmd, ".sql query=\"");
309 pm_strcat(cmd, query);
310 pm_strcat(cmd, "\"");
311 dircomm->write(cmd.c_str());
312 while ((stat = dircomm->read()) > 0) {
314 if (mainWin->m_displayAll) {
315 display_text(dircomm->msg());
318 strip_trailing_junk(dircomm->msg());
319 bool doappend = true;
321 QString dum = dircomm->msg();
322 if ((dum.left(6) == "*None*")) doappend = false;
325 results << dircomm->msg();
329 if (donotify && prev_notify) {
330 dircomm->notify(true);
332 discardToPrompt(conn);
334 return !mainWin->isClosing(); /* return false if closing */
339 * Sending a command to the Director
341 int Console::write_dir(const char *msg)
344 if (getDirComm(conn)) {
345 write_dir(conn, msg);
350 int Console::write_dir(const char *msg, bool dowait)
353 if (getDirComm(conn)) {
354 write_dir(conn, msg, dowait);
359 void Console::write_dir(int conn, const char *msg)
361 write_dir(conn, msg, true);
365 * Send a command to the Director
367 void Console::write_dir(int conn, const char *msg, bool dowait)
369 DirComm *dircomm = m_dircommHash.value(conn);
371 if (dircomm->m_sock) {
372 mainWin->set_status(_("Processing command ..."));
374 mainWin->waitEnter();
379 mainWin->set_status( tr(" Director not connected. Click on connect button."));
380 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
381 QBrush redBrush(Qt::red);
382 QTreeWidgetItem *item = mainWin->getFromHash(this);
383 item->setForeground(0, redBrush);
384 dircomm->m_at_prompt = false;
385 dircomm->m_at_main_prompt = false;
390 * get_job_defaults overload
392 bool Console::get_job_defaults(struct job_defaults &job_defs)
395 return get_job_defaults(conn, job_defs, true);
398 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs)
400 return get_job_defaults(conn, job_defs, false);
404 * Send a job name to the director, and read all the resulting
407 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs, bool donotify)
412 bool prev_notify = mainWin->m_notify;
418 beginNewCommand(conn);
419 DirComm *dircomm = m_dircommHash.value(conn);
420 bool prevWaitState = mainWin->getWaitState();
422 mainWin->waitEnter();
423 if (mainWin->m_connDebug)
424 Pmsg2(000, "job_defaults conn %i %s\n", conn, m_dir->name());
425 scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
426 dircomm->write(scmd);
427 while ((stat = dircomm->read()) > 0) {
428 if (mainWin->m_displayAll) display_text(dircomm->msg());
429 def = strchr(dircomm->msg(), '=');
433 /* Pointer to default value */
435 strip_trailing_junk(def);
437 if (strcmp(dircomm->msg(), "job") == 0) {
438 if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
443 if (strcmp(dircomm->msg(), "pool") == 0) {
444 job_defs.pool_name = def;
447 if (strcmp(dircomm->msg(), "messages") == 0) {
448 job_defs.messages_name = def;
451 if (strcmp(dircomm->msg(), "client") == 0) {
452 job_defs.client_name = def;
455 if (strcmp(dircomm->msg(), "storage") == 0) {
456 job_defs.store_name = def;
459 if (strcmp(dircomm->msg(), "where") == 0) {
460 job_defs.where = def;
463 if (strcmp(dircomm->msg(), "level") == 0) {
464 job_defs.level = def;
467 if (strcmp(dircomm->msg(), "type") == 0) {
471 if (strcmp(dircomm->msg(), "fileset") == 0) {
472 job_defs.fileset_name = def;
475 if (strcmp(dircomm->msg(), "catalog") == 0) {
476 job_defs.catalog_name = def;
479 if (strcmp(dircomm->msg(), "enabled") == 0) {
480 job_defs.enabled = *def == '1' ? true : false;
485 /* Fall through wanted */
487 if (donotify && prev_notify) {
490 if (!prevWaitState) {
498 * Save user settings associated with this console
500 void Console::writeSettings()
502 QFont font = get_font();
504 QSettings settings(m_dir->name(), "bat");
505 settings.beginGroup("Console");
506 settings.setValue("consoleFont", font.family());
507 settings.setValue("consolePointSize", font.pointSize());
508 settings.setValue("consoleFixedPitch", font.fixedPitch());
513 * Read and restore user settings associated with this console
515 void Console::readSettings()
517 QFont font = get_font();
519 QSettings settings(m_dir->name(), "bat");
520 settings.beginGroup("Console");
521 font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
522 font.setPointSize(settings.value("consolePointSize", 10).toInt());
523 font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
525 m_textEdit->setFont(font);
529 * Set the console textEdit font
531 void Console::set_font()
534 QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
536 m_textEdit->setFont(font);
541 * Get the console text edit font
543 const QFont Console::get_font()
545 return m_textEdit->font();
549 * Slot for responding to status dir button on button bar
551 void Console::status_dir()
553 QString cmd("status dir");
558 * Slot for responding to messages button on button bar
559 * Here we want to bring the console to the front so use pages' consoleCommand
561 void Console::messages()
563 QString cmd(".messages");
565 messagesPending(false);
569 * Put text into the console window
571 void Console::display_textf(const char *fmt, ...)
576 va_start(arg_ptr, fmt);
577 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
582 void Console::display_text(const QString buf)
584 if (buf.size() != 0) {
585 m_cursor->insertText(buf);
591 void Console::display_text(const char *buf)
594 m_cursor->insertText(buf);
599 void Console::display_html(const QString buf)
601 if (buf.size() != 0) {
602 m_cursor->insertHtml(buf);
607 /* Position cursor to end of screen */
608 void Console::update_cursor()
610 m_textEdit->moveCursor(QTextCursor::End);
611 m_textEdit->ensureCursorVisible();
614 void Console::beginNewCommand(int conn)
616 DirComm *dircomm = m_dircommHash.value(conn);
618 for (int i=0; i < 3; i++) {
620 while (dircomm->read() > 0) {
621 if (mainWin->m_commDebug) Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
622 if (mainWin->m_displayAll) display_text(dircomm->msg());
624 if (dircomm->m_at_main_prompt) {
631 void Console::displayToPrompt(int conn)
633 DirComm *dircomm = m_dircommHash.value(conn);
637 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
638 while (!dircomm->m_at_prompt) {
639 if ((stat=dircomm->read()) > 0) {
640 buf += dircomm->msg();
641 if (buf.size() >= 8196 || m_messages_pending) {
644 messagesPending(false);
649 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
652 void Console::discardToPrompt(int conn)
654 DirComm *dircomm = m_dircommHash.value(conn);
657 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
658 if (mainWin->m_displayAll) {
659 displayToPrompt(conn);
661 while (!dircomm->m_at_prompt) {
662 stat = dircomm->read();
668 if (mainWin->m_commDebug) {
669 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
673 QString Console::returnFromPrompt(int conn)
675 DirComm *dircomm = m_dircommHash.value(conn);
681 text += dircomm->msg();
682 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
683 while (!dircomm->m_at_prompt) {
684 if ((stat=dircomm->read()) > 0) {
685 text += dircomm->msg();
688 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
693 * When the notifier is enabled, read_dir() will automatically be
694 * called by the Qt event loop when ever there is any output
695 * from the Director, and read_dir() will then display it on
698 * When we are in a bat dialog, we want to control *all* output
699 * from the Director, so we set notify to off.
700 * m_console->notifiy(false);
703 /* dual purpose function to turn notify off and return a connection */
704 int Console::notifyOff()
707 if (getDirComm(conn)) {
713 /* knowing a connection, turn notify off or on */
714 bool Console::notify(int conn, bool enable)
716 DirComm *dircomm = m_dircommHash.value(conn);
717 return dircomm->notify(enable);
720 /* knowing a connection, return notify state */
721 bool Console::is_notify_enabled(int conn) const
723 DirComm *dircomm = m_dircommHash.value(conn);
724 return dircomm->is_notify_enabled();
727 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
729 m_directorTreeItem = item;
732 void Console::setDirRes(DIRRES *dir)
738 * To have the ability to get the name of the director resource.
740 void Console::getDirResName(QString &name_returned)
742 name_returned = m_dir->name();
745 /* Slot for responding to page selectors status help command */
746 void Console::consoleHelp()
752 /* Slot for responding to page selectors reload bacula-dir.conf */
753 void Console::consoleReload()
755 QString cmd("reload");
759 /* For suppressing .messages
760 * This may be rendered not needed if the multiple connections feature gets working */
761 bool Console::hasFocus()
763 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
769 /* For adding feature to have the gui's messages button change when
770 * messages are pending */
771 bool Console::messagesPending(bool pend)
773 bool prev = m_messages_pending;
774 m_messages_pending = pend;
775 mainWin->setMessageIcon();
779 /* terminate all existing connections */
780 void Console::terminate()
782 foreach(DirComm* dircomm, m_dircommHash) {
783 dircomm->terminate();
785 m_console->stopTimer();
788 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
789 bool Console::is_connectedGui()
791 if (is_connected(0)) {
794 QString message = tr("Director is currently disconnected\nPlease reconnect!");
795 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
800 int Console::read(int conn)
802 DirComm *dircomm = m_dircommHash.value(conn);
803 return dircomm->read();
806 char *Console::msg(int conn)
808 DirComm *dircomm = m_dircommHash.value(conn);
809 return dircomm->msg();
812 int Console::write(int conn, const QString msg)
814 DirComm *dircomm = m_dircommHash.value(conn);
815 mainWin->waitEnter();
816 int ret = dircomm->write(msg);
821 int Console::write(int conn, const char *msg)
823 DirComm *dircomm = m_dircommHash.value(conn);
824 mainWin->waitEnter();
825 int ret = dircomm->write(msg);
830 /* This checks to see if any is connected */
831 bool Console::is_connected()
833 bool connected = false;
834 foreach(DirComm* dircomm, m_dircommHash) {
835 if (dircomm->is_connected())
841 /* knowing the connection id, is it connected */
842 bool Console::is_connected(int conn)
844 DirComm *dircomm = m_dircommHash.value(conn);
845 return dircomm->is_connected();
849 * Need a connection. Check existing connections or create one
851 bool Console::getDirComm(int &conn)
853 if (findDirComm(conn)) {
856 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
857 return newDirComm(conn);
862 * Try to find a free (unused but established) connection
863 * KES: Note, I think there is a problem here because for
864 * some reason, the notifier is often turned off on file
865 * descriptors that seem to me to be available. That means
866 * that we do not use a free descriptor and thus we will create
867 * a new connection that is maybe not necessary. Someone needs
868 * to look into whether or not notify() is correctly turned on
869 * when we are back at the command prompt and idle.
872 bool Console::findDirComm(int &conn)
875 QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
876 while (iter != m_dircommHash.constEnd()) {
877 DirComm *dircomm = iter.value();
878 if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
879 conn = dircomm->m_conn;
882 if (mainWin->m_connDebug) {
883 Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
884 i, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
893 * Create a new connection
895 bool Console::newDirComm(int &conn)
898 if (mainWin->m_connDebug) {
899 Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
901 DirComm *dircomm = new DirComm(this, m_dircommCounter);
902 m_dircommHash.insert(m_dircommCounter, dircomm);
903 bool success = dircomm->connect_dir();
904 if (mainWin->m_connDebug) {
906 Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
908 Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
909 m_dircommCounter, m_dir->name());
913 m_dircommHash.remove(m_dircommCounter);
917 conn = m_dircommCounter;