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*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)
168 restore_list.clear();
170 fileset_list.clear();
171 messages_list.clear();
173 storage_list.clear();
176 volstatus_list.clear();
177 mediatype_list.clear();
178 dir_cmd(conn, ".jobs", job_list);
179 dir_cmd(conn, ".jobs type=R", restore_list);
180 dir_cmd(conn, ".clients", client_list);
181 dir_cmd(conn, ".filesets", fileset_list);
182 dir_cmd(conn, ".msgs", messages_list);
183 dir_cmd(conn, ".pools", pool_list);
184 dir_cmd(conn, ".storage", storage_list);
185 dir_cmd(conn, ".types", type_list);
186 dir_cmd(conn, ".levels", level_list);
187 dir_cmd(conn, ".volstatus", volstatus_list);
188 dir_cmd(conn, ".mediatypes", mediatype_list);
189 dir_cmd(conn, ".locations", location_list);
191 if (mainWin->m_connDebug) {
192 QString dbgmsg = QString("jobs=%1 clients=%2 filesets=%3 msgs=%4 pools=%5 storage=%6 types=%7 levels=%8 conn=%9 %10\n")
193 .arg(job_list.count()).arg(client_list.count()).arg(fileset_list.count()).arg(messages_list.count())
194 .arg(pool_list.count()).arg(storage_list.count()).arg(type_list.count()).arg(level_list.count())
195 .arg(conn).arg(m_dir->name());
196 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
201 messages_list.sort();
209 * Overload function for dir_cmd with a QString
212 bool Console::dir_cmd(QString &cmd, QStringList &results)
214 return dir_cmd(cmd.toUtf8().data(), results);
218 * Overload function for dir_cmd, this is if connection is not worried about
220 bool Console::dir_cmd(const char *cmd, QStringList &results)
223 if (getDirComm(conn)) {
224 dir_cmd(conn, cmd, results);
227 Pmsg1(000, "dir_cmd failed to connect to %s\n", m_dir->name());
233 * Send a command to the Director, and return the
234 * results in a QStringList.
236 bool Console::dir_cmd(int conn, const char *cmd, QStringList &results)
238 mainWin->waitEnter();
239 DirComm *dircomm = m_dircommHash.value(conn);
241 bool prev_notify = mainWin->m_notify;
243 if (mainWin->m_connDebug) {
244 QString dbgmsg = QString("dir_cmd conn %1 %2 %3\n").arg(conn).arg(m_dir->name()).arg(cmd);
245 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
249 while ((stat = dircomm->read()) > 0 && dircomm->is_in_command()) {
250 if (mainWin->m_displayAll) display_text(dircomm->msg());
251 strip_trailing_junk(dircomm->msg());
252 results << dircomm->msg();
254 if (stat > 0 && mainWin->m_displayAll) display_text(dircomm->msg());
256 notify(conn, true); /* turn it back on */
258 discardToPrompt(conn);
260 return true; /* ***FIXME*** return any command error */
264 * OverLoads for sql_cmd
266 bool Console::sql_cmd(int &conn, QString &query, QStringList &results)
268 return sql_cmd(conn, query.toUtf8().data(), results, false);
271 bool Console::sql_cmd(QString &query, QStringList &results)
274 if (!getDirComm(conn)) {
277 return sql_cmd(conn, query.toUtf8().data(), results, true);
280 bool Console::sql_cmd(const char *query, QStringList &results)
283 if (!getDirComm(conn)) {
286 return sql_cmd(conn, query, results, true);
290 * Send an sql query to the Director, and return the
291 * results in a QStringList.
293 bool Console::sql_cmd(int &conn, const char *query, QStringList &results, bool donotify)
295 DirComm *dircomm = m_dircommHash.value(conn);
297 POOL_MEM cmd(PM_MESSAGE);
298 bool prev_notify = mainWin->m_notify;
300 if (!is_connectedGui()) {
304 if (mainWin->m_connDebug) Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
306 dircomm->notify(false);
308 mainWin->waitEnter();
310 pm_strcpy(cmd, ".sql query=\"");
311 pm_strcat(cmd, query);
312 pm_strcat(cmd, "\"");
313 dircomm->write(cmd.c_str());
314 while ((stat = dircomm->read()) > 0) {
316 if (mainWin->m_displayAll) {
317 display_text(dircomm->msg());
320 strip_trailing_junk(dircomm->msg());
321 bool doappend = true;
323 QString dum = dircomm->msg();
324 if ((dum.left(6) == "*None*")) doappend = false;
327 results << dircomm->msg();
331 if (donotify && prev_notify) {
332 dircomm->notify(true);
334 discardToPrompt(conn);
336 return !mainWin->isClosing(); /* return false if closing */
341 * Sending a command to the Director
343 int Console::write_dir(const char *msg)
346 if (getDirComm(conn)) {
347 write_dir(conn, msg);
352 int Console::write_dir(const char *msg, bool dowait)
355 if (getDirComm(conn)) {
356 write_dir(conn, msg, dowait);
361 void Console::write_dir(int conn, const char *msg)
363 write_dir(conn, msg, true);
367 * Send a command to the Director
369 void Console::write_dir(int conn, const char *msg, bool dowait)
371 DirComm *dircomm = m_dircommHash.value(conn);
373 if (dircomm->m_sock) {
374 mainWin->set_status(_("Processing command ..."));
376 mainWin->waitEnter();
381 mainWin->set_status( tr(" Director not connected. Click on connect button."));
382 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
383 QBrush redBrush(Qt::red);
384 QTreeWidgetItem *item = mainWin->getFromHash(this);
385 item->setForeground(0, redBrush);
386 dircomm->m_at_prompt = false;
387 dircomm->m_at_main_prompt = false;
392 * get_job_defaults overload
394 bool Console::get_job_defaults(struct job_defaults &job_defs)
397 return get_job_defaults(conn, job_defs, true);
400 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs)
402 return get_job_defaults(conn, job_defs, false);
406 * Send a job name to the director, and read all the resulting
409 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs, bool donotify)
414 bool prev_notify = mainWin->m_notify;
420 beginNewCommand(conn);
421 DirComm *dircomm = m_dircommHash.value(conn);
422 bool prevWaitState = mainWin->getWaitState();
424 mainWin->waitEnter();
425 if (mainWin->m_connDebug)
426 Pmsg2(000, "job_defaults conn %i %s\n", conn, m_dir->name());
427 scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
428 dircomm->write(scmd);
429 while ((stat = dircomm->read()) > 0) {
430 if (mainWin->m_displayAll) display_text(dircomm->msg());
431 def = strchr(dircomm->msg(), '=');
435 /* Pointer to default value */
437 strip_trailing_junk(def);
439 if (strcmp(dircomm->msg(), "job") == 0) {
440 if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
445 if (strcmp(dircomm->msg(), "pool") == 0) {
446 job_defs.pool_name = def;
449 if (strcmp(dircomm->msg(), "messages") == 0) {
450 job_defs.messages_name = def;
453 if (strcmp(dircomm->msg(), "client") == 0) {
454 job_defs.client_name = def;
457 if (strcmp(dircomm->msg(), "storage") == 0) {
458 job_defs.store_name = def;
461 if (strcmp(dircomm->msg(), "where") == 0) {
462 job_defs.where = def;
465 if (strcmp(dircomm->msg(), "level") == 0) {
466 job_defs.level = def;
469 if (strcmp(dircomm->msg(), "type") == 0) {
473 if (strcmp(dircomm->msg(), "fileset") == 0) {
474 job_defs.fileset_name = def;
477 if (strcmp(dircomm->msg(), "catalog") == 0) {
478 job_defs.catalog_name = def;
481 if (strcmp(dircomm->msg(), "enabled") == 0) {
482 job_defs.enabled = *def == '1' ? true : false;
487 /* Fall through wanted */
489 if (donotify && prev_notify) {
492 if (!prevWaitState) {
500 * Save user settings associated with this console
502 void Console::writeSettings()
504 QFont font = get_font();
506 QSettings settings(m_dir->name(), "bat");
507 settings.beginGroup("Console");
508 settings.setValue("consoleFont", font.family());
509 settings.setValue("consolePointSize", font.pointSize());
510 settings.setValue("consoleFixedPitch", font.fixedPitch());
515 * Read and restore user settings associated with this console
517 void Console::readSettings()
519 QFont font = get_font();
521 QSettings settings(m_dir->name(), "bat");
522 settings.beginGroup("Console");
523 font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
524 font.setPointSize(settings.value("consolePointSize", 10).toInt());
525 font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
527 m_textEdit->setFont(font);
531 * Set the console textEdit font
533 void Console::set_font()
536 QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
538 m_textEdit->setFont(font);
543 * Get the console text edit font
545 const QFont Console::get_font()
547 return m_textEdit->font();
551 * Slot for responding to status dir button on button bar
553 void Console::status_dir()
555 QString cmd("status dir");
560 * Slot for responding to messages button on button bar
561 * Here we want to bring the console to the front so use pages' consoleCommand
563 void Console::messages()
565 QString cmd(".messages");
567 messagesPending(false);
571 * Put text into the console window
573 void Console::display_textf(const char *fmt, ...)
578 va_start(arg_ptr, fmt);
579 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
584 void Console::display_text(const QString buf)
586 if (buf.size() != 0) {
587 m_cursor->insertText(buf);
593 void Console::display_text(const char *buf)
596 m_cursor->insertText(buf);
601 void Console::display_html(const QString buf)
603 if (buf.size() != 0) {
604 m_cursor->insertHtml(buf);
609 /* Position cursor to end of screen */
610 void Console::update_cursor()
612 m_textEdit->moveCursor(QTextCursor::End);
613 m_textEdit->ensureCursorVisible();
616 void Console::beginNewCommand(int conn)
618 DirComm *dircomm = m_dircommHash.value(conn);
620 for (int i=0; i < 3; i++) {
622 while (dircomm->read() > 0) {
623 if (mainWin->m_commDebug) Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
624 if (mainWin->m_displayAll) display_text(dircomm->msg());
626 if (dircomm->m_at_main_prompt) {
633 void Console::displayToPrompt(int conn)
635 DirComm *dircomm = m_dircommHash.value(conn);
639 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
640 while (!dircomm->m_at_prompt) {
641 if ((stat=dircomm->read()) > 0) {
642 buf += dircomm->msg();
643 if (buf.size() >= 8196 || m_messages_pending) {
646 messagesPending(false);
651 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
654 void Console::discardToPrompt(int conn)
656 DirComm *dircomm = m_dircommHash.value(conn);
659 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
660 if (mainWin->m_displayAll) {
661 displayToPrompt(conn);
663 while (!dircomm->m_at_prompt) {
664 stat = dircomm->read();
670 if (mainWin->m_commDebug) {
671 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
675 QString Console::returnFromPrompt(int conn)
677 DirComm *dircomm = m_dircommHash.value(conn);
683 text += dircomm->msg();
684 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
685 while (!dircomm->m_at_prompt) {
686 if ((stat=dircomm->read()) > 0) {
687 text += dircomm->msg();
690 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
695 * When the notifier is enabled, read_dir() will automatically be
696 * called by the Qt event loop when ever there is any output
697 * from the Director, and read_dir() will then display it on
700 * When we are in a bat dialog, we want to control *all* output
701 * from the Director, so we set notify to off.
702 * m_console->notifiy(false);
705 /* dual purpose function to turn notify off and return a connection */
706 int Console::notifyOff()
709 if (getDirComm(conn)) {
715 /* knowing a connection, turn notify off or on */
716 bool Console::notify(int conn, bool enable)
718 DirComm *dircomm = m_dircommHash.value(conn);
719 return dircomm->notify(enable);
722 /* knowing a connection, return notify state */
723 bool Console::is_notify_enabled(int conn) const
725 DirComm *dircomm = m_dircommHash.value(conn);
726 return dircomm->is_notify_enabled();
729 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
731 m_directorTreeItem = item;
734 void Console::setDirRes(DIRRES *dir)
740 * To have the ability to get the name of the director resource.
742 void Console::getDirResName(QString &name_returned)
744 name_returned = m_dir->name();
747 /* Slot for responding to page selectors status help command */
748 void Console::consoleHelp()
754 /* Slot for responding to page selectors reload bacula-dir.conf */
755 void Console::consoleReload()
757 QString cmd("reload");
761 /* For suppressing .messages
762 * This may be rendered not needed if the multiple connections feature gets working */
763 bool Console::hasFocus()
765 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
771 /* For adding feature to have the gui's messages button change when
772 * messages are pending */
773 bool Console::messagesPending(bool pend)
775 bool prev = m_messages_pending;
776 m_messages_pending = pend;
777 mainWin->setMessageIcon();
781 /* terminate all existing connections */
782 void Console::terminate()
784 foreach(DirComm* dircomm, m_dircommHash) {
785 dircomm->terminate();
787 m_console->stopTimer();
790 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
791 bool Console::is_connectedGui()
793 if (is_connected(0)) {
796 QString message = tr("Director is currently disconnected\nPlease reconnect!");
797 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
802 int Console::read(int conn)
804 DirComm *dircomm = m_dircommHash.value(conn);
805 return dircomm->read();
808 char *Console::msg(int conn)
810 DirComm *dircomm = m_dircommHash.value(conn);
811 return dircomm->msg();
814 int Console::write(int conn, const QString msg)
816 DirComm *dircomm = m_dircommHash.value(conn);
817 mainWin->waitEnter();
818 int ret = dircomm->write(msg);
823 int Console::write(int conn, const char *msg)
825 DirComm *dircomm = m_dircommHash.value(conn);
826 mainWin->waitEnter();
827 int ret = dircomm->write(msg);
832 /* This checks to see if any is connected */
833 bool Console::is_connected()
835 bool connected = false;
836 foreach(DirComm* dircomm, m_dircommHash) {
837 if (dircomm->is_connected())
843 /* knowing the connection id, is it connected */
844 bool Console::is_connected(int conn)
846 DirComm *dircomm = m_dircommHash.value(conn);
847 return dircomm->is_connected();
851 * Need a connection. Check existing connections or create one
853 bool Console::getDirComm(int &conn)
855 if (findDirComm(conn)) {
858 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
859 return newDirComm(conn);
864 * Try to find a free (unused but established) connection
865 * KES: Note, I think there is a problem here because for
866 * some reason, the notifier is often turned off on file
867 * descriptors that seem to me to be available. That means
868 * that we do not use a free descriptor and thus we will create
869 * a new connection that is maybe not necessary. Someone needs
870 * to look into whether or not notify() is correctly turned on
871 * when we are back at the command prompt and idle.
874 bool Console::findDirComm(int &conn)
877 QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
878 while (iter != m_dircommHash.constEnd()) {
879 DirComm *dircomm = iter.value();
880 if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
881 conn = dircomm->m_conn;
884 if (mainWin->m_connDebug) {
885 Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
886 i, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
895 * Create a new connection
897 bool Console::newDirComm(int &conn)
900 if (mainWin->m_connDebug) {
901 Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
903 DirComm *dircomm = new DirComm(this, m_dircommCounter);
904 m_dircommHash.insert(m_dircommCounter, dircomm);
905 bool success = dircomm->connect_dir();
906 if (mainWin->m_connDebug) {
908 Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
910 Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
911 m_dircommCounter, m_dir->name());
915 m_dircommHash.remove(m_dircommCounter);
919 conn = m_dircommCounter;