2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many 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 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
22 * Written by Kern Sibbald, January MMVII
32 Console::Console(QTabWidget *parent) : Pages()
35 m_name = tr("Console");
36 m_messages_pending = false;
40 m_warningPrevent = false;
44 * Create a connection to the Director and put it in a hash table
46 m_dircommHash.insert(m_dircommCounter, new DirComm(this, m_dircommCounter));
49 m_textEdit = textEdit; /* our console screen */
50 m_cursor = new QTextCursor(m_textEdit->document());
51 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
54 m_contextActions.append(actionStatusDir);
55 m_contextActions.append(actionConsoleHelp);
56 m_contextActions.append(actionRequestMessages);
57 m_contextActions.append(actionConsoleReload);
58 connect(actionStatusDir, SIGNAL(triggered()), this, SLOT(status_dir()));
59 connect(actionConsoleHelp, SIGNAL(triggered()), this, SLOT(consoleHelp()));
60 connect(actionConsoleReload, SIGNAL(triggered()), this, SLOT(consoleReload()));
61 connect(actionRequestMessages, SIGNAL(triggered()), this, SLOT(messages()));
68 void Console::startTimer()
70 m_timer = new QTimer(this);
71 QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
72 m_timer->start(mainWin->m_checkMessagesInterval*30000);
75 void Console::stopTimer()
78 QWidget::disconnect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
85 /* slot connected to the timer
86 * requires preferences of check messages and operates at interval */
87 void Console::poll_messages()
91 /* Do not poll if notifier off */
92 if (!mainWin->m_notify) {
97 * Note if we call getDirComm here, we continuously consume
100 if (!findDirComm(conn)) { /* find a free DirComm */
101 return; /* try later */
104 DirComm *dircomm = m_dircommHash.value(conn);
105 if (mainWin->m_checkMessages && dircomm->m_at_main_prompt && hasFocus() && !mainWin->getWaitState()){
106 dircomm->write(".messages");
107 displayToPrompt(conn);
108 messagesPending(false);
113 * Connect to Director. This does not connect to the director, dircomm does.
114 * This creates the first and possibly 2nd dircomm instance
116 void Console::connect_dir()
118 DirComm *dircomm = m_dircommHash.value(0);
120 if (!m_console->m_dir) {
121 mainWin->set_status( tr("No Director found."));
125 m_textEdit = textEdit; /* our console screen */
127 if (dircomm->connect_dir()) {
128 if (mainWin->m_connDebug) {
129 Pmsg1(000, "DirComm 0 Seems to have Connected %s\n", m_dir->name());
133 mainWin->set_status(_("Connected"));
135 startTimer(); /* start message timer */
139 * A function created to separate out the population of the lists
140 * from the Console::connect_dir function
142 void Console::populateLists(bool /*forcenew*/)
145 if (!getDirComm(conn)) {
146 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
147 if (!newDirComm(conn)) {
148 Emsg1(M_INFO, 0, "Failed to connect to %s for populateLists.\n", m_dir->name());
156 void Console::populateLists(int conn)
159 restore_list.clear();
161 fileset_list.clear();
162 messages_list.clear();
164 storage_list.clear();
167 volstatus_list.clear();
168 mediatype_list.clear();
169 dir_cmd(conn, ".jobs", job_list);
170 dir_cmd(conn, ".jobs type=R", restore_list);
171 dir_cmd(conn, ".clients", client_list);
172 dir_cmd(conn, ".filesets", fileset_list);
173 dir_cmd(conn, ".msgs", messages_list);
174 dir_cmd(conn, ".pools", pool_list);
175 dir_cmd(conn, ".storage", storage_list);
176 dir_cmd(conn, ".types", type_list);
177 dir_cmd(conn, ".levels", level_list);
178 dir_cmd(conn, ".volstatus", volstatus_list);
179 dir_cmd(conn, ".mediatypes", mediatype_list);
180 dir_cmd(conn, ".locations", location_list);
182 if (mainWin->m_connDebug) {
183 QString dbgmsg = QString("jobs=%1 clients=%2 filesets=%3 msgs=%4 pools=%5 storage=%6 types=%7 levels=%8 conn=%9 %10\n")
184 .arg(job_list.count()).arg(client_list.count()).arg(fileset_list.count()).arg(messages_list.count())
185 .arg(pool_list.count()).arg(storage_list.count()).arg(type_list.count()).arg(level_list.count())
186 .arg(conn).arg(m_dir->name());
187 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
192 messages_list.sort();
200 * Overload function for dir_cmd with a QString
203 bool Console::dir_cmd(QString &cmd, QStringList &results)
205 return dir_cmd(cmd.toUtf8().data(), results);
209 * Overload function for dir_cmd, this is if connection is not worried about
211 bool Console::dir_cmd(const char *cmd, QStringList &results)
214 if (getDirComm(conn)) {
215 dir_cmd(conn, cmd, results);
218 Pmsg1(000, "dir_cmd failed to connect to %s\n", m_dir->name());
224 * Send a command to the Director, and return the
225 * results in a QStringList.
227 bool Console::dir_cmd(int conn, const char *cmd, QStringList &results)
229 mainWin->waitEnter();
230 DirComm *dircomm = m_dircommHash.value(conn);
232 bool prev_notify = is_notify_enabled(conn);
234 if (mainWin->m_connDebug) {
235 QString dbgmsg = QString("dir_cmd conn %1 %2 %3\n").arg(conn).arg(m_dir->name()).arg(cmd);
236 Pmsg1(000, "%s", dbgmsg.toUtf8().data());
242 while ((stat = dircomm->read()) > 0 && dircomm->is_in_command()) {
243 if (mainWin->m_displayAll) display_text(dircomm->msg());
244 strip_trailing_junk(dircomm->msg());
245 results << dircomm->msg();
247 if (stat > 0 && mainWin->m_displayAll) display_text(dircomm->msg());
249 notify(conn, true); /* turn it back on */
251 discardToPrompt(conn);
253 return true; /* ***FIXME*** return any command error */
257 * OverLoads for sql_cmd
259 bool Console::sql_cmd(int &conn, QString &query, QStringList &results)
261 return sql_cmd(conn, query.toUtf8().data(), results, false);
264 bool Console::sql_cmd(QString &query, QStringList &results)
267 if (!getDirComm(conn)) {
270 return sql_cmd(conn, query.toUtf8().data(), results, true);
273 bool Console::sql_cmd(const char *query, QStringList &results)
276 if (!getDirComm(conn)) {
279 return sql_cmd(conn, query, results, true);
283 * Send an sql query to the Director, and return the
284 * results in a QStringList.
286 bool Console::sql_cmd(int &conn, const char *query, QStringList &results, bool donotify)
288 DirComm *dircomm = m_dircommHash.value(conn);
290 POOL_MEM cmd(PM_MESSAGE);
291 bool prev_notify = is_notify_enabled(conn);
293 if (!is_connectedGui()) {
297 if (mainWin->m_connDebug) Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
299 dircomm->notify(false);
301 mainWin->waitEnter();
303 pm_strcpy(cmd, ".sql query=\"");
304 pm_strcat(cmd, query);
305 pm_strcat(cmd, "\"");
306 dircomm->write(cmd.c_str());
307 while ((stat = dircomm->read()) > 0) {
309 if (mainWin->m_displayAll) {
310 display_text(dircomm->msg());
313 strip_trailing_junk(dircomm->msg());
314 bool doappend = true;
316 QString dum = dircomm->msg();
317 if ((dum.left(6) == "*None*")) doappend = false;
320 results << dircomm->msg();
324 if (donotify && prev_notify) {
325 dircomm->notify(true);
327 discardToPrompt(conn);
329 if (donotify && prev_notify) {
330 dircomm->notify(true);
332 return !mainWin->isClosing(); /* return false if closing */
337 * Sending a command to the Director
339 int Console::write_dir(const char *msg)
342 if (getDirComm(conn)) {
343 write_dir(conn, msg);
348 int Console::write_dir(const char *msg, bool dowait)
351 if (getDirComm(conn)) {
352 write_dir(conn, msg, dowait);
357 void Console::write_dir(int conn, const char *msg)
359 write_dir(conn, msg, true);
363 * Send a command to the Director
365 void Console::write_dir(int conn, const char *msg, bool dowait)
367 DirComm *dircomm = m_dircommHash.value(conn);
369 if (dircomm->m_sock) {
370 mainWin->set_status(_("Processing command ..."));
372 mainWin->waitEnter();
377 mainWin->set_status( tr(" Director not connected. Click on connect button."));
378 mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
379 QBrush redBrush(Qt::red);
380 QTreeWidgetItem *item = mainWin->getFromHash(this);
381 item->setForeground(0, redBrush);
382 dircomm->m_at_prompt = false;
383 dircomm->m_at_main_prompt = false;
388 * get_job_defaults overload
390 bool Console::get_job_defaults(struct job_defaults &job_defs)
394 return get_job_defaults(conn, job_defs, true);
397 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs)
399 return get_job_defaults(conn, job_defs, true);
403 * Send a job name to the director, and read all the resulting
406 bool Console::get_job_defaults(int &conn, struct job_defaults &job_defs, bool donotify)
411 bool prev_notify = is_notify_enabled(conn);
413 DirComm *dircomm = m_dircommHash.value(conn);
416 dircomm->notify(false);
418 beginNewCommand(conn);
419 bool prevWaitState = mainWin->getWaitState();
420 if (!prevWaitState) {
421 mainWin->waitEnter();
423 if (mainWin->m_connDebug) {
424 Pmsg2(000, "job_defaults conn %i %s\n", conn, m_dir->name());
426 scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
427 dircomm->write(scmd);
428 while ((stat = dircomm->read()) > 0) {
429 if (mainWin->m_displayAll) display_text(dircomm->msg());
430 def = strchr(dircomm->msg(), '=');
434 /* Pointer to default value */
436 strip_trailing_junk(def);
438 if (strcmp(dircomm->msg(), "job") == 0) {
439 if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
444 if (strcmp(dircomm->msg(), "pool") == 0) {
445 job_defs.pool_name = def;
448 if (strcmp(dircomm->msg(), "messages") == 0) {
449 job_defs.messages_name = def;
452 if (strcmp(dircomm->msg(), "client") == 0) {
453 job_defs.client_name = def;
456 if (strcmp(dircomm->msg(), "storage") == 0) {
457 job_defs.store_name = def;
460 if (strcmp(dircomm->msg(), "where") == 0) {
461 job_defs.where = def;
464 if (strcmp(dircomm->msg(), "level") == 0) {
465 job_defs.level = def;
468 if (strcmp(dircomm->msg(), "type") == 0) {
472 if (strcmp(dircomm->msg(), "fileset") == 0) {
473 job_defs.fileset_name = def;
476 if (strcmp(dircomm->msg(), "catalog") == 0) {
477 job_defs.catalog_name = def;
480 if (strcmp(dircomm->msg(), "enabled") == 0) {
481 job_defs.enabled = *def == '1' ? true : false;
486 /* Fall through wanted */
488 if (donotify && prev_notify) {
491 if (!prevWaitState) {
499 * Save user settings associated with this console
501 void Console::writeSettings()
503 QFont font = get_font();
505 QSettings settings(m_dir->name(), "bat");
506 settings.beginGroup("Console");
507 settings.setValue("consoleFont", font.family());
508 settings.setValue("consolePointSize", font.pointSize());
509 settings.setValue("consoleFixedPitch", font.fixedPitch());
514 * Read and restore user settings associated with this console
516 void Console::readSettings()
518 QFont font = get_font();
520 QSettings settings(m_dir->name(), "bat");
521 settings.beginGroup("Console");
522 font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
523 font.setPointSize(settings.value("consolePointSize", 10).toInt());
524 font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
526 m_textEdit->setFont(font);
530 * Set the console textEdit font
532 void Console::set_font()
535 QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
537 m_textEdit->setFont(font);
542 * Get the console text edit font
544 const QFont Console::get_font()
546 return m_textEdit->font();
550 * Slot for responding to status dir button on button bar
552 void Console::status_dir()
554 QString cmd("status dir");
559 * Slot for responding to messages button on button bar
560 * Here we want to bring the console to the front so use pages' consoleCommand
562 void Console::messages()
564 QString cmd(".messages");
566 messagesPending(false);
570 * Put text into the console window
572 void Console::display_textf(const char *fmt, ...)
576 va_start(arg_ptr, fmt);
577 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
582 void Console::display_text(const QString buf)
584 if (mainWin->isClosing()) return;
585 if (buf.size() != 0) {
586 m_cursor->insertText(buf);
592 void Console::display_text(const char *buf)
594 if (mainWin->isClosing()) return;
596 m_cursor->insertText(buf);
601 void Console::display_html(const QString buf)
603 if (mainWin->isClosing()) return;
604 if (buf.size() != 0) {
605 m_cursor->insertHtml(buf);
610 /* Position cursor to end of screen */
611 void Console::update_cursor()
613 m_textEdit->moveCursor(QTextCursor::End);
614 m_textEdit->ensureCursorVisible();
617 void Console::beginNewCommand(int conn)
619 DirComm *dircomm = m_dircommHash.value(conn);
621 if (dircomm->m_at_main_prompt) {
624 for (int i=0; i < 3; i++) {
626 while (dircomm->read() > 0) {
627 if (mainWin->m_commDebug) Pmsg2(000, "begin new command loop %i %s\n", i, m_dir->name());
628 if (mainWin->m_displayAll) display_text(dircomm->msg());
630 if (dircomm->m_at_main_prompt) {
634 //display_text("\n");
637 void Console::displayToPrompt(int conn)
639 DirComm *dircomm = m_dircommHash.value(conn);
643 if (mainWin->m_commDebug) Pmsg1(000, "DisplaytoPrompt %s\n", m_dir->name());
644 while (!dircomm->m_at_prompt) {
645 if ((stat=dircomm->read()) > 0) {
646 buf += dircomm->msg();
647 if (buf.size() >= 8196 || m_messages_pending) {
650 messagesPending(false);
655 if (mainWin->m_commDebug) Pmsg2(000, "endDisplaytoPrompt=%d %s\n", stat, m_dir->name());
658 void Console::discardToPrompt(int conn)
660 DirComm *dircomm = m_dircommHash.value(conn);
663 if (mainWin->m_commDebug) Pmsg1(000, "discardToPrompt %s\n", m_dir->name());
664 if (mainWin->m_displayAll) {
665 displayToPrompt(conn);
667 while (!dircomm->m_at_prompt) {
668 stat = dircomm->read();
674 if (mainWin->m_commDebug) {
675 Pmsg2(000, "endDiscardToPrompt conn=%i %s\n", conn, m_dir->name());
679 QString Console::returnFromPrompt(int conn)
681 DirComm *dircomm = m_dircommHash.value(conn);
687 text += dircomm->msg();
688 if (mainWin->m_commDebug) Pmsg1(000, "returnFromPrompt %s\n", m_dir->name());
689 while (!dircomm->m_at_prompt) {
690 if ((stat=dircomm->read()) > 0) {
691 text += dircomm->msg();
694 if (mainWin->m_commDebug) Pmsg2(000, "endreturnFromPrompt=%d %s\n", stat, m_dir->name());
699 * When the notifier is enabled, read_dir() will automatically be
700 * called by the Qt event loop when ever there is any output
701 * from the Director, and read_dir() will then display it on
704 * When we are in a bat dialog, we want to control *all* output
705 * from the Director, so we set notify to off.
706 * m_console->notifiy(false);
709 /* dual purpose function to turn notify off and return a connection */
710 int Console::notifyOff()
713 if (getDirComm(conn)) {
719 /* knowing a connection, turn notify off or on */
720 bool Console::notify(int conn, bool enable)
722 DirComm *dircomm = m_dircommHash.value(conn);
724 return dircomm->notify(enable);
730 /* knowing a connection, return notify state */
731 bool Console::is_notify_enabled(int conn) const
733 DirComm *dircomm = m_dircommHash.value(conn);
735 return dircomm->is_notify_enabled();
741 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
743 m_directorTreeItem = item;
746 void Console::setDirRes(DIRRES *dir)
752 * To have the ability to get the name of the director resource.
754 void Console::getDirResName(QString &name_returned)
756 name_returned = m_dir->name();
759 /* Slot for responding to page selectors status help command */
760 void Console::consoleHelp()
766 /* Slot for responding to page selectors reload bacula-dir.conf */
767 void Console::consoleReload()
769 QString cmd("reload");
773 /* For suppressing .messages
774 * This may be rendered not needed if the multiple connections feature gets working */
775 bool Console::hasFocus()
777 if (mainWin->tabWidget->currentIndex() == mainWin->tabWidget->indexOf(this))
783 /* For adding feature to have the gui's messages button change when
784 * messages are pending */
785 bool Console::messagesPending(bool pend)
787 bool prev = m_messages_pending;
788 m_messages_pending = pend;
789 mainWin->setMessageIcon();
793 /* terminate all existing connections */
794 void Console::terminate()
796 foreach(DirComm* dircomm, m_dircommHash) {
797 dircomm->terminate();
799 m_console->stopTimer();
802 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
803 bool Console::is_connectedGui()
805 if (is_connected(0)) {
808 QString message = tr("Director is currently disconnected\nPlease reconnect!");
809 QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
814 int Console::read(int conn)
816 DirComm *dircomm = m_dircommHash.value(conn);
817 return dircomm->read();
820 char *Console::msg(int conn)
822 DirComm *dircomm = m_dircommHash.value(conn);
823 return dircomm->msg();
826 int Console::write(int conn, const QString msg)
828 DirComm *dircomm = m_dircommHash.value(conn);
829 mainWin->waitEnter();
830 int ret = dircomm->write(msg);
835 int Console::write(int conn, const char *msg)
837 DirComm *dircomm = m_dircommHash.value(conn);
838 mainWin->waitEnter();
839 int ret = dircomm->write(msg);
844 /* This checks to see if any is connected */
845 bool Console::is_connected()
847 bool connected = false;
848 foreach(DirComm* dircomm, m_dircommHash) {
849 if (dircomm->is_connected())
855 /* knowing the connection id, is it connected */
856 bool Console::is_connected(int conn)
858 DirComm *dircomm = m_dircommHash.value(conn);
859 return dircomm->is_connected();
863 * Need a connection. Check existing connections or create one
865 bool Console::getDirComm(int &conn)
867 if (findDirComm(conn)) {
870 if (mainWin->m_connDebug) Pmsg0(000, "call newDirComm\n");
871 return newDirComm(conn);
876 * Try to find a free (unused but established) connection
877 * KES: Note, I think there is a problem here because for
878 * some reason, the notifier is often turned off on file
879 * descriptors that seem to me to be available. That means
880 * that we do not use a free descriptor and thus we will create
881 * a new connection that is maybe not necessary. Someone needs
882 * to look into whether or not notify() is correctly turned on
883 * when we are back at the command prompt and idle.
886 bool Console::findDirComm(int &conn)
888 QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
889 while (iter != m_dircommHash.constEnd()) {
890 DirComm *dircomm = iter.value();
891 if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
892 conn = dircomm->m_conn;
895 if (mainWin->m_connDebug) {
896 Pmsg4(000, "currentDirComm=%d at_prompt=%d at_main=%d && notify=%d\n",
897 dircomm->m_conn, dircomm->m_at_prompt, dircomm->m_at_main_prompt, dircomm->is_notify_enabled());
905 * Create a new connection
907 bool Console::newDirComm(int &conn)
910 if (mainWin->m_connDebug) {
911 Pmsg2(000, "newDirComm=%i to: %s\n", m_dircommCounter, m_dir->name());
913 DirComm *dircomm = new DirComm(this, m_dircommCounter);
914 m_dircommHash.insert(m_dircommCounter, dircomm);
915 bool success = dircomm->connect_dir();
916 if (mainWin->m_connDebug) {
918 Pmsg2(000, "newDirComm=%i Connected %s\n", m_dircommCounter, m_dir->name());
920 Emsg2(M_ERROR, 0, "DirComm=%i. Unable to connect to %s\n",
921 m_dircommCounter, m_dir->name());
925 m_dircommHash.remove(m_dircommCounter);
929 conn = m_dircommCounter;