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()
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) Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
297 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();
323 dircomm->notify(true);
325 discardToPrompt(conn);
327 return !mainWin->isClosing(); /* return false if closing */
332 * Sending a command to the Director
334 int Console::write_dir(const char *msg)
337 if (getDirComm(conn)) {
338 write_dir(conn, msg);
343 int Console::write_dir(const char *msg, bool dowait)
346 if (getDirComm(conn)) {
347 write_dir(conn, msg, dowait);
352 void Console::write_dir(int conn, const char *msg)
354 write_dir(conn, msg, true);
358 * Send a command to the Director
360 void Console::write_dir(int conn, const char *msg, bool dowait)
362 DirComm *dircomm = m_dircommHash.value(conn);
364 if (dircomm->m_sock) {
365 mainWin->set_status(_("Processing command ..."));
367 mainWin->waitEnter();
372 mainWin->set_status( tr(" Director not connected. Click on connect button."));
373 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
374 QBrush redBrush(Qt::red);
375 QTreeWidgetItem *item = mainWin->getFromHash(this);
376 item->setForeground(0, redBrush);
377 dircomm->m_at_prompt = false;
378 dircomm->m_at_main_prompt = false;
383 * get_job_defaults overload
385 bool Console::get_job_defaults(struct job_defaults &job_defs)
388 return get_job_defaults(conn, job_defs, true);
391 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs)
393 return get_job_defaults(conn, job_defs, false);
397 * Send a job name to the director, and read all the resulting
400 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs, bool donotify)
408 beginNewCommand(conn);
409 DirComm *dircomm = m_dircommHash.value(conn);
410 bool prevWaitState = mainWin->getWaitState();
412 mainWin->waitEnter();
413 if (mainWin->m_connDebug)
414 Pmsg2(000, "job_defaults conn %i %s\n", conn, m_dir->name());
415 scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
416 dircomm->write(scmd);
417 while ((stat = dircomm->read()) > 0) {
418 if (mainWin->m_displayAll) display_text(dircomm->msg());
419 def = strchr(dircomm->msg(), '=');
423 /* Pointer to default value */
425 strip_trailing_junk(def);
427 if (strcmp(dircomm->msg(), "job") == 0) {
428 if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
433 if (strcmp(dircomm->msg(), "pool") == 0) {
434 job_defs.pool_name = def;
437 if (strcmp(dircomm->msg(), "messages") == 0) {
438 job_defs.messages_name = def;
441 if (strcmp(dircomm->msg(), "client") == 0) {
442 job_defs.client_name = def;
445 if (strcmp(dircomm->msg(), "storage") == 0) {
446 job_defs.store_name = def;
449 if (strcmp(dircomm->msg(), "where") == 0) {
450 job_defs.where = def;
453 if (strcmp(dircomm->msg(), "level") == 0) {
454 job_defs.level = def;
457 if (strcmp(dircomm->msg(), "type") == 0) {
461 if (strcmp(dircomm->msg(), "fileset") == 0) {
462 job_defs.fileset_name = def;
465 if (strcmp(dircomm->msg(), "catalog") == 0) {
466 job_defs.catalog_name = def;
469 if (strcmp(dircomm->msg(), "enabled") == 0) {
470 job_defs.enabled = *def == '1' ? true : false;
491 * Save user settings associated with this console
493 void Console::writeSettings()
495 QFont font = get_font();
497 QSettings settings(m_dir->name(), "bat");
498 settings.beginGroup("Console");
499 settings.setValue("consoleFont", font.family());
500 settings.setValue("consolePointSize", font.pointSize());
501 settings.setValue("consoleFixedPitch", font.fixedPitch());
506 * Read and restore user settings associated with this console
508 void Console::readSettings()
510 QFont font = get_font();
512 QSettings settings(m_dir->name(), "bat");
513 settings.beginGroup("Console");
514 font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
515 font.setPointSize(settings.value("consolePointSize", 10).toInt());
516 font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
518 m_textEdit->setFont(font);
522 * Set the console textEdit font
524 void Console::set_font()
527 QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
529 m_textEdit->setFont(font);
534 * Get the console text edit font
536 const QFont Console::get_font()
538 return m_textEdit->font();
542 * Slot for responding to status dir button on button bar
544 void Console::status_dir()
546 QString cmd("status dir");
551 * Slot for responding to messages button on button bar
552 * Here we want to bring the console to the front so use pages' consoleCommand
554 void Console::messages()
556 QString cmd(".messages");
558 messagesPending(false);
562 * Put text into the console window
564 void Console::display_textf(const char *fmt, ...)
569 va_start(arg_ptr, fmt);
570 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
575 void Console::display_text(const QString buf)
577 m_cursor->insertText(buf);
582 void Console::display_text(const char *buf)
584 m_cursor->insertText(buf);
588 void Console::display_html(const QString buf)
590 m_cursor->insertHtml(buf);
594 /* Position cursor to end of screen */
595 void Console::update_cursor()
597 m_textEdit->moveCursor(QTextCursor::End);
598 m_textEdit->ensureCursorVisible();
601 void Console::beginNewCommand(int conn)
603 DirComm *dircomm = m_dircommHash.value(conn);
605 for (int i=0; i < 3; i++) {
607 while (dircomm->read() > 0) {
608 Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
609 if (mainWin->m_displayAll) display_text(dircomm->msg());
611 if (dircomm->m_at_main_prompt) {
618 void Console::displayToPrompt(int conn)
620 DirComm *dircomm = m_dircommHash.value(conn);
624 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
625 while (!dircomm->m_at_prompt) {
626 if ((stat=dircomm->read()) > 0) {
627 buf += dircomm->msg();
628 if (buf.size() >= 8196 || m_messages_pending) {
631 messagesPending(false);
636 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
639 void Console::discardToPrompt(int conn)
641 DirComm *dircomm = m_dircommHash.value(conn);
644 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
645 if (mainWin->m_displayAll) {
646 displayToPrompt(conn);
648 while (!dircomm->m_at_prompt) {
649 stat = dircomm->read();
655 if (mainWin->m_commDebug) {
656 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
660 QString Console::returnFromPrompt(int conn)
662 DirComm *dircomm = m_dircommHash.value(conn);
667 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
668 while (!dircomm->m_at_prompt) {
669 if ((stat=dircomm->read()) > 0) {
670 text += dircomm->msg();
673 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
678 * When the notifier is enabled, read_dir() will automatically be
679 * called by the Qt event loop when ever there is any output
680 * from the Director, and read_dir() will then display it on
683 * When we are in a bat dialog, we want to control *all* output
684 * from the Director, so we set notify to off.
685 * m_console->notifiy(false);
688 /* dual purpose function to turn notify off and return a connection */
689 int Console::notifyOff()
692 if (getDirComm(conn)) {
698 /* knowing a connection, turn notify off or on */
699 bool Console::notify(int conn, bool enable)
701 DirComm *dircomm = m_dircommHash.value(conn);
702 return dircomm->notify(enable);
705 /* knowing a connection, return notify state */
706 bool Console::is_notify_enabled(int conn) const
708 DirComm *dircomm = m_dircommHash.value(conn);
709 return dircomm->is_notify_enabled();
712 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
714 m_directorTreeItem = item;
717 void Console::setDirRes(DIRRES *dir)
723 * To have the ability to get the name of the director resource.
725 void Console::getDirResName(QString &name_returned)
727 name_returned = m_dir->name();
730 /* Slot for responding to page selectors status help command */
731 void Console::consoleHelp()
737 /* Slot for responding to page selectors reload bacula-dir.conf */
738 void Console::consoleReload()
740 QString cmd("reload");
744 /* For suppressing .messages
745 * This may be rendered not needed if the multiple connections feature gets working */
746 bool Console::hasFocus()
748 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
754 /* For adding feature to have the gui's messages button change when
755 * messages are pending */
756 bool Console::messagesPending(bool pend)
758 bool prev = m_messages_pending;
759 m_messages_pending = pend;
760 mainWin->setMessageIcon();
764 /* terminate all existing connections */
765 void Console::terminate()
767 foreach(DirComm* dircomm, m_dircommHash) {
768 dircomm->terminate();
770 m_console->stopTimer();
773 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
774 bool Console::is_connectedGui()
776 if (is_connected(0)) {
779 QString message = tr("Director is currently disconnected\nPlease reconnect!");
780 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
785 int Console::read(int conn)
787 DirComm *dircomm = m_dircommHash.value(conn);
788 return dircomm->read();
791 char *Console::msg(int conn)
793 DirComm *dircomm = m_dircommHash.value(conn);
794 return dircomm->msg();
797 int Console::write(int conn, const QString msg)
799 DirComm *dircomm = m_dircommHash.value(conn);
800 mainWin->waitEnter();
801 int ret = dircomm->write(msg);
806 int Console::write(int conn, const char *msg)
808 DirComm *dircomm = m_dircommHash.value(conn);
809 mainWin->waitEnter();
810 int ret = dircomm->write(msg);
815 /* This checks to see if any is connected */
816 bool Console::is_connected()
818 bool connected = false;
819 foreach(DirComm* dircomm, m_dircommHash) {
820 if (dircomm->is_connected())
826 /* knowing the connection id, is it connected */
827 bool Console::is_connected(int conn)
829 DirComm *dircomm = m_dircommHash.value(conn);
830 return dircomm->is_connected();
834 * Need a connection. Check existing connections or create one
836 bool Console::getDirComm(int &conn)
838 if (findDirComm(conn)) {
841 if (mainWin->m_connDebug) {
842 Pmsg0(000, "call newDirComm\n");
844 return newDirComm(conn);
849 * Try to find a free (unused but established) connection
850 * KES: Note, I think there is a problem here because for
851 * some reason, the notifier is often turned off on file
852 * descriptors that seem to me to be available. That means
853 * that we do not use a free descriptor and thus we will create
854 * a new connection that is maybe not necessary. Someone needs
855 * to look into whether or not notify() is correctly turned on
856 * when we are back at the command prompt and idle.
859 bool Console::findDirComm(int &conn)
862 QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
863 while (iter != m_dircommHash.constEnd()) {
864 DirComm *dircomm = iter.value();
865 if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
866 conn = dircomm->m_conn;
869 if (mainWin->m_connDebug) {
870 Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
871 i, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
880 * Create a new connection
882 bool Console::newDirComm(int &conn)
885 if (mainWin->m_connDebug) {
886 Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
888 DirComm *dircomm = new DirComm(this, m_dircommCounter);
889 m_dircommHash.insert(m_dircommCounter, dircomm);
890 bool success = dircomm->connect_dir();
891 if (mainWin->m_connDebug) {
893 Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
895 Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
896 m_dircommCounter, m_dir->name());
900 m_dircommHash.remove(m_dircommCounter);
904 conn = m_dircommCounter;