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