]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/console/console.cpp
I Think this may solve the issue with the messages causing errors with the
[bacula/bacula] / bacula / src / qt-console / console / console.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
5
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 two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
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.
17
18    You should have received a copy of the GNU 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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  *   Version $Id$
30  *
31  *  Console Class
32  *
33  *   Kern Sibbald, January MMVII
34  *
35  */ 
36
37 #include "bat.h"
38 #include "console.h"
39 #include "restore.h"
40 #include "select.h"
41 #include "run/run.h"
42
43 Console::Console(QStackedWidget *parent)
44 {
45    QFont font;
46    m_messages_pending = false;
47    m_parent = parent;
48    m_closeable = false;
49    m_console = this;
50    m_dircommCounter = 0;
51    m_dircommHash.insert(m_dircommCounter, new DirComm(this, m_dircommCounter));
52
53    setupUi(this);
54    m_textEdit = textEdit;   /* our console screen */
55    m_cursor = new QTextCursor(m_textEdit->document());
56    mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
57
58    m_timer = NULL;
59    m_contextActions.append(actionStatusDir);
60    m_contextActions.append(actionConsoleHelp);
61    m_contextActions.append(actionRequestMessages);
62    m_contextActions.append(actionConsoleReload);
63    connect(actionStatusDir, SIGNAL(triggered()), this, SLOT(status_dir()));
64    connect(actionConsoleHelp, SIGNAL(triggered()), this, SLOT(consoleHelp()));
65    connect(actionConsoleReload, SIGNAL(triggered()), this, SLOT(consoleReload()));
66    connect(actionRequestMessages, SIGNAL(triggered()), this, SLOT(messages()));
67 }
68
69 Console::~Console()
70 {
71 }
72
73 void Console::startTimer()
74 {
75    m_timer = new QTimer(this);
76    QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
77    m_timer->start(mainWin->m_checkMessagesInterval*1000);
78 }
79
80 void Console::stopTimer()
81 {
82    if (m_timer) {
83       QWidget::disconnect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
84       m_timer->stop();
85       delete m_timer;
86       m_timer = NULL;
87    }
88 }
89
90 /* slot connected to the timer
91  * requires preferences of check messages and operates at interval */
92 void Console::poll_messages()
93 {
94    int conn;
95    if (!availableDirComm(conn))
96       return;
97    DirComm *dircomm = m_dircommHash.value(conn);
98
99    if (mainWin->m_checkMessages && dircomm->m_at_main_prompt && hasFocus()){
100       messagesPending(true);
101       dircomm->write(".messages");
102       displayToPrompt(conn);
103       messagesPending(false);
104    }
105 }
106
107 /*
108  * Connect to Director. 
109  */
110 void Console::connect_dir()
111 {
112    DirComm *dircomm = m_dircommHash.value(0);
113
114    if (!m_console->m_dir) {
115       mainWin->set_status( tr("No Director found."));
116       return;
117    }
118
119    m_textEdit = textEdit;   /* our console screen */
120
121    if (dircomm->connect_dir()) {
122       if (mainWin->m_connDebug)
123          Pmsg0(000, "DirComm 0 Seems to have Connected\n");
124       beginNewCommand(0);
125    }
126    
127    int ndc;
128    if (newDirComm(ndc)) {
129       if (mainWin->m_connDebug)
130          Pmsg1(000, "DirComm %i Seems to have Connected\n", ndc);
131       dircomm = m_dircommHash.value(ndc);
132       job_list.clear();
133       client_list.clear();
134       fileset_list.clear();
135       fileset_list.clear();
136       messages_list.clear();
137       pool_list.clear();
138       storage_list.clear();
139       type_list.clear();
140       level_list.clear();
141       dir_cmd(ndc, ".jobs", job_list);
142       dir_cmd(ndc, ".clients", client_list);
143       dir_cmd(ndc, ".filesets", fileset_list);  
144       dir_cmd(ndc, ".msgs", messages_list);
145       dir_cmd(ndc, ".pools", pool_list);
146       dir_cmd(ndc, ".storage", storage_list);
147       dir_cmd(ndc, ".types", type_list);
148       dir_cmd(ndc, ".levels", level_list);
149
150       if (mainWin->m_connDebug) {
151          QString dbgmsg = QString("jobs=%1 clients=%2 filesets=%3 msgs=%4 pools=%5 storage=%6 types=%7 levels=%8\n")
152            .arg(job_list.count()).arg(client_list.count()).arg(fileset_list.count()).arg(messages_list.count())
153            .arg(pool_list.count()).arg(storage_list.count()).arg(type_list.count()).arg(level_list.count());
154          Pmsg1(000, "%s\n", dbgmsg.toUtf8().data());
155       } else
156          if (mainWin->m_connDebug)
157             Pmsg0(000, "DirComm 1 Seems to Failed\n");
158    
159       mainWin->set_status(_("Connected"));
160       startTimer();                      /* start message timer */
161    }
162 }
163
164 /*
165  *  Overload function for dir_cmd with a QString
166  *  Ease of use
167  */
168 bool Console::dir_cmd(QString &cmd, QStringList &results)
169 {
170    return dir_cmd(cmd.toUtf8().data(), results);
171 }
172
173 /*
174  *  Overload function for dir_cmd, this is if connection is not worried about
175  */
176 bool Console::dir_cmd(const char *cmd, QStringList &results)
177 {
178    int conn;
179    if(availableDirComm(conn)) {
180       dir_cmd(conn, cmd, results);
181       return true;
182    } else {
183       Pmsg0(000, "dir_cmd Seems to Failed to find a connection\n");
184       return false;
185    }
186 }
187
188 /*
189  * Send a command to the Director, and return the
190  *  results in a QStringList.  
191  */
192 bool Console::dir_cmd(int conn, const char *cmd, QStringList &results)
193 {
194    DirComm *dircomm = m_dircommHash.value(conn);
195    int stat;
196
197    if (mainWin->m_connDebug)
198       Pmsg2(000, "dir_cmd conn %i %s\n", conn, cmd);
199    notify(conn, false);
200    dircomm->write(cmd);
201    while ((stat = dircomm->read()) > 0 && dircomm->is_in_command()) {
202       if (mainWin->m_displayAll) display_text(dircomm->msg());
203       strip_trailing_junk(dircomm->msg());
204       results << dircomm->msg();
205    }
206    if (stat > 0 && mainWin->m_displayAll) display_text(dircomm->msg());
207    notify(conn, true);
208    discardToPrompt(conn);
209    return true;              /* ***FIXME*** return any command error */
210 }
211
212 bool Console::sql_cmd(QString &query, QStringList &results)
213 {
214    return sql_cmd(query.toUtf8().data(), results);
215 }
216
217 /*
218  * Send an sql query to the Director, and return the
219  *  results in a QStringList.  
220  */
221 bool Console::sql_cmd(const char *query, QStringList &results)
222 {
223    int conn;
224    if (!availableDirComm(conn))
225       return false;
226
227    DirComm *dircomm = m_dircommHash.value(conn);
228    int stat;
229    POOL_MEM cmd(PM_MESSAGE);
230
231    if (!is_connectedGui()) {
232       return false;
233    }
234
235    if (mainWin->m_connDebug)
236       Pmsg2(000, "sql_cmd conn %i %s\n", conn, query);
237    notify(conn, false);
238    
239    pm_strcpy(cmd, ".sql query=\"");
240    pm_strcat(cmd, query);
241    pm_strcat(cmd, "\"");
242    dircomm->write(cmd.c_str());
243    while ((stat = dircomm->read()) > 0) {
244       bool first = true;
245       if (mainWin->m_displayAll) {
246          display_text(dircomm->msg());
247          display_text("\n");
248       }
249       strip_trailing_junk(dircomm->msg());
250       bool doappend = true;
251       if (first) {
252          QString dum = dircomm->msg();
253          if ((dum.left(6) == "*None*")) doappend = false;
254       }
255       if (doappend)
256          results << dircomm->msg();
257       first = false;
258    }
259    notify(conn, true);
260    discardToPrompt(conn);
261    return true;              /* ***FIXME*** return any command error */
262 }
263
264 /* Send a command to the Director */
265 int Console::write_dir(const char *msg)
266 {
267    int conn;
268    if(availableDirComm(conn))
269       write_dir(conn, msg);
270    return conn;
271 }
272
273 /* Send a command to the Director */
274 void Console::write_dir(int conn, const char *msg)
275 {
276    DirComm *dircomm = m_dircommHash.value(conn);
277
278    if (dircomm->m_sock) {
279       mainWin->set_status(_("Processing command ..."));
280       QApplication::setOverrideCursor(Qt::WaitCursor);
281       dircomm->write(msg);
282    } else {
283       mainWin->set_status( tr(" Director not connected. Click on connect button."));
284       mainWin->actionConnect->setIcon(QIcon(":images/disconnected.png"));
285       QBrush redBrush(Qt::red);
286       QTreeWidgetItem *item = mainWin->getFromHash(this);
287       item->setForeground(0, redBrush);
288       dircomm->m_at_prompt = false;
289       dircomm->m_at_main_prompt = false;
290    }
291 }
292
293 /*  
294  * Send a job name to the director, and read all the resulting
295  *  defaults. 
296  */
297 bool Console::get_job_defaults(struct job_defaults &job_defs)
298 {
299    QString scmd;
300    int stat;
301    char *def;
302
303    int conn = notifyOff();
304    beginNewCommand(conn);
305    DirComm *dircomm = m_dircommHash.value(conn);
306    if (mainWin->m_connDebug)
307       Pmsg1(000, "job_defaults conn %i\n", conn);
308    scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
309    dircomm->write(scmd);
310    while ((stat = dircomm->read()) > 0) {
311       if (mainWin->m_displayAll) display_text(dircomm->msg());
312       def = strchr(dircomm->msg(), '=');
313       if (!def) {
314          continue;
315       }
316       /* Pointer to default value */
317       *def++ = 0;
318       strip_trailing_junk(def);
319
320       if (strcmp(dircomm->msg(), "job") == 0) {
321          if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
322             goto bail_out;
323          }
324          continue;
325       }
326       if (strcmp(dircomm->msg(), "pool") == 0) {
327          job_defs.pool_name = def;
328          continue;
329       }
330       if (strcmp(dircomm->msg(), "messages") == 0) {
331          job_defs.messages_name = def;
332          continue;
333       }
334       if (strcmp(dircomm->msg(), "client") == 0) {
335          job_defs.client_name = def;
336          continue;
337       }
338       if (strcmp(dircomm->msg(), "storage") == 0) {
339          job_defs.store_name = def;
340          continue;
341       }
342       if (strcmp(dircomm->msg(), "where") == 0) {
343          job_defs.where = def;
344          continue;
345       }
346       if (strcmp(dircomm->msg(), "level") == 0) {
347          job_defs.level = def;
348          continue;
349       }
350       if (strcmp(dircomm->msg(), "type") == 0) {
351          job_defs.type = def;
352          continue;
353       }
354       if (strcmp(dircomm->msg(), "fileset") == 0) {
355          job_defs.fileset_name = def;
356          continue;
357       }
358       if (strcmp(dircomm->msg(), "catalog") == 0) {
359          job_defs.catalog_name = def;
360          continue;
361       }
362       if (strcmp(dircomm->msg(), "enabled") == 0) {
363          job_defs.enabled = *def == '1' ? true : false;
364          continue;
365       }
366    }
367
368    notify(conn, true);
369    return true;
370
371 bail_out:
372    notify(conn, true);
373    return false;
374 }
375
376
377 /*
378  * Save user settings associated with this console
379  */
380 void Console::writeSettings()
381 {
382    QFont font = get_font();
383
384    QSettings settings(m_dir->name(), "bat");
385    settings.beginGroup("Console");
386    settings.setValue("consoleFont", font.family());
387    settings.setValue("consolePointSize", font.pointSize());
388    settings.setValue("consoleFixedPitch", font.fixedPitch());
389    settings.endGroup();
390 }
391
392 /*
393  * Read and restore user settings associated with this console
394  */
395 void Console::readSettings()
396
397    QFont font = get_font();
398
399    QSettings settings(m_dir->name(), "bat");
400    settings.beginGroup("Console");
401    font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
402    font.setPointSize(settings.value("consolePointSize", 10).toInt());
403    font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
404    settings.endGroup();
405    m_textEdit->setFont(font);
406 }
407
408 /*
409  * Set the console textEdit font
410  */
411 void Console::set_font()
412 {
413    bool ok;
414    QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
415    if (ok) {
416       m_textEdit->setFont(font);
417    }
418 }
419
420 /*
421  * Get the console text edit font
422  */
423 const QFont Console::get_font()
424 {
425    return m_textEdit->font();
426 }
427
428 /*
429  * Slot for responding to status dir button on button bar
430  */
431 void Console::status_dir()
432 {
433    QString cmd("status dir");
434    consoleCommand(cmd);
435 }
436
437 /*
438  * Slot for responding to messages button on button bar
439  * Here we want to bring the console to the front so use pages' consoleCommand
440  */
441 void Console::messages()
442 {
443    QString cmd(".messages");
444    consoleCommand(cmd);
445    messagesPending(false);
446 }
447
448 /*
449  * Put text into the console window
450  */
451 void Console::display_textf(const char *fmt, ...)
452 {
453    va_list arg_ptr;
454    char buf[1000];
455    int len;
456    va_start(arg_ptr, fmt);
457    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
458    va_end(arg_ptr);
459    display_text(buf);
460 }
461
462 void Console::display_text(const QString buf)
463 {
464    m_cursor->insertText(buf);
465    update_cursor();
466 }
467
468
469 void Console::display_text(const char *buf)
470 {
471    m_cursor->insertText(buf);
472    update_cursor();
473 }
474
475 void Console::display_html(const QString buf)
476 {
477    m_cursor->insertHtml(buf);
478    update_cursor();
479 }
480
481 /* Position cursor to end of screen */
482 void Console::update_cursor()
483 {
484    QApplication::restoreOverrideCursor();
485    m_textEdit->moveCursor(QTextCursor::End);
486    m_textEdit->ensureCursorVisible();
487 }
488
489 void Console::beginNewCommand(int conn)
490 {
491    DirComm *dircomm = m_dircommHash.value(conn);
492
493    for (int i=0; i < 3; i++) {
494       dircomm->write(".");
495       while (dircomm->read() > 0) {
496          if (mainWin->m_displayAll) display_text(dircomm->msg());
497       }
498       if (dircomm->m_at_main_prompt) {
499          break;
500       }
501    }
502    display_text("\n");
503 }
504
505 void Console::displayToPrompt(int conn)
506
507    DirComm *dircomm = m_dircommHash.value(conn);
508
509    int stat = 0;
510    QString buf;
511    if (mainWin->m_commDebug) Pmsg0(000, "DisplaytoPrompt\n");
512    while (!dircomm->m_at_prompt) {
513       if ((stat=dircomm->read()) > 0) {
514          buf += dircomm->msg();
515          if (buf.size() >= 8196 || m_messages_pending) {
516             display_text(buf);
517             buf.clear();
518             messagesPending(false);
519          }
520       }
521    }
522    display_text(buf);
523    if (mainWin->m_commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
524 }
525
526 void Console::discardToPrompt(int conn)
527 {
528    DirComm *dircomm = m_dircommHash.value(conn);
529
530    int stat = 0;
531    if (mainWin->m_commDebug) Pmsg0(000, "discardToPrompt\n");
532    if (mainWin->m_displayAll) {
533       displayToPrompt(conn);
534    } else {
535       while (!dircomm->m_at_prompt) {
536          stat=dircomm->read();
537       }
538    }
539    if (mainWin->m_commDebug) Pmsg1(000, "endDiscardToPrompt=%d\n", stat);
540 }
541
542 /*
543  * When the notifier is enabled, read_dir() will automatically be
544  * called by the Qt event loop when ever there is any output 
545  * from the Directory, and read_dir() will then display it on
546  * the console.
547  *
548  * When we are in a bat dialog, we want to control *all* output
549  * from the Directory, so we set notify to off.
550  *    m_console->notifiy(false);
551  */
552
553 /* dual purpose function to turn notify off and return an available connection */
554 int Console::notifyOff()
555
556    int conn = 0;
557    if(availableDirComm(conn))
558       notify(conn, false);
559    return conn;
560 }
561
562 /* knowing a connection, turn notify off or on */
563 bool Console::notify(int conn, bool enable)
564
565    DirComm *dircomm = m_dircommHash.value(conn);
566    return dircomm->notify(enable);
567 }
568
569 /* knowing a connection, return notify state */
570 bool Console::is_notify_enabled(int conn) const
571 {
572    DirComm *dircomm = m_dircommHash.value(conn);
573    return dircomm->is_notify_enabled();
574 }
575
576 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
577 {
578    m_directorTreeItem = item;
579 }
580
581 void Console::setDirRes(DIRRES *dir) 
582
583    m_dir = dir;
584 }
585
586 /*
587  * To have the ability to get the name of the director resource.
588  */
589 void Console::getDirResName(QString &name_returned)
590 {
591    name_returned = m_dir->name();
592 }
593
594 /* Slot for responding to page selectors status help command */
595 void Console::consoleHelp()
596 {
597    QString cmd("help");
598    consoleCommand(cmd);
599 }
600
601 /* Slot for responding to page selectors reload bacula-dir.conf */
602 void Console::consoleReload()
603 {
604    QString cmd("reload");
605    consoleCommand(cmd);
606 }
607
608 /* Function to get a list of volumes */
609 void Console::getVolumeList(QStringList &volumeList)
610 {
611    QString query("SELECT VolumeName AS Media FROM Media ORDER BY Media");
612    if (mainWin->m_sqlDebug) {
613       Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
614    }
615    QStringList results;
616    if (sql_cmd(query, results)) {
617       QString field;
618       QStringList fieldlist;
619       /* Iterate through the lines of results. */
620       foreach (QString resultline, results) {
621          fieldlist = resultline.split("\t");
622          volumeList.append(fieldlist[0]);
623       } /* foreach resultline */
624    } /* if results from query */
625 }
626
627 /* Function to get a list of volumes */
628 void Console::getStatusList(QStringList &statusLongList)
629 {
630    QString statusQuery("SELECT JobStatusLong FROM Status");
631    if (mainWin->m_sqlDebug) {
632       Pmsg1(000, "Query cmd : %s\n",statusQuery.toUtf8().data());
633    }
634    QStringList statusResults;
635    if (sql_cmd(statusQuery, statusResults)) {
636       QString field;
637       QStringList fieldlist;
638       /* Iterate through the lines of results. */
639       foreach (QString resultline, statusResults) {
640          fieldlist = resultline.split("\t");
641          statusLongList.append(fieldlist[0]);
642       } /* foreach resultline */
643    } /* if results from statusquery */
644 }
645
646 /* For suppressing .messages
647  * This may be rendered not needed if the multiple connections feature gets working */
648 bool Console::hasFocus()
649 {
650    if (mainWin->stackedWidget->currentIndex() == mainWin->stackedWidget->indexOf(this))
651       return true;
652    else
653       return false;
654 }
655
656 /* For adding feature to have the gui's messages button change when 
657  * messages are pending */
658 bool Console::messagesPending(bool pend)
659 {
660    bool prev = m_messages_pending;
661    m_messages_pending = pend;
662    mainWin->setMessageIcon();
663    return prev;
664 }
665
666 /* terminate all existing connections */
667 void Console::terminate()
668 {
669    foreach(DirComm* dircomm,  m_dircommHash) {
670       dircomm->terminate();
671    }
672    m_console->stopTimer();
673 }
674
675 /* Maybe this should be checking the list, for the moment lets check 0 which should be connected */
676 bool Console::is_connectedGui()
677 {
678    if (is_connected(0)) {
679       return true;
680    } else {
681       QString message = tr("Director is currently disconnected\nPlease reconnect!");
682       QMessageBox::warning(this, "Bat", message, QMessageBox::Ok );
683       return false;
684    }
685 }
686
687 int Console::read(int conn)
688 {
689    DirComm *dircomm = m_dircommHash.value(conn);
690    return dircomm->read();
691 }
692
693 char *Console::msg(int conn)
694 {
695    DirComm *dircomm = m_dircommHash.value(conn);
696    return dircomm->msg();
697 }
698
699 int Console::write(int conn, const QString msg)
700 {
701    DirComm *dircomm = m_dircommHash.value(conn);
702    return dircomm->write(msg);
703 }
704
705 int Console::write(int conn, const char *msg)
706 {
707    DirComm *dircomm = m_dircommHash.value(conn);
708    return dircomm->write(msg);
709 }
710
711 /* This checks to see if any is connected */
712 bool Console::is_connected()
713 {
714    bool connected = false;
715    foreach(DirComm* dircomm,  m_dircommHash) {
716       if (dircomm->is_connected())
717          return true;
718    }
719    return connected;
720 }
721
722 /* knowing the connection id, is it connected */
723 bool Console::is_connected(int conn)
724 {
725    DirComm *dircomm = m_dircommHash.value(conn);
726    return dircomm->is_connected();
727 }
728
729 /*
730  * Need an available connection.  Check existing connections or create one
731  */
732 bool Console::availableDirComm(int &conn)
733 {
734    QHash<int, DirComm*>::const_iterator iter = m_dircommHash.constBegin();
735    while (iter != m_dircommHash.constEnd()) {
736       DirComm *dircomm = iter.value();
737       if (dircomm->m_at_prompt && dircomm->m_at_main_prompt && dircomm->is_notify_enabled()) {
738          conn = dircomm->m_conn;
739          return true;
740       }
741       ++iter;
742    }
743    if (newDirComm(conn))
744       return true;
745    else
746       return false;
747 }
748
749 /*
750  *  Create a new connection
751  */
752 bool Console::newDirComm(int &conn)
753 {
754    m_dircommCounter += 1;
755    conn = m_dircommCounter;
756    if (mainWin->m_connDebug)
757       Pmsg1(000, "DirComm %i About to Create and Connect\n", m_dircommCounter);
758    DirComm *dircomm = new DirComm(this, m_dircommCounter);
759    m_dircommHash.insert(m_dircommCounter, dircomm);
760    bool success = dircomm->connect_dir();
761    if (mainWin->m_connDebug)
762       if (success)
763          Pmsg1(000, "DirComm %i Connected\n", conn);
764       else
765          Pmsg1(000, "DirComm %i NOT Connected\n", conn);
766    return success;
767 }