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) : Pages()
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*30000);
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 dircomm->write(".messages");
116 displayToPrompt(conn);
117 messagesPending(false);
122 * Connect to Director. This does not connect to the director, dircomm does.
123 * This creates the first and possibly 2nd dircomm instance
125 void Console::connect_dir()
127 DirComm *dircomm = m_dircommHash.value(0);
129 if (!m_console->m_dir) {
130 mainWin->set_status( tr("No Director found."));
134 m_textEdit = textEdit; /* our console screen */
136 if (dircomm->connect_dir()) {
137 if (mainWin->m_connDebug) {
138 Pmsg1(000, "DirComm 0 Seems to have Connected %s\n", m_dir->name());
142 mainWin->set_status(_("Connected"));
144 startTimer(); /* start message timer */
148 * A function created to separate out the population of the lists
149 * from the Console::connect_dir function
151 void Console::populateLists(bool /*forcenew*/)
154 if (!getDirComm(conn)) {
155 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
156 if (!newDirComm(conn)) {
157 Emsg1(M_ABORT, 0, "Failed to connect to %s for populateLists.\n", m_dir->name());
164 void Console::populateLists(int conn)
167 restore_list.clear();
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, ".jobs type=R", restore_list);
179 dir_cmd(conn, ".clients", client_list);
180 dir_cmd(conn, ".filesets", fileset_list);
181 dir_cmd(conn, ".msgs", messages_list);
182 dir_cmd(conn, ".pools", pool_list);
183 dir_cmd(conn, ".storage", storage_list);
184 dir_cmd(conn, ".types", type_list);
185 dir_cmd(conn, ".levels", level_list);
186 dir_cmd(conn, ".volstatus", volstatus_list);
187 dir_cmd(conn, ".mediatypes", mediatype_list);
188 dir_cmd(conn, ".locations", location_list);
190 if (mainWin->m_connDebug) {
191 QString dbgmsg = QString("jobs=%1 clients=%2 filesets=%3 msgs=%4 pools=%5 storage=%6 types=%7 levels=%8 conn=%9 %10\n")
192 .arg(job_list.count()).arg(client_list.count()).arg(fileset_list.count()).arg(messages_list.count())
193 .arg(pool_list.count()).arg(storage_list.count()).arg(type_list.count()).arg(level_list.count())
194 .arg(conn).arg(m_dir->name());
195 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
200 messages_list.sort();
208 * Overload function for dir_cmd with a QString
211 bool Console::dir_cmd(QString &cmd, QStringList &results)
213 return dir_cmd(cmd.toUtf8().data(), results);
217 * Overload function for dir_cmd, this is if connection is not worried about
219 bool Console::dir_cmd(const char *cmd, QStringList &results)
222 if (getDirComm(conn)) {
223 dir_cmd(conn, cmd, results);
226 Pmsg1(000, "dir_cmd failed to connect to %s\n", m_dir->name());
232 * Send a command to the Director, and return the
233 * results in a QStringList.
235 bool Console::dir_cmd(int conn, const char *cmd, QStringList &results)
237 mainWin->waitEnter();
238 DirComm *dircomm = m_dircommHash.value(conn);
240 bool prev_notify = mainWin->m_notify;
242 if (mainWin->m_connDebug) {
243 QString dbgmsg = QString("dir_cmd conn %1 %2 %3\n").arg(conn).arg(m_dir->name()).arg(cmd);
244 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
248 while ((stat = dircomm->read()) > 0 && dircomm->is_in_command()) {
249 if (mainWin->m_displayAll) display_text(dircomm->msg());
250 strip_trailing_junk(dircomm->msg());
251 results << dircomm->msg();
253 if (stat > 0 && mainWin->m_displayAll) display_text(dircomm->msg());
255 notify(conn, true); /* turn it back on */
257 discardToPrompt(conn);
259 return true; /* ***FIXME*** return any command error */
263 * OverLoads for sql_cmd
265 bool Console::sql_cmd(int &conn, QString &query, QStringList &results)
267 return sql_cmd(conn, query.toUtf8().data(), results, false);
270 bool Console::sql_cmd(QString &query, QStringList &results)
273 if (!getDirComm(conn)) {
276 return sql_cmd(conn, query.toUtf8().data(), results, true);
279 bool Console::sql_cmd(const char *query, QStringList &results)
282 if (!getDirComm(conn)) {
285 return sql_cmd(conn, query, results, true);
289 * Send an sql query to the Director, and return the
290 * results in a QStringList.
292 bool Console::sql_cmd(int &conn, const char *query, QStringList &results, bool donotify)
294 DirComm *dircomm = m_dircommHash.value(conn);
296 POOL_MEM cmd(PM_MESSAGE);
297 bool prev_notify = mainWin->m_notify;
299 if (!is_connectedGui()) {
303 if (mainWin->m_connDebug) Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
305 dircomm->notify(false);
307 mainWin->waitEnter();
309 pm_strcpy(cmd, ".sql query=\"");
310 pm_strcat(cmd, query);
311 pm_strcat(cmd, "\"");
312 dircomm->write(cmd.c_str());
313 while ((stat = dircomm->read()) > 0) {
315 if (mainWin->m_displayAll) {
316 display_text(dircomm->msg());
319 strip_trailing_junk(dircomm->msg());
320 bool doappend = true;
322 QString dum = dircomm->msg();
323 if ((dum.left(6) == "*None*")) doappend = false;
326 results << dircomm->msg();
330 if (donotify && prev_notify) {
331 dircomm->notify(true);
333 discardToPrompt(conn);
335 return !mainWin->isClosing(); /* return false if closing */
340 * Sending a command to the Director
342 int Console::write_dir(const char *msg)
345 if (getDirComm(conn)) {
346 write_dir(conn, msg);
351 int Console::write_dir(const char *msg, bool dowait)
354 if (getDirComm(conn)) {
355 write_dir(conn, msg, dowait);
360 void Console::write_dir(int conn, const char *msg)
362 write_dir(conn, msg, true);
366 * Send a command to the Director
368 void Console::write_dir(int conn, const char *msg, bool dowait)
370 DirComm *dircomm = m_dircommHash.value(conn);
372 if (dircomm->m_sock) {
373 mainWin->set_status(_("Processing command ..."));
375 mainWin->waitEnter();
380 mainWin->set_status( tr(" Director not connected. Click on connect button."));
381 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
382 QBrush redBrush(Qt::red);
383 QTreeWidgetItem *item = mainWin->getFromHash(this);
384 item->setForeground(0, redBrush);
385 dircomm->m_at_prompt = false;
386 dircomm->m_at_main_prompt = false;
391 * get_job_defaults overload
393 bool Console::get_job_defaults(struct job_defaults &job_defs)
396 return get_job_defaults(conn, job_defs, true);
399 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs)
401 return get_job_defaults(conn, job_defs, false);
405 * Send a job name to the director, and read all the resulting
408 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs, bool donotify)
413 bool prev_notify = mainWin->m_notify;
419 beginNewCommand(conn);
420 DirComm *dircomm = m_dircommHash.value(conn);
421 bool prevWaitState = mainWin->getWaitState();
423 mainWin->waitEnter();
424 if (mainWin->m_connDebug)
425 Pmsg2(000, "job_defaults conn %i %s\n", conn, m_dir->name());
426 scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
427 dircomm->write(scmd);
428 while ((stat = dircomm->read()) > 0) {
429 if (mainWin->m_displayAll) display_text(dircomm->msg());
430 def = strchr(dircomm->msg(), '=');
434 /* Pointer to default value */
436 strip_trailing_junk(def);
438 if (strcmp(dircomm->msg(), "job") == 0) {
439 if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
444 if (strcmp(dircomm->msg(), "pool") == 0) {
445 job_defs.pool_name = def;
448 if (strcmp(dircomm->msg(), "messages") == 0) {
449 job_defs.messages_name = def;
452 if (strcmp(dircomm->msg(), "client") == 0) {
453 job_defs.client_name = def;
456 if (strcmp(dircomm->msg(), "storage") == 0) {
457 job_defs.store_name = def;
460 if (strcmp(dircomm->msg(), "where") == 0) {
461 job_defs.where = def;
464 if (strcmp(dircomm->msg(), "level") == 0) {
465 job_defs.level = def;
468 if (strcmp(dircomm->msg(), "type") == 0) {
472 if (strcmp(dircomm->msg(), "fileset") == 0) {
473 job_defs.fileset_name = def;
476 if (strcmp(dircomm->msg(), "catalog") == 0) {
477 job_defs.catalog_name = def;
480 if (strcmp(dircomm->msg(), "enabled") == 0) {
481 job_defs.enabled = *def == '1' ? true : false;
486 /* Fall through wanted */
488 if (donotify && prev_notify) {
491 if (!prevWaitState) {
499 * Save user settings associated with this console
501 void Console::writeSettings()
503 QFont font = get_font();
505 QSettings settings(m_dir->name(), "bat");
506 settings.beginGroup("Console");
507 settings.setValue("consoleFont", font.family());
508 settings.setValue("consolePointSize", font.pointSize());
509 settings.setValue("consoleFixedPitch", font.fixedPitch());
514 * Read and restore user settings associated with this console
516 void Console::readSettings()
518 QFont font = get_font();
520 QSettings settings(m_dir->name(), "bat");
521 settings.beginGroup("Console");
522 font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
523 font.setPointSize(settings.value("consolePointSize", 10).toInt());
524 font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
526 m_textEdit->setFont(font);
530 * Set the console textEdit font
532 void Console::set_font()
535 QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
537 m_textEdit->setFont(font);
542 * Get the console text edit font
544 const QFont Console::get_font()
546 return m_textEdit->font();
550 * Slot for responding to status dir button on button bar
552 void Console::status_dir()
554 QString cmd("status dir");
559 * Slot for responding to messages button on button bar
560 * Here we want to bring the console to the front so use pages' consoleCommand
562 void Console::messages()
564 QString cmd(".messages");
566 messagesPending(false);
570 * Put text into the console window
572 void Console::display_textf(const char *fmt, ...)
577 va_start(arg_ptr, fmt);
578 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
583 void Console::display_text(const QString buf)
585 if (buf.size() != 0) {
586 m_cursor->insertText(buf);
592 void Console::display_text(const char *buf)
595 m_cursor->insertText(buf);
600 void Console::display_html(const QString buf)
602 if (buf.size() != 0) {
603 m_cursor->insertHtml(buf);
608 /* Position cursor to end of screen */
609 void Console::update_cursor()
611 m_textEdit->moveCursor(QTextCursor::End);
612 m_textEdit->ensureCursorVisible();
615 void Console::beginNewCommand(int conn)
617 DirComm *dircomm = m_dircommHash.value(conn);
619 for (int i=0; i < 3; i++) {
621 while (dircomm->read() > 0) {
622 if (mainWin->m_commDebug) Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
623 if (mainWin->m_displayAll) display_text(dircomm->msg());
625 if (dircomm->m_at_main_prompt) {
632 void Console::displayToPrompt(int conn)
634 DirComm *dircomm = m_dircommHash.value(conn);
638 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
639 while (!dircomm->m_at_prompt) {
640 if ((stat=dircomm->read()) > 0) {
641 buf += dircomm->msg();
642 if (buf.size() >= 8196 || m_messages_pending) {
645 messagesPending(false);
650 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
653 void Console::discardToPrompt(int conn)
655 DirComm *dircomm = m_dircommHash.value(conn);
658 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
659 if (mainWin->m_displayAll) {
660 displayToPrompt(conn);
662 while (!dircomm->m_at_prompt) {
663 stat = dircomm->read();
669 if (mainWin->m_commDebug) {
670 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
674 QString Console::returnFromPrompt(int conn)
676 DirComm *dircomm = m_dircommHash.value(conn);
682 text += dircomm->msg();
683 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
684 while (!dircomm->m_at_prompt) {
685 if ((stat=dircomm->read()) > 0) {
686 text += dircomm->msg();
689 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
694 * When the notifier is enabled, read_dir() will automatically be
695 * called by the Qt event loop when ever there is any output
696 * from the Director, and read_dir() will then display it on
699 * When we are in a bat dialog, we want to control *all* output
700 * from the Director, so we set notify to off.
701 * m_console->notifiy(false);
704 /* dual purpose function to turn notify off and return a connection */
705 int Console::notifyOff()
708 if (getDirComm(conn)) {
714 /* knowing a connection, turn notify off or on */
715 bool Console::notify(int conn, bool enable)
717 DirComm *dircomm = m_dircommHash.value(conn);
718 return dircomm->notify(enable);
721 /* knowing a connection, return notify state */
722 bool Console::is_notify_enabled(int conn) const
724 DirComm *dircomm = m_dircommHash.value(conn);
725 return dircomm->is_notify_enabled();
728 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
730 m_directorTreeItem = item;
733 void Console::setDirRes(DIRRES *dir)
739 * To have the ability to get the name of the director resource.
741 void Console::getDirResName(QString &name_returned)
743 name_returned = m_dir->name();
746 /* Slot for responding to page selectors status help command */
747 void Console::consoleHelp()
753 /* Slot for responding to page selectors reload bacula-dir.conf */
754 void Console::consoleReload()
756 QString cmd("reload");
760 /* For suppressing .messages
761 * This may be rendered not needed if the multiple connections feature gets working */
762 bool Console::hasFocus()
764 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
770 /* For adding feature to have the gui's messages button change when
771 * messages are pending */
772 bool Console::messagesPending(bool pend)
774 bool prev = m_messages_pending;
775 m_messages_pending = pend;
776 mainWin->setMessageIcon();
780 /* terminate all existing connections */
781 void Console::terminate()
783 foreach(DirComm* dircomm, m_dircommHash) {
784 dircomm->terminate();
786 m_console->stopTimer();
789 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
790 bool Console::is_connectedGui()
792 if (is_connected(0)) {
795 QString message = tr("Director is currently disconnected\nPlease reconnect!");
796 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
801 int Console::read(int conn)
803 DirComm *dircomm = m_dircommHash.value(conn);
804 return dircomm->read();
807 char *Console::msg(int conn)
809 DirComm *dircomm = m_dircommHash.value(conn);
810 return dircomm->msg();
813 int Console::write(int conn, const QString msg)
815 DirComm *dircomm = m_dircommHash.value(conn);
816 mainWin->waitEnter();
817 int ret = dircomm->write(msg);
822 int Console::write(int conn, const char *msg)
824 DirComm *dircomm = m_dircommHash.value(conn);
825 mainWin->waitEnter();
826 int ret = dircomm->write(msg);
831 /* This checks to see if any is connected */
832 bool Console::is_connected()
834 bool connected = false;
835 foreach(DirComm* dircomm, m_dircommHash) {
836 if (dircomm->is_connected())
842 /* knowing the connection id, is it connected */
843 bool Console::is_connected(int conn)
845 DirComm *dircomm = m_dircommHash.value(conn);
846 return dircomm->is_connected();
850 * Need a connection. Check existing connections or create one
852 bool Console::getDirComm(int &conn)
854 if (findDirComm(conn)) {
857 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
858 return newDirComm(conn);
863 * Try to find a free (unused but established) connection
864 * KES: Note, I think there is a problem here because for
865 * some reason, the notifier is often turned off on file
866 * descriptors that seem to me to be available. That means
867 * that we do not use a free descriptor and thus we will create
868 * a new connection that is maybe not necessary. Someone needs
869 * to look into whether or not notify() is correctly turned on
870 * when we are back at the command prompt and idle.
873 bool Console::findDirComm(int &conn)
876 QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
877 while (iter != m_dircommHash.constEnd()) {
878 DirComm *dircomm = iter.value();
879 if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
880 conn = dircomm->m_conn;
883 if (mainWin->m_connDebug) {
884 Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
885 i, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
894 * Create a new connection
896 bool Console::newDirComm(int &conn)
899 if (mainWin->m_connDebug) {
900 Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
902 DirComm *dircomm = new DirComm(this, m_dircommCounter);
903 m_dircommHash.insert(m_dircommCounter, dircomm);
904 bool success = dircomm->connect_dir();
905 if (mainWin->m_connDebug) {
907 Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
909 Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
910 m_dircommCounter, m_dir->name());
914 m_dircommHash.remove(m_dircommCounter);
918 conn = m_dircommCounter;