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 m_cursor->insertText(buf);
589 void Console::display_text(const char *buf)
591 m_cursor->insertText(buf);
595 void Console::display_html(const QString buf)
597 m_cursor->insertHtml(buf);
601 /* Position cursor to end of screen */
602 void Console::update_cursor()
604 m_textEdit->moveCursor(QTextCursor::End);
605 m_textEdit->ensureCursorVisible();
608 void Console::beginNewCommand(int conn)
610 DirComm *dircomm = m_dircommHash.value(conn);
612 for (int i=0; i < 3; i++) {
614 while (dircomm->read() > 0) {
615 Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
616 if (mainWin->m_displayAll) display_text(dircomm->msg());
618 if (dircomm->m_at_main_prompt) {
625 void Console::displayToPrompt(int conn)
627 DirComm *dircomm = m_dircommHash.value(conn);
631 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
632 while (!dircomm->m_at_prompt) {
633 if ((stat=dircomm->read()) > 0) {
634 buf += dircomm->msg();
635 if (buf.size() >= 8196 || m_messages_pending) {
638 messagesPending(false);
643 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
646 void Console::discardToPrompt(int conn)
648 DirComm *dircomm = m_dircommHash.value(conn);
651 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
652 if (mainWin->m_displayAll) {
653 displayToPrompt(conn);
655 while (!dircomm->m_at_prompt) {
656 stat = dircomm->read();
662 if (mainWin->m_commDebug) {
663 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
667 QString Console::returnFromPrompt(int conn)
669 DirComm *dircomm = m_dircommHash.value(conn);
675 text += dircomm->msg();
676 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
677 while (!dircomm->m_at_prompt) {
678 if ((stat=dircomm->read()) > 0) {
679 text += dircomm->msg();
682 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
687 * When the notifier is enabled, read_dir() will automatically be
688 * called by the Qt event loop when ever there is any output
689 * from the Director, and read_dir() will then display it on
692 * When we are in a bat dialog, we want to control *all* output
693 * from the Director, so we set notify to off.
694 * m_console->notifiy(false);
697 /* dual purpose function to turn notify off and return a connection */
698 int Console::notifyOff()
701 if (getDirComm(conn)) {
707 /* knowing a connection, turn notify off or on */
708 bool Console::notify(int conn, bool enable)
710 DirComm *dircomm = m_dircommHash.value(conn);
711 return dircomm->notify(enable);
714 /* knowing a connection, return notify state */
715 bool Console::is_notify_enabled(int conn) const
717 DirComm *dircomm = m_dircommHash.value(conn);
718 return dircomm->is_notify_enabled();
721 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
723 m_directorTreeItem = item;
726 void Console::setDirRes(DIRRES *dir)
732 * To have the ability to get the name of the director resource.
734 void Console::getDirResName(QString &name_returned)
736 name_returned = m_dir->name();
739 /* Slot for responding to page selectors status help command */
740 void Console::consoleHelp()
746 /* Slot for responding to page selectors reload bacula-dir.conf */
747 void Console::consoleReload()
749 QString cmd("reload");
753 /* For suppressing .messages
754 * This may be rendered not needed if the multiple connections feature gets working */
755 bool Console::hasFocus()
757 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
763 /* For adding feature to have the gui's messages button change when
764 * messages are pending */
765 bool Console::messagesPending(bool pend)
767 bool prev = m_messages_pending;
768 m_messages_pending = pend;
769 mainWin->setMessageIcon();
773 /* terminate all existing connections */
774 void Console::terminate()
776 foreach(DirComm* dircomm, m_dircommHash) {
777 dircomm->terminate();
779 m_console->stopTimer();
782 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
783 bool Console::is_connectedGui()
785 if (is_connected(0)) {
788 QString message = tr("Director is currently disconnected\nPlease reconnect!");
789 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
794 int Console::read(int conn)
796 DirComm *dircomm = m_dircommHash.value(conn);
797 return dircomm->read();
800 char *Console::msg(int conn)
802 DirComm *dircomm = m_dircommHash.value(conn);
803 return dircomm->msg();
806 int Console::write(int conn, const QString msg)
808 DirComm *dircomm = m_dircommHash.value(conn);
809 mainWin->waitEnter();
810 int ret = dircomm->write(msg);
815 int Console::write(int conn, const char *msg)
817 DirComm *dircomm = m_dircommHash.value(conn);
818 mainWin->waitEnter();
819 int ret = dircomm->write(msg);
824 /* This checks to see if any is connected */
825 bool Console::is_connected()
827 bool connected = false;
828 foreach(DirComm* dircomm, m_dircommHash) {
829 if (dircomm->is_connected())
835 /* knowing the connection id, is it connected */
836 bool Console::is_connected(int conn)
838 DirComm *dircomm = m_dircommHash.value(conn);
839 return dircomm->is_connected();
843 * Need a connection. Check existing connections or create one
845 bool Console::getDirComm(int &conn)
847 if (findDirComm(conn)) {
850 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
851 return newDirComm(conn);
856 * Try to find a free (unused but established) connection
857 * KES: Note, I think there is a problem here because for
858 * some reason, the notifier is often turned off on file
859 * descriptors that seem to me to be available. That means
860 * that we do not use a free descriptor and thus we will create
861 * a new connection that is maybe not necessary. Someone needs
862 * to look into whether or not notify() is correctly turned on
863 * when we are back at the command prompt and idle.
866 bool Console::findDirComm(int &conn)
869 QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
870 while (iter != m_dircommHash.constEnd()) {
871 DirComm *dircomm = iter.value();
872 if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
873 conn = dircomm->m_conn;
876 if (mainWin->m_connDebug) {
877 Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
878 i, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
887 * Create a new connection
889 bool Console::newDirComm(int &conn)
892 if (mainWin->m_connDebug) {
893 Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
895 DirComm *dircomm = new DirComm(this, m_dircommCounter);
896 m_dircommHash.insert(m_dircommCounter, dircomm);
897 bool success = dircomm->connect_dir();
898 if (mainWin->m_connDebug) {
900 Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
902 Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
903 m_dircommCounter, m_dir->name());
907 m_dircommHash.remove(m_dircommCounter);
911 conn = m_dircommCounter;