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