]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/console/console.cpp
ac90518935ace2e0182fe13ea90ddbe30841b869
[bacula/bacula] / bacula / src / qt-console / console / console.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2007 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 plus additions
11    that are listed 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 John Walker.
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 <QAbstractEventDispatcher>
38 #include "bat.h"
39 #include "console.h"
40
41 Console::Console(QStackedWidget *parent)
42 {
43    QFont font;
44    m_parent = parent;
45    m_closeable = false;
46    m_console = this;
47    (void)parent;
48
49    setupUi(this);
50    m_sock = NULL;
51    m_at_prompt = false;
52    m_textEdit = textEdit;   /* our console screen */
53    m_cursor = new QTextCursor(m_textEdit->document());
54    mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/disconnected.png")));
55
56    /* Check for messages every 5 seconds */
57 // m_timer = new QTimer(this);
58 // QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
59 // m_timer->start(5000);
60 }
61
62 Console::~Console()
63 {
64 }
65
66 void Console::poll_messages()
67 {
68    m_messages_pending = true;
69 }
70
71 /* Terminate any open socket */
72 void Console::terminate()
73 {
74    if (m_sock) {
75       m_sock->close();
76       m_sock = NULL;
77    }
78 // m_timer->stop();
79 }
80
81 /*
82  * Connect to Director. If there are more than one, put up
83  * a modal dialog so that the user chooses one.
84  */
85 void Console::connect()
86 {
87    JCR jcr;
88    utime_t heart_beat;
89
90    m_textEdit = textEdit;   /* our console screen */
91
92    if (!m_dir) {          
93       mainWin->set_status("No Director found.");
94       return;
95    }
96    if (m_sock) {
97       mainWin->set_status("Already connected.");
98       return;
99    }
100
101    memset(&jcr, 0, sizeof(jcr));
102
103    mainWin->set_statusf(_("Connecting to Director %s:%d"), m_dir->address, m_dir->DIRport);
104    display_textf(_("Connecting to Director %s:%d\n\n"), m_dir->address, m_dir->DIRport);
105
106    /* Give GUI a chance */
107    app->processEvents();
108    
109    LockRes();
110    /* If cons==NULL, default console will be used */
111    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
112    UnlockRes();
113
114    if (m_dir->heartbeat_interval) {
115       heart_beat = m_dir->heartbeat_interval;
116    } else if (cons) {
117       heart_beat = cons->heartbeat_interval;
118    } else {
119       heart_beat = 0;
120    }        
121
122    m_sock = bnet_connect(NULL, 5, 15, heart_beat,
123                           _("Director daemon"), m_dir->address,
124                           NULL, m_dir->DIRport, 0);
125    if (m_sock == NULL) {
126       mainWin->set_status("Connection failed");
127       return;
128    } else {
129       /* Update page selector to green to indicate that Console is connected */
130       mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/connected.png")));
131       QBrush greenBrush(Qt::green);
132       QTreeWidgetItem *item = mainWin->getFromHash(this);
133       item->setForeground(0, greenBrush);
134    }
135
136    jcr.dir_bsock = m_sock;
137
138    if (!authenticate_director(&jcr, m_dir, cons)) {
139       display_text(m_sock->msg);
140       return;
141    }
142
143    /* Give GUI a chance */
144    app->processEvents();
145
146    mainWin->set_status(_("Initializing ..."));
147
148    /* Set up input notifier */
149    m_notifier = new QSocketNotifier(m_sock->fd, QSocketNotifier::Read, 0);
150    QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
151
152    write(".api 1");
153    discardToPrompt();
154
155    beginNewCommand();
156    dir_cmd(".jobs", job_list);
157    dir_cmd(".clients", client_list);
158    dir_cmd(".filesets", fileset_list);  
159    dir_cmd(".messages", messages_list);
160    dir_cmd(".pools", pool_list);
161    dir_cmd(".storage", storage_list);
162    dir_cmd(".types", type_list);
163    dir_cmd(".levels", level_list);
164
165    mainWin->set_status(_("Connected"));
166    return;
167 }
168
169 bool Console::dir_cmd(QString &cmd, QStringList &results)
170 {
171    return dir_cmd(cmd.toUtf8().data(), results);
172 }
173
174 /*
175  * Send a command to the Director, and return the
176  *  results in a QStringList.  
177  */
178 bool Console::dir_cmd(const char *cmd, QStringList &results)
179 {
180    int stat;
181
182    notify(false);
183    write(cmd);
184    while ((stat = read()) > 0) {
185       strip_trailing_junk(msg());
186       results << msg();
187    }
188    notify(true);
189    discardToPrompt();
190    return true;              /* ***FIXME*** return any command error */
191 }
192
193 bool Console::sql_cmd(QString &query, QStringList &results)
194 {
195    return sql_cmd(query.toUtf8().data(), results);
196 }
197
198 /*
199  * Send an sql query to the Director, and return the
200  *  results in a QStringList.  
201  */
202 bool Console::sql_cmd(const char *query, QStringList &results)
203 {
204    int stat;
205    POOL_MEM cmd(PM_MESSAGE);
206
207    notify(false);
208    
209    pm_strcpy(cmd, ".sql query=\"");
210    pm_strcat(cmd, query);
211    pm_strcat(cmd, "\"");
212    write(cmd.c_str());
213    while ((stat = read()) > 0) {
214       strip_trailing_junk(msg());
215       results << msg();
216    }
217    notify(true);
218    discardToPrompt();
219    return true;              /* ***FIXME*** return any command error */
220 }
221
222
223 /*  
224  * Send a job name to the director, and read all the resulting
225  *  defaults. 
226  */
227 bool Console::get_job_defaults(struct job_defaults &job_defs)
228 {
229    QString scmd;
230    int stat;
231    char *def;
232
233    notify(false);
234    beginNewCommand();
235    scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
236    write(scmd);
237    while ((stat = read()) > 0) {
238       def = strchr(msg(), '=');
239       if (!def) {
240          continue;
241       }
242       /* Pointer to default value */
243       *def++ = 0;
244       strip_trailing_junk(def);
245
246       if (strcmp(msg(), "job") == 0) {
247          if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
248             goto bail_out;
249          }
250          continue;
251       }
252       if (strcmp(msg(), "pool") == 0) {
253          job_defs.pool_name = def;
254          continue;
255       }
256       if (strcmp(msg(), "messages") == 0) {
257          job_defs.messages_name = def;
258          continue;
259       }
260       if (strcmp(msg(), "client") == 0) {
261          job_defs.client_name = def;
262          continue;
263       }
264       if (strcmp(msg(), "storage") == 0) {
265          job_defs.store_name = def;
266          continue;
267       }
268       if (strcmp(msg(), "where") == 0) {
269          job_defs.where = def;
270          continue;
271       }
272       if (strcmp(msg(), "level") == 0) {
273          job_defs.level = def;
274          continue;
275       }
276       if (strcmp(msg(), "type") == 0) {
277          job_defs.type = def;
278          continue;
279       }
280       if (strcmp(msg(), "fileset") == 0) {
281          job_defs.fileset_name = def;
282          continue;
283       }
284       if (strcmp(msg(), "catalog") == 0) {
285          job_defs.catalog_name = def;
286          continue;
287       }
288       if (strcmp(msg(), "enabled") == 0) {
289          job_defs.enabled = *def == '1' ? true : false;
290          continue;
291       }
292    }
293
294 #ifdef xxx
295    bsnprintf(cmd, sizeof(cmd), "job=%s pool=%s client=%s storage=%s where=%s\n"
296       "level=%s type=%s fileset=%s catalog=%s enabled=%d\n",
297       job_defs.job_name.toUtf8().data(), job_defs.pool_name.toUtf8().data(), 
298       job_defs.client_name.toUtf8().data(), 
299       job_defs.pool_name.toUtf8().data(), job_defs.messages_name.toUtf8().data(), 
300       job_defs.store_name.toUtf8().data(),
301       job_defs.where.toUtf8().data(), job_defs.level.toUtf8().data(), 
302       job_defs.type.toUtf8().data(), job_defs.fileset_name.toUtf8().data(),
303       job_defs.catalog_name.toUtf8().data(), job_defs.enabled);
304 #endif
305
306    notify(true);
307    return true;
308
309 bail_out:
310    notify(true);
311    return false;
312 }
313
314
315 /*
316  * Save user settings associated with this console
317  */
318 void Console::writeSettings()
319 {
320    QFont font = get_font();
321
322    QSettings settings(m_dir->name(), "bat");
323    settings.beginGroup("Console");
324    settings.setValue("consoleFont", font.family());
325    settings.setValue("consolePointSize", font.pointSize());
326    settings.setValue("consoleFixedPitch", font.fixedPitch());
327    settings.endGroup();
328 }
329
330 /*
331  * Read and restore user settings associated with this console
332  */
333 void Console::readSettings()
334
335    QFont font = get_font();
336
337    QSettings settings(m_dir->name(), "bat");
338    settings.beginGroup("Console");
339    font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
340    font.setPointSize(settings.value("consolePointSize", 10).toInt());
341    font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
342    settings.endGroup();
343    m_textEdit->setFont(font);
344 }
345
346 /*
347  * Set the console textEdit font
348  */
349 void Console::set_font()
350 {
351    bool ok;
352    QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
353    if (ok) {
354       m_textEdit->setFont(font);
355    }
356 }
357
358 /*
359  * Get the console text edit font
360  */
361 const QFont Console::get_font()
362 {
363    return m_textEdit->font();
364 }
365
366
367 void Console::status_dir()
368 {
369    write_dir("status dir\n");
370    displayToPrompt();
371 }
372
373 /*
374  * Put text into the console window
375  */
376 void Console::display_textf(const char *fmt, ...)
377 {
378    va_list arg_ptr;
379    char buf[1000];
380    int len;
381    va_start(arg_ptr, fmt);
382    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
383    va_end(arg_ptr);
384    display_text(buf);
385 }
386
387 void Console::display_text(const QString buf)
388 {
389    m_cursor->movePosition(QTextCursor::End);
390    m_cursor->insertText(buf);
391 }
392
393
394 void Console::display_text(const char *buf)
395 {
396    m_cursor->movePosition(QTextCursor::End);
397    m_cursor->insertText(buf);
398 }
399
400 /* Position cursor to end of screen */
401 void Console::update_cursor()
402 {
403    QApplication::restoreOverrideCursor();
404    m_textEdit->moveCursor(QTextCursor::End);
405    m_textEdit->ensureCursorVisible();
406 }
407
408 /* 
409  * This should be moved into a bSocket class 
410  */
411 char *Console::msg()
412 {
413    if (m_sock) {
414       return m_sock->msg;
415    }
416    return NULL;
417 }
418
419 /* Send a command to the Director */
420 void Console::write_dir(const char *msg)
421 {
422    if (m_sock) {
423       mainWin->set_status(_("Processing command ..."));
424       QApplication::setOverrideCursor(Qt::WaitCursor);
425       write(msg);
426    } else {
427       mainWin->set_status(" Director not connected. Click on connect button.");
428       mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/disconnected.png")));
429       QBrush redBrush(Qt::red);
430       QTreeWidgetItem *item = mainWin->getFromHash(this);
431       item->setForeground(0, redBrush);
432       m_at_prompt = false;
433    }
434 }
435
436 int Console::write(const QString msg)
437 {
438    return write(msg.toUtf8().data());
439 }
440
441 int Console::write(const char *msg)
442 {
443    m_sock->msglen = pm_strcpy(m_sock->msg, msg);
444    m_at_prompt = false;
445    if (commDebug) Pmsg1(000, "send: %s\n", msg);
446    return m_sock->send();
447 }
448
449 /*
450  * Get to main command prompt 
451  */
452 void Console::beginNewCommand()
453 {
454    write(".\n");
455    while (read() > 0) {
456    }
457    write(".\n");
458    while (read() > 0) {
459    }
460    write(".\n");
461    while (read() > 0) {
462    }
463    display_text("\n");
464 }
465
466 void Console::displayToPrompt()
467
468    int stat;
469    if (commDebug) Pmsg0(000, "DisplaytoPrompt\n");
470    while (!m_at_prompt) {
471       if ((stat=read()) > 0) {
472          display_text(msg());
473       }
474    }
475    if (commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
476 }
477
478 void Console::discardToPrompt()
479
480    int stat;
481    if (commDebug) Pmsg0(000, "discardToPrompt\n");
482    while (!m_at_prompt) {
483       stat = read();
484    }
485    if (commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
486 }
487
488
489 /* 
490  * Blocking read from director
491  */
492 int Console::read()
493 {
494    int stat = 0;
495    while (m_sock) {
496       for (;;) {
497          stat = bnet_wait_data_intr(m_sock, 1);
498          if (stat > 0) {
499             break;
500          } 
501          app->processEvents();
502          if (m_api_set && m_messages_pending) {
503             write_dir(".messages");
504             m_messages_pending = false;
505          }
506       }
507       stat = m_sock->recv();
508       if (stat >= 0) {
509          if (m_at_prompt) {
510             display_text("\n");
511             m_at_prompt = false;
512          }
513          if (commDebug) Pmsg1(000, "got: %s", m_sock->msg);
514       }
515       switch (m_sock->msglen) {
516       case BNET_SERVER_READY:
517          if (m_api_set && m_messages_pending) {
518             write_dir(".messages");
519             m_messages_pending = false;
520          }
521          m_at_prompt = true;
522          continue;
523       case BNET_MSGS_PENDING:
524          if (commDebug) Pmsg0(000, "MSGS PENDING\n");
525          m_messages_pending = true;
526          continue;
527       case BNET_CMD_OK:
528          if (commDebug) Pmsg0(000, "CMD OK\n");
529          m_at_prompt = false;
530          continue;
531       case BNET_CMD_BEGIN:
532          if (commDebug) Pmsg0(000, "CMD BEGIN\n");
533          m_at_prompt = false;
534          continue;
535       case BNET_PROMPT:
536          if (commDebug) Pmsg0(000, "PROMPT\n");
537          m_at_prompt = true;
538          mainWin->set_status(_("At prompt waiting for input ..."));
539          update_cursor();
540          QApplication::restoreOverrideCursor();
541          break;
542       case BNET_CMD_FAILED:
543          if (commDebug) Pmsg0(000, "CMD FAIL\n");
544          mainWin->set_status(_("Command failed. At prompt waiting for input ..."));
545          update_cursor();
546          QApplication::restoreOverrideCursor();
547          break;
548       /* We should not get this one */
549       case BNET_EOD:
550          if (commDebug) Pmsg0(000, "EOD\n");
551          mainWin->set_status_ready();
552          update_cursor();
553          QApplication::restoreOverrideCursor();
554          if (!m_api_set) {
555             break;
556          }
557          continue;
558       case BNET_START_SELECT:
559          new selectDialog(this);    
560          break;
561       case BNET_RUN_CMD:
562          new runCmdDialog(this);
563          break;
564       case BNET_ERROR_MSG:
565          m_sock->recv();              /* get the message */
566          display_text(msg());
567          QMessageBox::critical(this, "Error", msg(), QMessageBox::Ok);
568          break;
569       case BNET_WARNING_MSG:
570          m_sock->recv();              /* get the message */
571          display_text(msg());
572          QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
573          break;
574       case BNET_INFO_MSG:
575          m_sock->recv();              /* get the message */
576          display_text(msg());
577          mainWin->set_status(msg());
578          break;
579       }
580       if (is_bnet_stop(m_sock)) {         /* error or term request */
581          m_sock->close();
582          m_sock = NULL;
583          mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/disconnected.png")));
584          QBrush redBrush(Qt::red);
585          QTreeWidgetItem *item = mainWin->getFromHash(this);
586          item->setForeground(0, redBrush);
587          m_notifier->setEnabled(false);
588          delete m_notifier;
589          m_notifier = NULL;
590          mainWin->set_status(_("Director disconnected."));
591          QApplication::restoreOverrideCursor();
592          stat = BNET_HARDEOF;
593       }
594       break;
595    } 
596    return stat;
597 }
598
599 /* Called by signal when the Director has output for us */
600 void Console::read_dir(int fd)
601 {
602    int stat;
603    (void)fd;
604
605    if (commDebug) Pmsg0(000, "read_dir\n");
606    while ((stat = read()) >= 0) {
607       display_text(msg());
608    }
609 }
610
611 /*
612  * When the notifier is enabled, read_dir() will automatically be
613  * called by the Qt event loop when ever there is any output 
614  * from the Directory, and read_dir() will then display it on
615  * the console.
616  *
617  * When we are in a bat dialog, we want to control *all* output
618  * from the Directory, so we set notify to off.
619  *    m_console->notifiy(false);
620  */
621 void Console::notify(bool enable) 
622
623    m_notifier->setEnabled(enable);   
624 }
625
626 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
627 {
628    m_directorTreeItem = item;
629 }
630
631 void Console::setDirRes(DIRRES *dir) 
632
633    m_dir = dir;
634 }
635
636 /*
637  * To have the ability to get the name of the director resource.
638  */
639 void Console::getDirResName(QString &name_returned)
640 {
641    name_returned = m_dir->name();
642 }