2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
19 * Written by Kern Sibbald, January MMVII
29 Console::Console(QTabWidget *parent) : Pages()
32 m_name = tr("Console");
33 m_messages_pending = false;
37 m_warningPrevent = false;
41 * Create a connection to the Director and put it in a hash table
43 m_dircommHash.insert(m_dircommCounter, new DirComm(this, m_dircommCounter));
46 m_textEdit = textEdit; /* our console screen */
47 m_cursor = new QTextCursor(m_textEdit->document());
48 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
51 m_contextActions.append(actionStatusDir);
52 m_contextActions.append(actionConsoleHelp);
53 m_contextActions.append(actionRequestMessages);
54 m_contextActions.append(actionConsoleReload);
55 connect(actionStatusDir, SIGNAL(triggered()), this, SLOT(status_dir()));
56 connect(actionConsoleHelp, SIGNAL(triggered()), this, SLOT(consoleHelp()));
57 connect(actionConsoleReload, SIGNAL(triggered()), this, SLOT(consoleReload()));
58 connect(actionRequestMessages, SIGNAL(triggered()), this, SLOT(messages()));
65 void Console::startTimer()
67 m_timer = new QTimer(this);
68 QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
69 m_timer->start(mainWin->m_checkMessagesInterval*30000);
72 void Console::stopTimer()
75 QWidget::disconnect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
82 /* slot connected to the timer
83 * requires preferences of check messages and operates at interval */
84 void Console::poll_messages()
88 /* Do not poll if notifier off */
89 if (!mainWin->m_notify) {
94 * Note if we call getDirComm here, we continuously consume
97 if (!findDirComm(conn)) { /* find a free DirComm */
98 return; /* try later */
101 DirComm *dircomm = m_dircommHash.value(conn);
102 if (mainWin->m_checkMessages && dircomm->m_at_main_prompt && hasFocus() && !mainWin->getWaitState()){
103 dircomm->write(".messages");
104 displayToPrompt(conn);
105 messagesPending(false);
110 * Connect to Director. This does not connect to the director, dircomm does.
111 * This creates the first and possibly 2nd dircomm instance
113 void Console::connect_dir()
115 DirComm *dircomm = m_dircommHash.value(0);
117 if (!m_console->m_dir) {
118 mainWin->set_status( tr("No Director found."));
122 m_textEdit = textEdit; /* our console screen */
124 if (dircomm->connect_dir()) {
125 if (mainWin->m_connDebug) {
126 Pmsg1(000, "DirComm 0 Seems to have Connected %s\n", m_dir->name());
130 mainWin->set_status(_("Connected"));
132 startTimer(); /* start message timer */
136 * A function created to separate out the population of the lists
137 * from the Console::connect_dir function
139 void Console::populateLists(bool /*forcenew*/)
142 if (!getDirComm(conn)) {
143 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
144 if (!newDirComm(conn)) {
145 Emsg1(M_INFO, 0, "Failed to connect to %s for populateLists.\n", m_dir->name());
153 void Console::populateLists(int conn)
156 restore_list.clear();
158 fileset_list.clear();
159 messages_list.clear();
161 storage_list.clear();
164 volstatus_list.clear();
165 mediatype_list.clear();
166 dir_cmd(conn, ".jobs", job_list);
167 dir_cmd(conn, ".jobs type=R", restore_list);
168 dir_cmd(conn, ".clients", client_list);
169 dir_cmd(conn, ".filesets", fileset_list);
170 dir_cmd(conn, ".msgs", messages_list);
171 dir_cmd(conn, ".pools", pool_list);
172 dir_cmd(conn, ".storage", storage_list);
173 dir_cmd(conn, ".types", type_list);
174 dir_cmd(conn, ".levels", level_list);
175 dir_cmd(conn, ".volstatus", volstatus_list);
176 dir_cmd(conn, ".mediatypes", mediatype_list);
177 dir_cmd(conn, ".locations", location_list);
179 if (mainWin->m_connDebug) {
180 QString dbgmsg = QString("jobs=%1 clients=%2 filesets=%3 msgs=%4 pools=%5 storage=%6 types=%7 levels=%8 conn=%9 %10\n")
181 .arg(job_list.count()).arg(client_list.count()).arg(fileset_list.count()).arg(messages_list.count())
182 .arg(pool_list.count()).arg(storage_list.count()).arg(type_list.count()).arg(level_list.count())
183 .arg(conn).arg(m_dir->name());
184 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
189 messages_list.sort();
197 * Overload function for dir_cmd with a QString
200 bool Console::dir_cmd(QString &cmd, QStringList &results)
202 return dir_cmd(cmd.toUtf8().data(), results);
206 * Overload function for dir_cmd, this is if connection is not worried about
208 bool Console::dir_cmd(const char *cmd, QStringList &results)
211 if (getDirComm(conn)) {
212 dir_cmd(conn, cmd, results);
215 Pmsg1(000, "dir_cmd failed to connect to %s\n", m_dir->name());
221 * Send a command to the Director, and return the
222 * results in a QStringList.
224 bool Console::dir_cmd(int conn, const char *cmd, QStringList &results)
226 mainWin->waitEnter();
227 DirComm *dircomm = m_dircommHash.value(conn);
229 bool prev_notify = is_notify_enabled(conn);
231 if (mainWin->m_connDebug) {
232 QString dbgmsg = QString("dir_cmd conn %1 %2 %3\n").arg(conn).arg(m_dir->name()).arg(cmd);
233 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
239 while ((stat = dircomm->read()) > 0 && dircomm->is_in_command()) {
240 if (mainWin->m_displayAll) display_text(dircomm->msg());
241 strip_trailing_junk(dircomm->msg());
242 results << dircomm->msg();
244 if (stat > 0 && mainWin->m_displayAll) display_text(dircomm->msg());
246 notify(conn, true); /* turn it back on */
248 discardToPrompt(conn);
250 return true; /* ***FIXME*** return any command error */
254 * OverLoads for sql_cmd
256 bool Console::sql_cmd(int &conn, QString &query, QStringList &results)
258 return sql_cmd(conn, query.toUtf8().data(), results, false);
261 bool Console::sql_cmd(QString &query, QStringList &results)
264 if (!getDirComm(conn)) {
267 return sql_cmd(conn, query.toUtf8().data(), results, true);
270 bool Console::sql_cmd(const char *query, QStringList &results)
273 if (!getDirComm(conn)) {
276 return sql_cmd(conn, query, results, true);
280 * Send an sql query to the Director, and return the
281 * results in a QStringList.
283 bool Console::sql_cmd(int &conn, const char *query, QStringList &results, bool donotify)
285 DirComm *dircomm = m_dircommHash.value(conn);
287 POOL_MEM cmd(PM_MESSAGE);
288 bool prev_notify = is_notify_enabled(conn);
290 if (!is_connectedGui()) {
294 if (mainWin->m_connDebug) Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
296 dircomm->notify(false);
298 mainWin->waitEnter();
300 pm_strcpy(cmd, ".sql query=\"");
301 pm_strcat(cmd, query);
302 pm_strcat(cmd, "\"");
303 dircomm->write(cmd.c_str());
304 while ((stat = dircomm->read()) > 0) {
306 if (mainWin->m_displayAll) {
307 display_text(dircomm->msg());
310 strip_trailing_junk(dircomm->msg());
311 bool doappend = true;
313 QString dum = dircomm->msg();
314 if ((dum.left(6) == "*None*")) doappend = false;
317 results << dircomm->msg();
321 if (donotify && prev_notify) {
322 dircomm->notify(true);
324 discardToPrompt(conn);
326 if (donotify && prev_notify) {
327 dircomm->notify(true);
329 return !mainWin->isClosing(); /* return false if closing */
334 * Sending a command to the Director
336 int Console::write_dir(const char *msg)
339 if (getDirComm(conn)) {
340 write_dir(conn, msg);
345 int Console::write_dir(const char *msg, bool dowait)
348 if (getDirComm(conn)) {
349 write_dir(conn, msg, dowait);
354 void Console::write_dir(int conn, const char *msg)
356 write_dir(conn, msg, true);
360 * Send a command to the Director
362 void Console::write_dir(int conn, const char *msg, bool dowait)
364 DirComm *dircomm = m_dircommHash.value(conn);
366 if (dircomm->m_sock) {
367 mainWin->set_status(_("Processing command ..."));
369 mainWin->waitEnter();
374 mainWin->set_status( tr(" Director not connected. Click on connect button."));
375 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
376 QBrush redBrush(Qt::red);
377 QTreeWidgetItem *item = mainWin->getFromHash(this);
378 item->setForeground(0, redBrush);
379 dircomm->m_at_prompt = false;
380 dircomm->m_at_main_prompt = false;
385 * get_job_defaults overload
387 bool Console::get_job_defaults(struct job_defaults &job_defs)
391 return get_job_defaults(conn, job_defs, true);
394 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs)
396 return get_job_defaults(conn, job_defs, true);
400 * Send a job name to the director, and read all the resulting
403 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs, bool donotify)
408 bool prev_notify = is_notify_enabled(conn);
410 DirComm *dircomm = m_dircommHash.value(conn);
413 dircomm->notify(false);
415 beginNewCommand(conn);
416 bool prevWaitState = mainWin->getWaitState();
418 mainWin->waitEnter();
419 if (mainWin->m_connDebug)
420 Pmsg2(000, "job_defaults conn %i %s\n", conn, m_dir->name());
421 scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
422 dircomm->write(scmd);
423 while ((stat = dircomm->read()) > 0) {
424 if (mainWin->m_displayAll) display_text(dircomm->msg());
425 def = strchr(dircomm->msg(), '=');
429 /* Pointer to default value */
431 strip_trailing_junk(def);
433 if (strcmp(dircomm->msg(), "job") == 0) {
434 if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
439 if (strcmp(dircomm->msg(), "pool") == 0) {
440 job_defs.pool_name = def;
443 if (strcmp(dircomm->msg(), "messages") == 0) {
444 job_defs.messages_name = def;
447 if (strcmp(dircomm->msg(), "client") == 0) {
448 job_defs.client_name = def;
451 if (strcmp(dircomm->msg(), "storage") == 0) {
452 job_defs.store_name = def;
455 if (strcmp(dircomm->msg(), "where") == 0) {
456 job_defs.where = def;
459 if (strcmp(dircomm->msg(), "level") == 0) {
460 job_defs.level = def;
463 if (strcmp(dircomm->msg(), "type") == 0) {
467 if (strcmp(dircomm->msg(), "fileset") == 0) {
468 job_defs.fileset_name = def;
471 if (strcmp(dircomm->msg(), "catalog") == 0) {
472 job_defs.catalog_name = def;
475 if (strcmp(dircomm->msg(), "enabled") == 0) {
476 job_defs.enabled = *def == '1' ? true : false;
481 /* Fall through wanted */
483 if (donotify && prev_notify) {
486 if (!prevWaitState) {
494 * Save user settings associated with this console
496 void Console::writeSettings()
498 QFont font = get_font();
500 QSettings settings(m_dir->name(), "bat");
501 settings.beginGroup("Console");
502 settings.setValue("consoleFont", font.family());
503 settings.setValue("consolePointSize", font.pointSize());
504 settings.setValue("consoleFixedPitch", font.fixedPitch());
509 * Read and restore user settings associated with this console
511 void Console::readSettings()
513 QFont font = get_font();
515 QSettings settings(m_dir->name(), "bat");
516 settings.beginGroup("Console");
517 font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
518 font.setPointSize(settings.value("consolePointSize", 10).toInt());
519 font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
521 m_textEdit->setFont(font);
525 * Set the console textEdit font
527 void Console::set_font()
530 QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
532 m_textEdit->setFont(font);
537 * Get the console text edit font
539 const QFont Console::get_font()
541 return m_textEdit->font();
545 * Slot for responding to status dir button on button bar
547 void Console::status_dir()
549 QString cmd("status dir");
554 * Slot for responding to messages button on button bar
555 * Here we want to bring the console to the front so use pages' consoleCommand
557 void Console::messages()
559 QString cmd(".messages");
561 messagesPending(false);
565 * Put text into the console window
567 void Console::display_textf(const char *fmt, ...)
571 va_start(arg_ptr, fmt);
572 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
577 void Console::display_text(const QString buf)
579 if (buf.size() != 0) {
580 m_cursor->insertText(buf);
586 void Console::display_text(const char *buf)
589 m_cursor->insertText(buf);
594 void Console::display_html(const QString buf)
596 if (buf.size() != 0) {
597 m_cursor->insertHtml(buf);
602 /* Position cursor to end of screen */
603 void Console::update_cursor()
605 m_textEdit->moveCursor(QTextCursor::End);
606 m_textEdit->ensureCursorVisible();
609 void Console::beginNewCommand(int conn)
611 DirComm *dircomm = m_dircommHash.value(conn);
613 for (int i=0; i < 3; i++) {
615 while (dircomm->read() > 0) {
616 if (mainWin->m_commDebug) Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
617 if (mainWin->m_displayAll) display_text(dircomm->msg());
619 if (dircomm->m_at_main_prompt) {
626 void Console::displayToPrompt(int conn)
628 DirComm *dircomm = m_dircommHash.value(conn);
632 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
633 while (!dircomm->m_at_prompt) {
634 if ((stat=dircomm->read()) > 0) {
635 buf += dircomm->msg();
636 if (buf.size() >= 8196 || m_messages_pending) {
639 messagesPending(false);
644 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
647 void Console::discardToPrompt(int conn)
649 DirComm *dircomm = m_dircommHash.value(conn);
652 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
653 if (mainWin->m_displayAll) {
654 displayToPrompt(conn);
656 while (!dircomm->m_at_prompt) {
657 stat = dircomm->read();
663 if (mainWin->m_commDebug) {
664 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
668 QString Console::returnFromPrompt(int conn)
670 DirComm *dircomm = m_dircommHash.value(conn);
676 text += dircomm->msg();
677 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
678 while (!dircomm->m_at_prompt) {
679 if ((stat=dircomm->read()) > 0) {
680 text += dircomm->msg();
683 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
688 * When the notifier is enabled, read_dir() will automatically be
689 * called by the Qt event loop when ever there is any output
690 * from the Director, and read_dir() will then display it on
693 * When we are in a bat dialog, we want to control *all* output
694 * from the Director, so we set notify to off.
695 * m_console->notifiy(false);
698 /* dual purpose function to turn notify off and return a connection */
699 int Console::notifyOff()
702 if (getDirComm(conn)) {
708 /* knowing a connection, turn notify off or on */
709 bool Console::notify(int conn, bool enable)
711 DirComm *dircomm = m_dircommHash.value(conn);
713 return dircomm->notify(enable);
719 /* knowing a connection, return notify state */
720 bool Console::is_notify_enabled(int conn) const
722 DirComm *dircomm = m_dircommHash.value(conn);
724 return dircomm->is_notify_enabled();
730 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
732 m_directorTreeItem = item;
735 void Console::setDirRes(DIRRES *dir)
741 * To have the ability to get the name of the director resource.
743 void Console::getDirResName(QString &name_returned)
745 name_returned = m_dir->name();
748 /* Slot for responding to page selectors status help command */
749 void Console::consoleHelp()
755 /* Slot for responding to page selectors reload bacula-dir.conf */
756 void Console::consoleReload()
758 QString cmd("reload");
762 /* For suppressing .messages
763 * This may be rendered not needed if the multiple connections feature gets working */
764 bool Console::hasFocus()
766 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
772 /* For adding feature to have the gui's messages button change when
773 * messages are pending */
774 bool Console::messagesPending(bool pend)
776 bool prev = m_messages_pending;
777 m_messages_pending = pend;
778 mainWin->setMessageIcon();
782 /* terminate all existing connections */
783 void Console::terminate()
785 foreach(DirComm* dircomm, m_dircommHash) {
786 dircomm->terminate();
788 m_console->stopTimer();
791 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
792 bool Console::is_connectedGui()
794 if (is_connected(0)) {
797 QString message = tr("Director is currently disconnected\nPlease reconnect!");
798 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
803 int Console::read(int conn)
805 DirComm *dircomm = m_dircommHash.value(conn);
806 return dircomm->read();
809 char *Console::msg(int conn)
811 DirComm *dircomm = m_dircommHash.value(conn);
812 return dircomm->msg();
815 int Console::write(int conn, const QString msg)
817 DirComm *dircomm = m_dircommHash.value(conn);
818 mainWin->waitEnter();
819 int ret = dircomm->write(msg);
824 int Console::write(int conn, const char *msg)
826 DirComm *dircomm = m_dircommHash.value(conn);
827 mainWin->waitEnter();
828 int ret = dircomm->write(msg);
833 /* This checks to see if any is connected */
834 bool Console::is_connected()
836 bool connected = false;
837 foreach(DirComm* dircomm, m_dircommHash) {
838 if (dircomm->is_connected())
844 /* knowing the connection id, is it connected */
845 bool Console::is_connected(int conn)
847 DirComm *dircomm = m_dircommHash.value(conn);
848 return dircomm->is_connected();
852 * Need a connection. Check existing connections or create one
854 bool Console::getDirComm(int &conn)
856 if (findDirComm(conn)) {
859 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
860 return newDirComm(conn);
865 * Try to find a free (unused but established) connection
866 * KES: Note, I think there is a problem here because for
867 * some reason, the notifier is often turned off on file
868 * descriptors that seem to me to be available. That means
869 * that we do not use a free descriptor and thus we will create
870 * a new connection that is maybe not necessary. Someone needs
871 * to look into whether or not notify() is correctly turned on
872 * when we are back at the command prompt and idle.
875 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 dircomm->m_conn, 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;