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