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