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)
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 m_cursor->insertText(buf);
591 void Console::display_text(const char *buf)
593 m_cursor->insertText(buf);
597 void Console::display_html(const QString buf)
599 m_cursor->insertHtml(buf);
603 /* Position cursor to end of screen */
604 void Console::update_cursor()
606 m_textEdit->moveCursor(QTextCursor::End);
607 m_textEdit->ensureCursorVisible();
610 void Console::beginNewCommand(int conn)
612 DirComm *dircomm = m_dircommHash.value(conn);
614 for (int i=0; i < 3; i++) {
616 while (dircomm->read() > 0) {
617 Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
618 if (mainWin->m_displayAll) display_text(dircomm->msg());
620 if (dircomm->m_at_main_prompt) {
627 void Console::displayToPrompt(int conn)
629 DirComm *dircomm = m_dircommHash.value(conn);
633 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
634 while (!dircomm->m_at_prompt) {
635 if ((stat=dircomm->read()) > 0) {
636 buf += dircomm->msg();
637 if (buf.size() >= 8196 || m_messages_pending) {
640 messagesPending(false);
645 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
648 void Console::discardToPrompt(int conn)
650 DirComm *dircomm = m_dircommHash.value(conn);
653 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
654 if (mainWin->m_displayAll) {
655 displayToPrompt(conn);
657 while (!dircomm->m_at_prompt) {
658 stat = dircomm->read();
664 if (mainWin->m_commDebug) {
665 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
669 QString Console::returnFromPrompt(int conn)
671 DirComm *dircomm = m_dircommHash.value(conn);
677 text += dircomm->msg();
678 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
679 while (!dircomm->m_at_prompt) {
680 if ((stat=dircomm->read()) > 0) {
681 text += dircomm->msg();
684 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
689 * When the notifier is enabled, read_dir() will automatically be
690 * called by the Qt event loop when ever there is any output
691 * from the Director, and read_dir() will then display it on
694 * When we are in a bat dialog, we want to control *all* output
695 * from the Director, so we set notify to off.
696 * m_console->notifiy(false);
699 /* dual purpose function to turn notify off and return a connection */
700 int Console::notifyOff()
703 if (getDirComm(conn)) {
709 /* knowing a connection, turn notify off or on */
710 bool Console::notify(int conn, bool enable)
712 DirComm *dircomm = m_dircommHash.value(conn);
713 return dircomm->notify(enable);
716 /* knowing a connection, return notify state */
717 bool Console::is_notify_enabled(int conn) const
719 DirComm *dircomm = m_dircommHash.value(conn);
720 return dircomm->is_notify_enabled();
723 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
725 m_directorTreeItem = item;
728 void Console::setDirRes(DIRRES *dir)
734 * To have the ability to get the name of the director resource.
736 void Console::getDirResName(QString &name_returned)
738 name_returned = m_dir->name();
741 /* Slot for responding to page selectors status help command */
742 void Console::consoleHelp()
748 /* Slot for responding to page selectors reload bacula-dir.conf */
749 void Console::consoleReload()
751 QString cmd("reload");
755 /* For suppressing .messages
756 * This may be rendered not needed if the multiple connections feature gets working */
757 bool Console::hasFocus()
759 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
765 /* For adding feature to have the gui's messages button change when
766 * messages are pending */
767 bool Console::messagesPending(bool pend)
769 bool prev = m_messages_pending;
770 m_messages_pending = pend;
771 mainWin->setMessageIcon();
775 /* terminate all existing connections */
776 void Console::terminate()
778 foreach(DirComm* dircomm, m_dircommHash) {
779 dircomm->terminate();
781 m_console->stopTimer();
784 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
785 bool Console::is_connectedGui()
787 if (is_connected(0)) {
790 QString message = tr("Director is currently disconnected\nPlease reconnect!");
791 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
796 int Console::read(int conn)
798 DirComm *dircomm = m_dircommHash.value(conn);
799 return dircomm->read();
802 char *Console::msg(int conn)
804 DirComm *dircomm = m_dircommHash.value(conn);
805 return dircomm->msg();
808 int Console::write(int conn, const QString msg)
810 DirComm *dircomm = m_dircommHash.value(conn);
811 mainWin->waitEnter();
812 int ret = dircomm->write(msg);
817 int Console::write(int conn, const char *msg)
819 DirComm *dircomm = m_dircommHash.value(conn);
820 mainWin->waitEnter();
821 int ret = dircomm->write(msg);
826 /* This checks to see if any is connected */
827 bool Console::is_connected()
829 bool connected = false;
830 foreach(DirComm* dircomm, m_dircommHash) {
831 if (dircomm->is_connected())
837 /* knowing the connection id, is it connected */
838 bool Console::is_connected(int conn)
840 DirComm *dircomm = m_dircommHash.value(conn);
841 return dircomm->is_connected();
845 * Need a connection. Check existing connections or create one
847 bool Console::getDirComm(int &conn)
849 if (findDirComm(conn)) {
852 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
853 return newDirComm(conn);
858 * Try to find a free (unused but established) connection
859 * KES: Note, I think there is a problem here because for
860 * some reason, the notifier is often turned off on file
861 * descriptors that seem to me to be available. That means
862 * that we do not use a free descriptor and thus we will create
863 * a new connection that is maybe not necessary. Someone needs
864 * to look into whether or not notify() is correctly turned on
865 * when we are back at the command prompt and idle.
868 bool Console::findDirComm(int &conn)
871 QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
872 while (iter != m_dircommHash.constEnd()) {
873 DirComm *dircomm = iter.value();
874 if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
875 conn = dircomm->m_conn;
878 if (mainWin->m_connDebug) {
879 Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
880 i, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
889 * Create a new connection
891 bool Console::newDirComm(int &conn)
894 if (mainWin->m_connDebug) {
895 Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
897 DirComm *dircomm = new DirComm(this, m_dircommCounter);
898 m_dircommHash.insert(m_dircommCounter, dircomm);
899 bool success = dircomm->connect_dir();
900 if (mainWin->m_connDebug) {
902 Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
904 Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
905 m_dircommCounter, m_dir->name());
909 m_dircommHash.remove(m_dircommCounter);
913 conn = m_dircommCounter;