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