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 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 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()
101 * Note if we call getDirComm here, we continuously consume
104 if (!findDirComm(conn)) { /* find a free DirComm */
105 return; /* try later */
108 DirComm *dircomm = m_dircommHash.value(conn);
109 if (mainWin->m_checkMessages && dircomm->m_at_main_prompt && hasFocus() && !mainWin->getWaitState()){
110 messagesPending(true);
111 dircomm->write(".messages");
112 displayToPrompt(conn);
113 messagesPending(false);
118 * Connect to Director. This does not connect to the director, dircomm does.
119 * This creates the first and possibly 2nd dircomm instance
121 void Console::connect_dir()
123 DirComm *dircomm = m_dircommHash.value(0);
125 if (!m_console->m_dir) {
126 mainWin->set_status( tr("No Director found."));
130 m_textEdit = textEdit; /* our console screen */
132 if (dircomm->connect_dir()) {
133 if (mainWin->m_connDebug) {
134 Pmsg1(000, "DirComm 0 Seems to have Connected %s\n", m_dir->name());
138 mainWin->set_status(_("Connected"));
140 startTimer(); /* start message timer */
144 * A function created to separate out the population of the lists
145 * from the Console::connect_dir function
147 void Console::populateLists(bool /*forcenew*/)
150 if (!getDirComm(conn)) {
151 if (mainWin->m_connDebug) {
152 Pmsg0(000, "call newDirComm\n");
154 if (!newDirComm(conn)) {
155 Emsg1(M_ABORT, 0, "Failed to connect to %s for populateLists.\n", m_dir->name());
162 void Console::populateLists(int conn)
166 fileset_list.clear();
167 messages_list.clear();
169 storage_list.clear();
172 volstatus_list.clear();
173 mediatype_list.clear();
174 dir_cmd(conn, ".jobs", job_list);
175 dir_cmd(conn, ".clients", client_list);
176 dir_cmd(conn, ".filesets", fileset_list);
177 dir_cmd(conn, ".msgs", messages_list);
178 dir_cmd(conn, ".pools", pool_list);
179 dir_cmd(conn, ".storage", storage_list);
180 dir_cmd(conn, ".types", type_list);
181 dir_cmd(conn, ".levels", level_list);
182 dir_cmd(conn, ".volstatus", volstatus_list);
183 dir_cmd(conn, ".mediatypes", mediatype_list);
184 dir_cmd(conn, ".locations", location_list);
186 if (mainWin->m_connDebug) {
187 QString dbgmsg = QString("jobs=%1 clients=%2 filesets=%3 msgs=%4 pools=%5 storage=%6 types=%7 levels=%8 conn=%9 %10\n")
188 .arg(job_list.count()).arg(client_list.count()).arg(fileset_list.count()).arg(messages_list.count())
189 .arg(pool_list.count()).arg(storage_list.count()).arg(type_list.count()).arg(level_list.count())
190 .arg(conn).arg(m_dir->name());
191 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
196 messages_list.sort();
204 * Overload function for dir_cmd with a QString
207 bool Console::dir_cmd(QString &cmd, QStringList &results)
209 return dir_cmd(cmd.toUtf8().data(), results);
213 * Overload function for dir_cmd, this is if connection is not worried about
215 bool Console::dir_cmd(const char *cmd, QStringList &results)
218 if (getDirComm(conn)) {
219 dir_cmd(conn, cmd, results);
222 Pmsg1(000, "dir_cmd failed to connect to %s\n", m_dir->name());
228 * Send a command to the Director, and return the
229 * results in a QStringList.
231 bool Console::dir_cmd(int conn, const char *cmd, QStringList &results)
233 mainWin->waitEnter();
234 DirComm *dircomm = m_dircommHash.value(conn);
237 if (mainWin->m_connDebug) {
238 QString dbgmsg = QString("dir_cmd conn %1 %2 %3\n").arg(conn).arg(m_dir->name()).arg(cmd);
239 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
243 while ((stat = dircomm->read()) > 0 && dircomm->is_in_command()) {
244 if (mainWin->m_displayAll) display_text(dircomm->msg());
245 strip_trailing_junk(dircomm->msg());
246 results << dircomm->msg();
248 if (stat > 0 && mainWin->m_displayAll) display_text(dircomm->msg());
250 discardToPrompt(conn);
252 return true; /* ***FIXME*** return any command error */
256 * OverLoads for sql_cmd
258 bool Console::sql_cmd(int &conn, QString &query, QStringList &results)
260 return sql_cmd(conn, query.toUtf8().data(), results, false);
263 bool Console::sql_cmd(QString &query, QStringList &results)
266 if (!getDirComm(conn)) {
269 return sql_cmd(conn, query.toUtf8().data(), results, true);
272 bool Console::sql_cmd(const char *query, QStringList &results)
275 if (!getDirComm(conn)) {
278 return sql_cmd(conn, query, results, true);
282 * Send an sql query to the Director, and return the
283 * results in a QStringList.
285 bool Console::sql_cmd(int &conn, const char *query, QStringList &results, bool donotify)
287 DirComm *dircomm = m_dircommHash.value(conn);
289 POOL_MEM cmd(PM_MESSAGE);
291 if (!is_connectedGui()) {
295 if (mainWin->m_connDebug)
296 Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
298 dircomm->notify(false);
299 mainWin->waitEnter();
301 pm_strcpy(cmd, ".sql query=\"");
302 pm_strcat(cmd, query);
303 pm_strcat(cmd, "\"");
304 dircomm->write(cmd.c_str());
305 while ((stat = dircomm->read()) > 0) {
307 if (mainWin->m_displayAll) {
308 display_text(dircomm->msg());
311 strip_trailing_junk(dircomm->msg());
312 bool doappend = true;
314 QString dum = dircomm->msg();
315 if ((dum.left(6) == "*None*")) doappend = false;
318 results << dircomm->msg();
322 dircomm->notify(true);
323 discardToPrompt(conn);
325 return true; /* ***FIXME*** return any command error */
330 * Sending a command to the Director
332 int Console::write_dir(const char *msg)
335 if (getDirComm(conn)) {
336 write_dir(conn, msg);
341 int Console::write_dir(const char *msg, bool dowait)
344 if (getDirComm(conn)) {
345 write_dir(conn, msg, dowait);
350 void Console::write_dir(int conn, const char *msg)
352 write_dir(conn, msg, true);
356 * Send a command to the Director
358 void Console::write_dir(int conn, const char *msg, bool dowait)
360 DirComm *dircomm = m_dircommHash.value(conn);
362 if (dircomm->m_sock) {
363 mainWin->set_status(_("Processing command ..."));
365 mainWin->waitEnter();
370 mainWin->set_status( tr(" Director not connected. Click on connect button."));
371 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
372 QBrush redBrush(Qt::red);
373 QTreeWidgetItem *item = mainWin->getFromHash(this);
374 item->setForeground(0, redBrush);
375 dircomm->m_at_prompt = false;
376 dircomm->m_at_main_prompt = false;
381 * get_job_defaults overload
383 bool Console::get_job_defaults(struct job_defaults &job_defs)
386 return get_job_defaults(conn, job_defs, true);
389 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs)
391 return get_job_defaults(conn, job_defs, false);
395 * Send a job name to the director, and read all the resulting
398 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs, bool donotify)
406 beginNewCommand(conn);
407 DirComm *dircomm = m_dircommHash.value(conn);
408 bool prevWaitState = mainWin->getWaitState();
410 mainWin->waitEnter();
411 if (mainWin->m_connDebug)
412 Pmsg2(000, "job_defaults conn %i %s\n", conn, m_dir->name());
413 scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
414 dircomm->write(scmd);
415 while ((stat = dircomm->read()) > 0) {
416 if (mainWin->m_displayAll) display_text(dircomm->msg());
417 def = strchr(dircomm->msg(), '=');
421 /* Pointer to default value */
423 strip_trailing_junk(def);
425 if (strcmp(dircomm->msg(), "job") == 0) {
426 if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
431 if (strcmp(dircomm->msg(), "pool") == 0) {
432 job_defs.pool_name = def;
435 if (strcmp(dircomm->msg(), "messages") == 0) {
436 job_defs.messages_name = def;
439 if (strcmp(dircomm->msg(), "client") == 0) {
440 job_defs.client_name = def;
443 if (strcmp(dircomm->msg(), "storage") == 0) {
444 job_defs.store_name = def;
447 if (strcmp(dircomm->msg(), "where") == 0) {
448 job_defs.where = def;
451 if (strcmp(dircomm->msg(), "level") == 0) {
452 job_defs.level = def;
455 if (strcmp(dircomm->msg(), "type") == 0) {
459 if (strcmp(dircomm->msg(), "fileset") == 0) {
460 job_defs.fileset_name = def;
463 if (strcmp(dircomm->msg(), "catalog") == 0) {
464 job_defs.catalog_name = def;
467 if (strcmp(dircomm->msg(), "enabled") == 0) {
468 job_defs.enabled = *def == '1' ? true : false;
489 * Save user settings associated with this console
491 void Console::writeSettings()
493 QFont font = get_font();
495 QSettings settings(m_dir->name(), "bat");
496 settings.beginGroup("Console");
497 settings.setValue("consoleFont", font.family());
498 settings.setValue("consolePointSize", font.pointSize());
499 settings.setValue("consoleFixedPitch", font.fixedPitch());
504 * Read and restore user settings associated with this console
506 void Console::readSettings()
508 QFont font = get_font();
510 QSettings settings(m_dir->name(), "bat");
511 settings.beginGroup("Console");
512 font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
513 font.setPointSize(settings.value("consolePointSize", 10).toInt());
514 font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
516 m_textEdit->setFont(font);
520 * Set the console textEdit font
522 void Console::set_font()
525 QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
527 m_textEdit->setFont(font);
532 * Get the console text edit font
534 const QFont Console::get_font()
536 return m_textEdit->font();
540 * Slot for responding to status dir button on button bar
542 void Console::status_dir()
544 QString cmd("status dir");
549 * Slot for responding to messages button on button bar
550 * Here we want to bring the console to the front so use pages' consoleCommand
552 void Console::messages()
554 QString cmd(".messages");
556 messagesPending(false);
560 * Put text into the console window
562 void Console::display_textf(const char *fmt, ...)
567 va_start(arg_ptr, fmt);
568 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
573 void Console::display_text(const QString buf)
575 m_cursor->insertText(buf);
580 void Console::display_text(const char *buf)
582 m_cursor->insertText(buf);
586 void Console::display_html(const QString buf)
588 m_cursor->insertHtml(buf);
592 /* Position cursor to end of screen */
593 void Console::update_cursor()
595 m_textEdit->moveCursor(QTextCursor::End);
596 m_textEdit->ensureCursorVisible();
599 void Console::beginNewCommand(int conn)
601 DirComm *dircomm = m_dircommHash.value(conn);
603 for (int i=0; i < 3; i++) {
605 while (dircomm->read() > 0) {
606 Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
607 if (mainWin->m_displayAll) display_text(dircomm->msg());
609 if (dircomm->m_at_main_prompt) {
616 void Console::displayToPrompt(int conn)
618 DirComm *dircomm = m_dircommHash.value(conn);
622 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
623 while (!dircomm->m_at_prompt) {
624 if ((stat=dircomm->read()) > 0) {
625 buf += dircomm->msg();
626 if (buf.size() >= 8196 || m_messages_pending) {
629 messagesPending(false);
634 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
637 void Console::discardToPrompt(int conn)
639 DirComm *dircomm = m_dircommHash.value(conn);
642 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
643 if (mainWin->m_displayAll) {
644 displayToPrompt(conn);
646 while (!dircomm->m_at_prompt) {
647 stat = dircomm->read();
653 if (mainWin->m_commDebug) {
654 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
658 QString Console::returnFromPrompt(int conn)
660 DirComm *dircomm = m_dircommHash.value(conn);
665 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
666 while (!dircomm->m_at_prompt) {
667 if ((stat=dircomm->read()) > 0) {
668 text += dircomm->msg();
671 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
676 * When the notifier is enabled, read_dir() will automatically be
677 * called by the Qt event loop when ever there is any output
678 * from the Director, and read_dir() will then display it on
681 * When we are in a bat dialog, we want to control *all* output
682 * from the Director, so we set notify to off.
683 * m_console->notifiy(false);
686 /* dual purpose function to turn notify off and return a connection */
687 int Console::notifyOff()
690 if (getDirComm(conn)) {
696 /* knowing a connection, turn notify off or on */
697 bool Console::notify(int conn, bool enable)
699 DirComm *dircomm = m_dircommHash.value(conn);
700 return dircomm->notify(enable);
703 /* knowing a connection, return notify state */
704 bool Console::is_notify_enabled(int conn) const
706 DirComm *dircomm = m_dircommHash.value(conn);
707 return dircomm->is_notify_enabled();
710 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
712 m_directorTreeItem = item;
715 void Console::setDirRes(DIRRES *dir)
721 * To have the ability to get the name of the director resource.
723 void Console::getDirResName(QString &name_returned)
725 name_returned = m_dir->name();
728 /* Slot for responding to page selectors status help command */
729 void Console::consoleHelp()
735 /* Slot for responding to page selectors reload bacula-dir.conf */
736 void Console::consoleReload()
738 QString cmd("reload");
742 /* For suppressing .messages
743 * This may be rendered not needed if the multiple connections feature gets working */
744 bool Console::hasFocus()
746 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
752 /* For adding feature to have the gui's messages button change when
753 * messages are pending */
754 bool Console::messagesPending(bool pend)
756 bool prev = m_messages_pending;
757 m_messages_pending = pend;
758 mainWin->setMessageIcon();
762 /* terminate all existing connections */
763 void Console::terminate()
765 foreach(DirComm* dircomm, m_dircommHash) {
766 dircomm->terminate();
768 m_console->stopTimer();
771 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
772 bool Console::is_connectedGui()
774 if (is_connected(0)) {
777 QString message = tr("Director is currently disconnected\nPlease reconnect!");
778 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
783 int Console::read(int conn)
785 DirComm *dircomm = m_dircommHash.value(conn);
786 return dircomm->read();
789 char *Console::msg(int conn)
791 DirComm *dircomm = m_dircommHash.value(conn);
792 return dircomm->msg();
795 int Console::write(int conn, const QString msg)
797 DirComm *dircomm = m_dircommHash.value(conn);
798 mainWin->waitEnter();
799 int ret = dircomm->write(msg);
804 int Console::write(int conn, const char *msg)
806 DirComm *dircomm = m_dircommHash.value(conn);
807 mainWin->waitEnter();
808 int ret = dircomm->write(msg);
813 /* This checks to see if any is connected */
814 bool Console::is_connected()
816 bool connected = false;
817 foreach(DirComm* dircomm, m_dircommHash) {
818 if (dircomm->is_connected())
824 /* knowing the connection id, is it connected */
825 bool Console::is_connected(int conn)
827 DirComm *dircomm = m_dircommHash.value(conn);
828 return dircomm->is_connected();
832 * Need a connection. Check existing connections or create one
834 bool Console::getDirComm(int &conn)
836 if (findDirComm(conn)) {
839 if (mainWin->m_connDebug) {
840 Pmsg0(000, "call newDirComm\n");
842 return newDirComm(conn);
847 * Try to find a free (unused but established) connection
848 * KES: Note, I think there is a problem here because for
849 * some reason, the notifier is often turned off on file
850 * descriptors that seem to me to be available. That means
851 * that we do not use a free descriptor and thus we will create
852 * a new connection that is maybe not necessary. Someone needs
853 * to look into whether or not notify() is correctly turned on
854 * when we are back at the command prompt and idle.
857 bool Console::findDirComm(int &conn)
860 QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
861 while (iter != m_dircommHash.constEnd()) {
862 DirComm *dircomm = iter.value();
863 if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
864 conn = dircomm->m_conn;
867 if (mainWin->m_connDebug) {
868 Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
869 i, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
878 * Create a new connection
880 bool Console::newDirComm(int &conn)
883 if (mainWin->m_connDebug) {
884 Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
886 DirComm *dircomm = new DirComm(this, m_dircommCounter);
887 m_dircommHash.insert(m_dircommCounter, dircomm);
888 bool success = dircomm->connect_dir();
889 if (mainWin->m_connDebug) {
891 Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
893 Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
894 m_dircommCounter, m_dir->name());
898 m_dircommHash.remove(m_dircommCounter);
902 conn = m_dircommCounter;