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