]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/console/console.cpp
1594171f418166a8c20ad819ee50f3ecdf5a3676
[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 1");
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    notify(false);
166    write(cmd);
167    while ((stat = read()) > 0) {
168       strip_trailing_junk(msg());
169       list << msg();
170    }
171    notify(true);
172    return list;
173 }
174
175 /*  
176  * Send a job name to the director, and read all the resulting
177  *  defaults. 
178  */
179 bool Console::get_job_defaults(struct job_defaults &job_defs)
180 {
181    QString scmd;
182    int stat;
183    char *def;
184
185    notify(false);
186    beginNewCommand();
187    scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
188    write(scmd);
189    while ((stat = read()) > 0) {
190       def = strchr(msg(), '=');
191       if (!def) {
192          continue;
193       }
194       /* Pointer to default value */
195       *def++ = 0;
196       strip_trailing_junk(def);
197
198       if (strcmp(msg(), "job") == 0) {
199          if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
200             goto bail_out;
201          }
202          continue;
203       }
204       if (strcmp(msg(), "pool") == 0) {
205          job_defs.pool_name = def;
206          continue;
207       }
208       if (strcmp(msg(), "messages") == 0) {
209          job_defs.messages_name = def;
210          continue;
211       }
212       if (strcmp(msg(), "client") == 0) {
213          job_defs.client_name = def;
214          continue;
215       }
216       if (strcmp(msg(), "storage") == 0) {
217          job_defs.store_name = def;
218          continue;
219       }
220       if (strcmp(msg(), "where") == 0) {
221          job_defs.where = def;
222          continue;
223       }
224       if (strcmp(msg(), "level") == 0) {
225          job_defs.level = def;
226          continue;
227       }
228       if (strcmp(msg(), "type") == 0) {
229          job_defs.type = def;
230          continue;
231       }
232       if (strcmp(msg(), "fileset") == 0) {
233          job_defs.fileset_name = def;
234          continue;
235       }
236       if (strcmp(msg(), "catalog") == 0) {
237          job_defs.catalog_name = def;
238          continue;
239       }
240       if (strcmp(msg(), "enabled") == 0) {
241          job_defs.enabled = *def == '1' ? true : false;
242          continue;
243       }
244    }
245
246 #ifdef xxx
247    bsnprintf(cmd, sizeof(cmd), "job=%s pool=%s client=%s storage=%s where=%s\n"
248       "level=%s type=%s fileset=%s catalog=%s enabled=%d\n",
249       job_defs.job_name.toUtf8().data(), job_defs.pool_name.toUtf8().data(), 
250       job_defs.client_name.toUtf8().data(), 
251       job_defs.pool_name.toUtf8().data(), job_defs.messages_name.toUtf8().data(), 
252       job_defs.store_name.toUtf8().data(),
253       job_defs.where.toUtf8().data(), job_defs.level.toUtf8().data(), 
254       job_defs.type.toUtf8().data(), job_defs.fileset_name.toUtf8().data(),
255       job_defs.catalog_name.toUtf8().data(), job_defs.enabled);
256 #endif
257
258    notify(true);
259    return true;
260
261 bail_out:
262    notify(true);
263    return false;
264 }
265
266
267 /*
268  * Save user settings associated with this console
269  */
270 void Console::writeSettings()
271 {
272    QFont font = get_font();
273
274    QSettings settings("bacula.org", "bat");
275    /* ***FIXME*** make console name unique */
276    settings.beginGroup("Console");
277    settings.setValue("consoleFont", font.family());
278    settings.setValue("consolePointSize", font.pointSize());
279    settings.setValue("consoleFixedPitch", font.fixedPitch());
280    settings.endGroup();
281 }
282
283 /*
284  * Read and restore user settings associated with this console
285  */
286 void Console::readSettings()
287
288    QFont font = get_font();
289
290    QSettings settings("bacula.org", "bat");
291    settings.beginGroup("Console");
292    font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
293    font.setPointSize(settings.value("consolePointSize", 10).toInt());
294    font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
295    settings.endGroup();
296    m_textEdit->setFont(font);
297 }
298
299 /*
300  * Set the console textEdit font
301  */
302 void Console::set_font()
303 {
304    bool ok;
305    QFont font = QFontDialog::getFont(&ok, QFont(m_textEdit->font()), this);
306    if (ok) {
307       m_textEdit->setFont(font);
308    }
309 }
310
311 /*
312  * Get the console text edit font
313  */
314 const QFont Console::get_font()
315 {
316    return m_textEdit->font();
317 }
318
319
320 void Console::status_dir()
321 {
322    write_dir("status dir\n");
323    displayToPrompt();
324 }
325
326 /*
327  * Put text into the console window
328  */
329 void Console::display_textf(const char *fmt, ...)
330 {
331    va_list arg_ptr;
332    char buf[1000];
333    int len;
334    va_start(arg_ptr, fmt);
335    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
336    va_end(arg_ptr);
337    display_text(buf);
338 }
339
340 void Console::display_text(const QString buf)
341 {
342    m_cursor->movePosition(QTextCursor::End);
343    m_cursor->insertText(buf);
344 }
345
346
347 void Console::display_text(const char *buf)
348 {
349    m_cursor->movePosition(QTextCursor::End);
350    m_cursor->insertText(buf);
351 }
352
353 /* Position cursor to end of screen */
354 void Console::update_cursor()
355 {
356    QApplication::restoreOverrideCursor();
357    m_textEdit->moveCursor(QTextCursor::End);
358    m_textEdit->ensureCursorVisible();
359 }
360
361 /* 
362  * This should be moved into a bSocket class 
363  */
364 char *Console::msg()
365 {
366    if (m_sock) {
367       return m_sock->msg;
368    }
369    return NULL;
370 }
371
372 /* Send a command to the Director */
373 void Console::write_dir(const char *msg)
374 {
375    if (m_sock) {
376       mainWin->set_status(_("Processing command ..."));
377       QApplication::setOverrideCursor(Qt::WaitCursor);
378       write(msg);
379    } else {
380       mainWin->set_status(" Director not connected. Click on connect button.");
381       mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/disconnected.png")));
382       QBrush redBrush(Qt::red);
383       m_consoleItem->setForeground(0, redBrush);
384       m_at_prompt = false;
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    while (!m_at_prompt) {
424       if ((stat=read()) > 0) {
425          display_text(msg());
426       }
427    }
428    if (commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
429 }
430
431 void Console::discardToPrompt()
432
433    int stat;
434    if (commDebug) Pmsg0(000, "discardToPrompt\n");
435    while (!m_at_prompt) {
436       stat = read();
437    }
438    if (commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
439 }
440
441
442 /* 
443  * Blocking read from director
444  */
445 int Console::read()
446 {
447    int stat = 0;
448    while (m_sock) {
449       for (;;) {
450          stat = bnet_wait_data_intr(m_sock, 1);
451          if (stat > 0) {
452             break;
453          } 
454          app->processEvents();
455          if (m_api_set && m_messages_pending) {
456             write_dir(".messages");
457             m_messages_pending = false;
458          }
459       }
460       stat = m_sock->recv();
461       if (stat >= 0) {
462          if (m_at_prompt) {
463             display_text("\n");
464             m_at_prompt = false;
465          }
466          if (commDebug) Pmsg1(000, "got: %s", m_sock->msg);
467       }
468       switch (m_sock->msglen) {
469       case BNET_SERVER_READY:
470          if (m_api_set && m_messages_pending) {
471             write_dir(".messages");
472             m_messages_pending = false;
473          }
474          m_at_prompt = true;
475          continue;
476       case BNET_MSGS_PENDING:
477          if (commDebug) Pmsg0(000, "MSGS PENDING\n");
478          m_messages_pending = true;
479          continue;
480       case BNET_CMD_OK:
481          if (commDebug) Pmsg0(000, "CMD OK\n");
482          m_at_prompt = false;
483          continue;
484       case BNET_CMD_BEGIN:
485          if (commDebug) Pmsg0(000, "CMD BEGIN\n");
486          m_at_prompt = false;
487          continue;
488       case BNET_PROMPT:
489          if (commDebug) Pmsg0(000, "PROMPT\n");
490          m_at_prompt = true;
491          mainWin->set_status(_("At prompt waiting for input ..."));
492          update_cursor();
493          QApplication::restoreOverrideCursor();
494          break;
495       case BNET_CMD_FAILED:
496          if (commDebug) Pmsg0(000, "CMD FAIL\n");
497          mainWin->set_status(_("Command failed. At prompt waiting for input ..."));
498          update_cursor();
499          QApplication::restoreOverrideCursor();
500          break;
501       /* We should not get this one */
502       case BNET_EOD:
503          if (commDebug) Pmsg0(000, "EOD\n");
504          mainWin->set_status_ready();
505          update_cursor();
506          QApplication::restoreOverrideCursor();
507          if (!m_api_set) {
508             break;
509          }
510          continue;
511       case BNET_START_SELECT:
512          new selectDialog(this);    
513          break;
514       case BNET_RUN_CMD:
515          new runCmdDialog(this);
516          break;
517       case BNET_ERROR_MSG:
518          m_sock->recv();              /* get the message */
519          display_text(msg());
520          QMessageBox::critical(this, "Error", msg(), QMessageBox::Ok);
521          break;
522       case BNET_WARNING_MSG:
523          m_sock->recv();              /* get the message */
524          display_text(msg());
525          QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
526          break;
527       case BNET_INFO_MSG:
528          m_sock->recv();              /* get the message */
529          display_text(msg());
530          mainWin->set_status(msg());
531          break;
532       }
533       if (is_bnet_stop(m_sock)) {         /* error or term request */
534          m_sock->close();
535          m_sock = NULL;
536          mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/disconnected.png")));
537          QBrush redBrush(Qt::red);
538          m_consoleItem->setForeground(0, redBrush);
539          m_notifier->setEnabled(false);
540          delete m_notifier;
541          m_notifier = NULL;
542          mainWin->set_status(_("Director disconnected."));
543          QApplication::restoreOverrideCursor();
544          stat = BNET_HARDEOF;
545       }
546       break;
547    } 
548    return stat;
549 }
550
551 /* Called by signal when the Director has output for us */
552 void Console::read_dir(int fd)
553 {
554    int stat;
555    (void)fd;
556
557    if (commDebug) Pmsg0(000, "read_dir\n");
558    while ((stat = read()) >= 0) {
559       display_text(msg());
560    }
561 }
562
563 /*
564  * When the notifier is enabled, read_dir() will automatically be
565  * called by the Qt event loop when ever there is any output 
566  * from the Directory, and read_dir() will then display it on
567  * the console.
568  *
569  * When we are in a bat dialog, we want to control *all* output
570  * from the Directory, so we set notify to off.
571  *    m_console->notifiy(false);
572  */
573 void Console::notify(bool enable) 
574
575    m_notifier->setEnabled(enable);   
576 }
577
578 void Console::setTreeItem(QTreeWidgetItem *item) 
579
580    m_consoleItem = item;
581 }
582
583 void Console::setDirRes(DIRRES *dir) 
584
585    m_dir = dir;
586 }