]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/console/console.cpp
Add BNET_RUN_CMD to bat
[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    readSettings();
54    /* Check for messages every 5 seconds */
55 // m_timer = new QTimer(this);
56 // QWidget::connect(m_timer, SIGNAL(timeout()), this, SLOT(poll_messages()));
57 // m_timer->start(5000);
58
59 }
60
61 void Console::poll_messages()
62 {
63    m_messages_pending = true;
64 }
65
66 /* Terminate any open socket */
67 void Console::terminate()
68 {
69    if (m_sock) {
70       m_sock->close();
71       m_sock = NULL;
72    }
73 // m_timer->stop();
74 }
75
76 /*
77  * Connect to Director. If there are more than one, put up
78  * a modal dialog so that the user chooses one.
79  */
80 void Console::connect()
81 {
82    JCR jcr;
83
84    m_textEdit = textEdit;   /* our console screen */
85
86    if (!m_dir) {          
87       mainWin->set_status("No Director found.");
88       return;
89    }
90    if (m_sock) {
91       mainWin->set_status("Already connected.");
92       return;
93    }
94
95    memset(&jcr, 0, sizeof(jcr));
96
97    mainWin->set_statusf(_("Connecting to Director %s:%d"), m_dir->address, m_dir->DIRport);
98    display_textf(_("Connecting to Director %s:%d\n\n"), m_dir->address, m_dir->DIRport);
99
100    /* Give GUI a chance */
101    app->processEvents();
102    
103    LockRes();
104    /* If cons==NULL, default console will be used */
105    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
106    UnlockRes();
107
108    m_sock = bnet_connect(NULL, 5, 15, _("Director daemon"), m_dir->address,
109                           NULL, m_dir->DIRport, 0);
110    if (m_sock == NULL) {
111       mainWin->set_status("Connection failed");
112       return;
113    } else {
114       /* Update page selector to green to indicate that Console is connected */
115       mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/connected.png")));
116       QBrush greenBrush(Qt::green);
117       m_consoleItem->setForeground(0, greenBrush);
118    }
119
120    jcr.dir_bsock = m_sock;
121
122    if (!authenticate_director(&jcr, m_dir, cons)) {
123       display_text(m_sock->msg);
124       return;
125    }
126
127    /* Give GUI a chance */
128    app->processEvents();
129
130    mainWin->set_status(_("Initializing ..."));
131
132    /* Set up input notifier */
133    m_notifier = new QSocketNotifier(m_sock->fd, QSocketNotifier::Read, 0);
134    QObject::connect(m_notifier, SIGNAL(activated(int)), this, SLOT(read_dir(int)));
135
136    write(".api 1");
137    discardToPrompt();
138
139    beginNewCommand();
140    job_list = get_list(".jobs");
141    client_list = get_list(".clients");
142    fileset_list = get_list(".filesets");
143    messages_list = get_list(".messages");
144    pool_list = get_list(".pools");
145    storage_list = get_list(".storage");
146    type_list = get_list(".types");
147    level_list = get_list(".levels");
148
149    mainWin->set_status(_("Connected"));
150    return;
151 }
152
153
154 /*  
155  * Send a command to the director, and read all the resulting
156  *  output into a list.
157  */
158 QStringList Console::get_list(char *cmd)
159 {
160    QStringList list;
161    int stat;
162
163    notify(false);
164    write(cmd);
165    while ((stat = read()) > 0) {
166       strip_trailing_junk(msg());
167       list << msg();
168    }
169    notify(true);
170    return list;
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_consoleItem->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_consoleItem->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::setTreeItem(QTreeWidgetItem *item) 
577
578    m_consoleItem = item;
579 }
580
581 void Console::setDirRes(DIRRES *dir) 
582
583    m_dir = dir;
584 }