2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2007-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
23 * Written by Kern Sibbald, January MMVII
33 Console::Console(QTabWidget *parent) : Pages()
36 m_name = tr("Console");
37 m_messages_pending = false;
41 m_warningPrevent = false;
45 * Create a connection to the Director and put it in a hash table
47 m_dircommHash.insert(m_dircommCounter, new DirComm(this, m_dircommCounter));
50 m_textEdit = textEdit; /* our console screen */
51 m_cursor = new QTextCursor(m_textEdit->document());
52 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
55 m_contextActions.append(actionStatusDir);
56 m_contextActions.append(actionConsoleHelp);
57 m_contextActions.append(actionRequestMessages);
58 m_contextActions.append(actionConsoleReload);
59 connect(actionStatusDir, SIGNAL(triggered()), this, SLOT(status_dir()));
60 connect(actionConsoleHelp, SIGNAL(triggered()), this, SLOT(consoleHelp()));
61 connect(actionConsoleReload, SIGNAL(triggered()), this, SLOT(consoleReload()));
62 connect(actionRequestMessages, SIGNAL(triggered()), this, SLOT(messages()));
69 void Console::startTimer()
71 m_timer = new QTimer(this);
72 QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
73 m_timer->start(mainWin->m_checkMessagesInterval*30000);
76 void Console::stopTimer()
79 QWidget::disconnect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
86 /* slot connected to the timer
87 * requires preferences of check messages and operates at interval */
88 void Console::poll_messages()
92 /* Do not poll if notifier off */
93 if (!mainWin->m_notify) {
98 * Note if we call getDirComm here, we continuously consume
101 if (!findDirComm(conn)) { /* find a free DirComm */
102 return; /* try later */
105 DirComm *dircomm = m_dircommHash.value(conn);
106 if (mainWin->m_checkMessages && dircomm->m_at_main_prompt && hasFocus() && !mainWin->getWaitState()){
107 dircomm->write(".messages");
108 displayToPrompt(conn);
109 messagesPending(false);
114 * Connect to Director. This does not connect to the director, dircomm does.
115 * This creates the first and possibly 2nd dircomm instance
117 void Console::connect_dir()
119 DirComm *dircomm = m_dircommHash.value(0);
121 if (!m_console->m_dir) {
122 mainWin->set_status( tr("No Director found."));
126 m_textEdit = textEdit; /* our console screen */
128 if (dircomm->connect_dir()) {
129 if (mainWin->m_connDebug) {
130 Pmsg1(000, "DirComm 0 Seems to have Connected %s\n", m_dir->name());
134 mainWin->set_status(_("Connected"));
136 startTimer(); /* start message timer */
140 * A function created to separate out the population of the lists
141 * from the Console::connect_dir function
143 void Console::populateLists(bool /*forcenew*/)
146 if (!getDirComm(conn)) {
147 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
148 if (!newDirComm(conn)) {
149 Emsg1(M_INFO, 0, "Failed to connect to %s for populateLists.\n", m_dir->name());
157 void Console::populateLists(int conn)
160 restore_list.clear();
162 fileset_list.clear();
163 messages_list.clear();
165 storage_list.clear();
168 volstatus_list.clear();
169 mediatype_list.clear();
170 dir_cmd(conn, ".jobs", job_list);
171 dir_cmd(conn, ".jobs type=R", restore_list);
172 dir_cmd(conn, ".clients", client_list);
173 dir_cmd(conn, ".filesets", fileset_list);
174 dir_cmd(conn, ".msgs", messages_list);
175 dir_cmd(conn, ".pools", pool_list);
176 dir_cmd(conn, ".storage", storage_list);
177 dir_cmd(conn, ".types", type_list);
178 dir_cmd(conn, ".levels", level_list);
179 dir_cmd(conn, ".volstatus", volstatus_list);
180 dir_cmd(conn, ".mediatypes", mediatype_list);
181 dir_cmd(conn, ".locations", location_list);
183 if (mainWin->m_connDebug) {
184 QString dbgmsg = QString("jobs=%1 clients=%2 filesets=%3 msgs=%4 pools=%5 storage=%6 types=%7 levels=%8 conn=%9 %10\n")
185 .arg(job_list.count()).arg(client_list.count()).arg(fileset_list.count()).arg(messages_list.count())
186 .arg(pool_list.count()).arg(storage_list.count()).arg(type_list.count()).arg(level_list.count())
187 .arg(conn).arg(m_dir->name());
188 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
193 messages_list.sort();
201 * Overload function for dir_cmd with a QString
204 bool Console::dir_cmd(QString &cmd, QStringList &results)
206 return dir_cmd(cmd.toUtf8().data(), results);
210 * Overload function for dir_cmd, this is if connection is not worried about
212 bool Console::dir_cmd(const char *cmd, QStringList &results)
215 if (getDirComm(conn)) {
216 dir_cmd(conn, cmd, results);
219 Pmsg1(000, "dir_cmd failed to connect to %s\n", m_dir->name());
225 * Send a command to the Director, and return the
226 * results in a QStringList.
228 bool Console::dir_cmd(int conn, const char *cmd, QStringList &results)
230 mainWin->waitEnter();
231 DirComm *dircomm = m_dircommHash.value(conn);
233 bool prev_notify = is_notify_enabled(conn);
235 if (mainWin->m_connDebug) {
236 QString dbgmsg = QString("dir_cmd conn %1 %2 %3\n").arg(conn).arg(m_dir->name()).arg(cmd);
237 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 notify(conn, true); /* turn it back on */
252 discardToPrompt(conn);
254 return true; /* ***FIXME*** return any command error */
258 * OverLoads for sql_cmd
260 bool Console::sql_cmd(int &conn, QString &query, QStringList &results)
262 return sql_cmd(conn, query.toUtf8().data(), results, false);
265 bool Console::sql_cmd(QString &query, QStringList &results)
268 if (!getDirComm(conn)) {
271 return sql_cmd(conn, query.toUtf8().data(), results, true);
274 bool Console::sql_cmd(const char *query, QStringList &results)
277 if (!getDirComm(conn)) {
280 return sql_cmd(conn, query, results, true);
284 * Send an sql query to the Director, and return the
285 * results in a QStringList.
287 bool Console::sql_cmd(int &conn, const char *query, QStringList &results, bool donotify)
289 DirComm *dircomm = m_dircommHash.value(conn);
291 POOL_MEM cmd(PM_MESSAGE);
292 bool prev_notify = is_notify_enabled(conn);
294 if (!is_connectedGui()) {
298 if (mainWin->m_connDebug) Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
300 dircomm->notify(false);
302 mainWin->waitEnter();
304 pm_strcpy(cmd, ".sql query=\"");
305 pm_strcat(cmd, query);
306 pm_strcat(cmd, "\"");
307 dircomm->write(cmd.c_str());
308 while ((stat = dircomm->read()) > 0) {
310 if (mainWin->m_displayAll) {
311 display_text(dircomm->msg());
314 strip_trailing_junk(dircomm->msg());
315 bool doappend = true;
317 QString dum = dircomm->msg();
318 if ((dum.left(6) == "*None*")) doappend = false;
321 results << dircomm->msg();
325 if (donotify && prev_notify) {
326 dircomm->notify(true);
328 discardToPrompt(conn);
330 if (donotify && prev_notify) {
331 dircomm->notify(true);
333 return !mainWin->isClosing(); /* return false if closing */
338 * Sending a command to the Director
340 int Console::write_dir(const char *msg)
343 if (getDirComm(conn)) {
344 write_dir(conn, msg);
349 int Console::write_dir(const char *msg, bool dowait)
352 if (getDirComm(conn)) {
353 write_dir(conn, msg, dowait);
358 void Console::write_dir(int conn, const char *msg)
360 write_dir(conn, msg, true);
364 * Send a command to the Director
366 void Console::write_dir(int conn, const char *msg, bool dowait)
368 DirComm *dircomm = m_dircommHash.value(conn);
370 if (dircomm->m_sock) {
371 mainWin->set_status(_("Processing command ..."));
373 mainWin->waitEnter();
378 mainWin->set_status( tr(" Director not connected. Click on connect button."));
379 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
380 QBrush redBrush(Qt::red);
381 QTreeWidgetItem *item = mainWin->getFromHash(this);
382 item->setForeground(0, redBrush);
383 dircomm->m_at_prompt = false;
384 dircomm->m_at_main_prompt = false;
389 * get_job_defaults overload
391 bool Console::get_job_defaults(struct job_defaults &job_defs)
395 return get_job_defaults(conn, job_defs, true);
398 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs)
400 return get_job_defaults(conn, job_defs, true);
404 * Send a job name to the director, and read all the resulting
407 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs, bool donotify)
412 bool prev_notify = is_notify_enabled(conn);
414 DirComm *dircomm = m_dircommHash.value(conn);
417 dircomm->notify(false);
419 beginNewCommand(conn);
420 bool prevWaitState = mainWin->getWaitState();
421 if (!prevWaitState) {
422 mainWin->waitEnter();
424 if (mainWin->m_connDebug) {
425 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, ...)
577 va_start(arg_ptr, fmt);
578 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
583 void Console::display_text(const QString buf)
585 if (mainWin->isClosing()) return;
586 if (buf.size() != 0) {
587 m_cursor->insertText(buf);
593 void Console::display_text(const char *buf)
595 if (mainWin->isClosing()) return;
597 m_cursor->insertText(buf);
602 void Console::display_html(const QString buf)
604 if (mainWin->isClosing()) return;
605 if (buf.size() != 0) {
606 m_cursor->insertHtml(buf);
611 /* Position cursor to end of screen */
612 void Console::update_cursor()
614 m_textEdit->moveCursor(QTextCursor::End);
615 m_textEdit->ensureCursorVisible();
618 void Console::beginNewCommand(int conn)
620 DirComm *dircomm = m_dircommHash.value(conn);
622 if (dircomm->m_at_main_prompt) {
625 for (int i=0; i < 3; i++) {
627 while (dircomm->read() > 0) {
628 if (mainWin->m_commDebug) Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
629 if (mainWin->m_displayAll) display_text(dircomm->msg());
631 if (dircomm->m_at_main_prompt) {
635 //display_text("\n");
638 void Console::displayToPrompt(int conn)
640 DirComm *dircomm = m_dircommHash.value(conn);
644 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
645 while (!dircomm->m_at_prompt) {
646 if ((stat=dircomm->read()) > 0) {
647 buf += dircomm->msg();
648 if (buf.size() >= 8196 || m_messages_pending) {
651 messagesPending(false);
656 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
659 void Console::discardToPrompt(int conn)
661 DirComm *dircomm = m_dircommHash.value(conn);
664 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
665 if (mainWin->m_displayAll) {
666 displayToPrompt(conn);
668 while (!dircomm->m_at_prompt) {
669 stat = dircomm->read();
675 if (mainWin->m_commDebug) {
676 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
680 QString Console::returnFromPrompt(int conn)
682 DirComm *dircomm = m_dircommHash.value(conn);
688 text += dircomm->msg();
689 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
690 while (!dircomm->m_at_prompt) {
691 if ((stat=dircomm->read()) > 0) {
692 text += dircomm->msg();
695 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
700 * When the notifier is enabled, read_dir() will automatically be
701 * called by the Qt event loop when ever there is any output
702 * from the Director, and read_dir() will then display it on
705 * When we are in a bat dialog, we want to control *all* output
706 * from the Director, so we set notify to off.
707 * m_console->notifiy(false);
710 /* dual purpose function to turn notify off and return a connection */
711 int Console::notifyOff()
714 if (getDirComm(conn)) {
720 /* knowing a connection, turn notify off or on */
721 bool Console::notify(int conn, bool enable)
723 DirComm *dircomm = m_dircommHash.value(conn);
725 return dircomm->notify(enable);
731 /* knowing a connection, return notify state */
732 bool Console::is_notify_enabled(int conn) const
734 DirComm *dircomm = m_dircommHash.value(conn);
736 return dircomm->is_notify_enabled();
742 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
744 m_directorTreeItem = item;
747 void Console::setDirRes(DIRRES *dir)
753 * To have the ability to get the name of the director resource.
755 void Console::getDirResName(QString &name_returned)
757 name_returned = m_dir->name();
760 /* Slot for responding to page selectors status help command */
761 void Console::consoleHelp()
767 /* Slot for responding to page selectors reload bacula-dir.conf */
768 void Console::consoleReload()
770 QString cmd("reload");
774 /* For suppressing .messages
775 * This may be rendered not needed if the multiple connections feature gets working */
776 bool Console::hasFocus()
778 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
784 /* For adding feature to have the gui's messages button change when
785 * messages are pending */
786 bool Console::messagesPending(bool pend)
788 bool prev = m_messages_pending;
789 m_messages_pending = pend;
790 mainWin->setMessageIcon();
794 /* terminate all existing connections */
795 void Console::terminate()
797 foreach(DirComm* dircomm, m_dircommHash) {
798 dircomm->terminate();
800 m_console->stopTimer();
803 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
804 bool Console::is_connectedGui()
806 if (is_connected(0)) {
809 QString message = tr("Director is currently disconnected\nPlease reconnect!");
810 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
815 int Console::read(int conn)
817 DirComm *dircomm = m_dircommHash.value(conn);
818 return dircomm->read();
821 char *Console::msg(int conn)
823 DirComm *dircomm = m_dircommHash.value(conn);
824 return dircomm->msg();
827 int Console::write(int conn, const QString msg)
829 DirComm *dircomm = m_dircommHash.value(conn);
830 mainWin->waitEnter();
831 int ret = dircomm->write(msg);
836 int Console::write(int conn, const char *msg)
838 DirComm *dircomm = m_dircommHash.value(conn);
839 mainWin->waitEnter();
840 int ret = dircomm->write(msg);
845 /* This checks to see if any is connected */
846 bool Console::is_connected()
848 bool connected = false;
849 foreach(DirComm* dircomm, m_dircommHash) {
850 if (dircomm->is_connected())
856 /* knowing the connection id, is it connected */
857 bool Console::is_connected(int conn)
859 DirComm *dircomm = m_dircommHash.value(conn);
860 return dircomm->is_connected();
864 * Need a connection. Check existing connections or create one
866 bool Console::getDirComm(int &conn)
868 if (findDirComm(conn)) {
871 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
872 return newDirComm(conn);
877 * Try to find a free (unused but established) connection
878 * KES: Note, I think there is a problem here because for
879 * some reason, the notifier is often turned off on file
880 * descriptors that seem to me to be available. That means
881 * that we do not use a free descriptor and thus we will create
882 * a new connection that is maybe not necessary. Someone needs
883 * to look into whether or not notify() is correctly turned on
884 * when we are back at the command prompt and idle.
887 bool Console::findDirComm(int &conn)
889 QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
890 while (iter != m_dircommHash.constEnd()) {
891 DirComm *dircomm = iter.value();
892 if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
893 conn = dircomm->m_conn;
896 if (mainWin->m_connDebug) {
897 Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
898 dircomm->m_conn, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
906 * Create a new connection
908 bool Console::newDirComm(int &conn)
911 if (mainWin->m_connDebug) {
912 Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
914 DirComm *dircomm = new DirComm(this, m_dircommCounter);
915 m_dircommHash.insert(m_dircommCounter, dircomm);
916 bool success = dircomm->connect_dir();
917 if (mainWin->m_connDebug) {
919 Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
921 Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
922 m_dircommCounter, m_dir->name());
926 m_dircommHash.remove(m_dircommCounter);
930 conn = m_dircommCounter;