]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/console/console.cpp
Start restore tree
[bacula/bacula] / bacula / src / qt-console / console / console.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-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 /*
30  *   Version $Id$
31  *
32  *  Console Class
33  *
34  *   Kern Sibbald, January MMVI
35  *
36  */ 
37
38 #include <QAbstractEventDispatcher>
39 #include "bat.h"
40 #include "console.h"
41
42 Console::Console(QStackedWidget *parent)
43 {
44    QFont font;
45    QTreeWidgetItem *item, *topItem;
46    QTreeWidget *treeWidget = mainWin->treeWidget;
47
48    setupUi(this);
49    parent->addWidget(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    bRestore *brestore = new bRestore(parent);
57    brestore->setupUi(brestore);
58    parent->addWidget(brestore);
59
60    /* Just take the first Director */
61    LockRes();
62    m_dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
63    UnlockRes();
64
65    /* ***FIXME*** Dummy setup of treeWidget */
66    treeWidget->clear();
67    treeWidget->setColumnCount(1);
68    treeWidget->setHeaderLabel("Selection");
69    topItem = new QTreeWidgetItem(treeWidget);
70    topItem->setText(0, m_dir->name());
71    topItem->setIcon(0, QIcon(QString::fromUtf8("images/server.png")));
72    item = new QTreeWidgetItem(topItem);
73    m_consoleItem = item;
74    item->setText(0, "Console");
75    item->setText(1, "0");
76    QBrush redBrush(Qt::red);
77    item->setForeground(0, redBrush);
78    item = new QTreeWidgetItem(topItem);
79    item->setText(0, "brestore");
80    item->setText(1, "1");
81    treeWidget->expandItem(topItem);
82
83    readSettings();
84
85 }
86
87 /* Terminate any open socket */
88 void Console::terminate()
89 {
90    if (m_sock) {
91       m_sock->close();
92       m_sock = NULL;
93    }
94 }
95
96 /*
97  * Connect to Director. If there are more than one, put up
98  * a modal dialog so that the user chooses one.
99  */
100 void Console::connect()
101 {
102    JCR jcr;
103
104    m_textEdit = textEdit;   /* our console screen */
105
106    if (!m_dir) {          
107       mainWin->set_status("No Director found.");
108       return;
109    }
110    if (m_sock) {
111       mainWin->set_status("Already connected.");
112       return;
113    }
114
115    memset(&jcr, 0, sizeof(jcr));
116
117    mainWin->set_statusf(_("Connecting to Director %s:%d"), m_dir->address, m_dir->DIRport);
118    display_textf(_("Connecting to Director %s:%d\n\n"), m_dir->address, m_dir->DIRport);
119
120    /* Give GUI a chance */
121    app->processEvents();
122    
123    LockRes();
124    /* If cons==NULL, default console will be used */
125    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
126    UnlockRes();
127
128    m_sock = bnet_connect(NULL, 5, 15, _("Director daemon"), m_dir->address,
129                           NULL, m_dir->DIRport, 0);
130    if (m_sock == NULL) {
131       mainWin->set_status("Connection failed");
132       return;
133    } else {
134       /* Update page selector to green to indicate that Console is connected */
135       mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/connected.png")));
136       QBrush greenBrush(Qt::green);
137       m_consoleItem->setForeground(0, greenBrush);
138    }
139
140    jcr.dir_bsock = m_sock;
141
142    if (!authenticate_director(&jcr, m_dir, cons)) {
143       display_text(m_sock->msg);
144       return;
145    }
146
147    /* Give GUI a chance */
148    app->processEvents();
149
150    mainWin->set_status(_("Initializing ..."));
151
152    /* Set up input notifier */
153    m_notifier = new QSocketNotifier(m_sock->fd, QSocketNotifier::Read, 0);
154    QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
155
156    beginNewCommand();
157    job_list = get_list(".jobs");
158    client_list = get_list(".clients");
159    fileset_list = get_list(".filesets");
160    messages_list = get_list(".messages");
161    pool_list = get_list(".pools");
162    storage_list = get_list(".storage");
163    type_list = get_list(".types");
164    level_list = get_list(".levels");
165
166    mainWin->set_status(_("Connected"));
167    return;
168 }
169
170
171 /*  
172  * Send a command to the director, and read all the resulting
173  *  output into a list.
174  */
175 QStringList Console::get_list(char *cmd)
176 {
177    QStringList list;
178    int stat;
179
180    setEnabled(false);
181    write(cmd);
182    while ((stat = read()) > 0) {
183       strip_trailing_junk(msg());
184       list << msg();
185    }
186    setEnabled(true);
187    list.sort();
188    return list;
189 }
190
191 /*  
192  * Send a job name to the director, and read all the resulting
193  *  defaults. 
194  */
195 bool Console::get_job_defaults(struct job_defaults &job_defs)
196 {
197    QString scmd;
198    char cmd[1000];
199    int stat;
200    char *def;
201
202    setEnabled(false);
203    beginNewCommand();
204    scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
205    write(scmd);
206    while ((stat = read()) > 0) {
207       def = strchr(msg(), '=');
208       if (!def) {
209          continue;
210       }
211       /* Pointer to default value */
212       *def++ = 0;
213       strip_trailing_junk(def);
214
215       if (strcmp(msg(), "job") == 0) {
216          if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
217             goto bail_out;
218          }
219          continue;
220       }
221       if (strcmp(msg(), "pool") == 0) {
222          job_defs.pool_name = def;
223          continue;
224       }
225       if (strcmp(msg(), "messages") == 0) {
226          job_defs.messages_name = def;
227          continue;
228       }
229       if (strcmp(msg(), "client") == 0) {
230          job_defs.client_name = def;
231          continue;
232       }
233       if (strcmp(msg(), "storage") == 0) {
234          job_defs.store_name = def;
235          continue;
236       }
237       if (strcmp(msg(), "where") == 0) {
238          job_defs.where = def;
239          continue;
240       }
241       if (strcmp(msg(), "level") == 0) {
242          job_defs.level = def;
243          continue;
244       }
245       if (strcmp(msg(), "type") == 0) {
246          job_defs.type = def;
247          continue;
248       }
249       if (strcmp(msg(), "fileset") == 0) {
250          job_defs.fileset_name = def;
251          continue;
252       }
253       if (strcmp(msg(), "catalog") == 0) {
254          job_defs.catalog_name = def;
255          continue;
256       }
257       if (strcmp(msg(), "enabled") == 0) {
258          job_defs.enabled = *def == '1' ? true : false;
259          continue;
260       }
261    }
262    bsnprintf(cmd, sizeof(cmd), "job=%s pool=%s client=%s storage=%s where=%s\n"
263       "level=%s type=%s fileset=%s catalog=%s enabled=%d\n",
264       job_defs.job_name.toUtf8().data(), job_defs.pool_name.toUtf8().data(), 
265       job_defs.client_name.toUtf8().data(), 
266       job_defs.pool_name.toUtf8().data(), job_defs.messages_name.toUtf8().data(), 
267       job_defs.store_name.toUtf8().data(),
268       job_defs.where.toUtf8().data(), job_defs.level.toUtf8().data(), 
269       job_defs.type.toUtf8().data(), job_defs.fileset_name.toUtf8().data(),
270       job_defs.catalog_name.toUtf8().data(), job_defs.enabled);
271
272    setEnabled(true);
273    return true;
274
275 bail_out:
276    setEnabled(true);
277    return false;
278 }
279
280
281 /*
282  * Save user settings associated with this console
283  */
284 void Console::writeSettings()
285 {
286    QFont font = get_font();
287
288    QSettings settings("bacula.org", "bat");
289    /* ***FIXME*** make console name unique */
290    settings.beginGroup("Console");
291    settings.setValue("consoleFont", font.family());
292    settings.setValue("consolePointSize", font.pointSize());
293    settings.setValue("consoleFixedPitch", font.fixedPitch());
294    settings.endGroup();
295 }
296
297 /*
298  * Read and restore user settings associated with this console
299  */
300 void Console::readSettings()
301
302    QFont font = get_font();
303
304    QSettings settings("bacula.org", "bat");
305    settings.beginGroup("Console");
306    font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
307    font.setPointSize(settings.value("consolePointSize", 10).toInt());
308    font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
309    settings.endGroup();
310    m_textEdit->setFont(font);
311 }
312
313 /*
314  * Set the console textEdit font
315  */
316 void Console::set_font()
317 {
318    bool ok;
319    QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
320    if (ok) {
321       m_textEdit->setFont(font);
322    }
323 }
324
325 /*
326  * Get the console text edit font
327  */
328 const QFont Console::get_font()
329 {
330    return m_textEdit->font();
331 }
332
333
334 void Console::status_dir()
335 {
336    write_dir("status dir\n");
337 }
338
339 /*
340  * Put text into the console window
341  */
342 void Console::display_textf(const char *fmt, ...)
343 {
344    va_list arg_ptr;
345    char buf[1000];
346    int len;
347    va_start(arg_ptr, fmt);
348    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
349    va_end(arg_ptr);
350    display_text(buf);
351 }
352
353 void Console::display_text(const QString buf)
354 {
355    m_cursor->movePosition(QTextCursor::End);
356    m_cursor->insertText(buf);
357 }
358
359
360 void Console::display_text(const char *buf)
361 {
362    m_cursor->movePosition(QTextCursor::End);
363    m_cursor->insertText(buf);
364 }
365
366 /* Position cursor to end of screen */
367 void Console::update_cursor()
368 {
369    QApplication::restoreOverrideCursor();
370    m_textEdit->moveCursor(QTextCursor::End);
371    m_textEdit->ensureCursorVisible();
372 }
373
374 /* 
375  * This should be moved into a bSocket class 
376  */
377 char *Console::msg()
378 {
379    if (m_sock) {
380       return m_sock->msg;
381    }
382    return NULL;
383 }
384
385 /* Send a command to the Director */
386 void Console::write_dir(const char *msg)
387 {
388    if (m_sock) {
389       m_at_prompt = false;
390       mainWin->set_status(_("Processing command ..."));
391       QApplication::setOverrideCursor(Qt::WaitCursor);
392       m_sock->msglen = strlen(msg);
393       pm_strcpy(&m_sock->msg, msg);
394       m_sock->send();
395    } else {
396       mainWin->set_status(" Director not connected. Click on connect button.");
397       mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/disconnected.png")));
398       QBrush redBrush(Qt::red);
399       m_consoleItem->setForeground(0, redBrush);
400    }
401 }
402
403 int Console::write(const QString msg)
404 {
405    return write(msg.toUtf8().data());
406 }
407
408 int Console::write(const char *msg)
409 {
410    m_sock->msglen = strlen(msg);
411    pm_strcpy(&m_sock->msg, msg);
412    return m_sock->send();
413 }
414
415 /*
416  * Get to main command prompt 
417  */
418 void Console::beginNewCommand()
419 {
420 // displayToPrompt();
421    write(".\n");
422    while (read() > 0) {
423    }
424    write(".\n");
425    while (read() > 0) {
426    }
427    write(".\n");
428    while (read() > 0) {
429    }
430    display_text("\n");
431 }
432
433 void Console::displayToPrompt()
434
435    while (read() > 0) {
436       display_text(msg());
437    }
438
439 }
440
441 /* 
442  * Blocking read from director */
443 int Console::read()
444 {
445    int stat;
446    if (m_sock) {
447       for (;;) {
448          stat = bnet_wait_data_intr(m_sock, 1);
449          if (stat > 0) {
450             break;
451          } 
452          app->processEvents();
453          if (stat < 0) {
454             return BNET_ERROR;
455          }
456       }
457       return m_sock->recv();
458    } 
459    return BNET_HARDEOF;
460 }
461
462 /* Called by signal when the Director has output for us */
463 void Console::read_dir(int fd)
464 {
465    int stat;
466    (void)fd;
467
468    if (!m_sock) {
469       return;
470    }
471    stat = read();
472    if (stat >= 0) {
473       if (m_at_prompt) {
474          display_text("\n");
475          m_at_prompt = false;
476       }
477       display_text(msg());
478       return;
479    }
480    if (is_bnet_stop(m_sock)) {         /* error or term request */
481       m_sock->close();
482       m_sock = NULL;
483       mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/disconnected.png")));
484       QBrush redBrush(Qt::red);
485       m_consoleItem->setForeground(0, redBrush);
486       m_notifier->setEnabled(false);
487       delete m_notifier;
488       m_notifier = NULL;
489       mainWin->set_status(_("Director disconnected."));
490       QApplication::restoreOverrideCursor();
491       return;
492    }
493    /* Must be a signal -- either do something or ignore it */
494    if (m_sock->msglen == BNET_PROMPT) {
495       m_at_prompt = true;
496       mainWin->set_status(_("At prompt waiting for input ..."));
497       update_cursor();
498    }
499    if (m_sock->msglen == BNET_EOD) {
500       mainWin->set_status_ready();
501       update_cursor();
502    }
503    return;
504 }