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