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