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