]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/console/console.cpp
This is a first commit at attempting to prevent connecting to a director which
[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(QString::fromUtf8("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(QString::fromUtf8("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 void Console::status_dir()
370 {
371    QString cmd("status dir");
372          consoleCommand(cmd);
373 }
374
375 /*
376  * Put text into the console window
377  */
378 void Console::display_textf(const char *fmt, ...)
379 {
380    va_list arg_ptr;
381    char buf[1000];
382    int len;
383    va_start(arg_ptr, fmt);
384    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
385    va_end(arg_ptr);
386    display_text(buf);
387 }
388
389 void Console::display_text(const QString buf)
390 {
391    m_cursor->movePosition(QTextCursor::End);
392    m_cursor->insertText(buf);
393 }
394
395
396 void Console::display_text(const char *buf)
397 {
398    m_cursor->movePosition(QTextCursor::End);
399    m_cursor->insertText(buf);
400 }
401
402 /* Position cursor to end of screen */
403 void Console::update_cursor()
404 {
405    QApplication::restoreOverrideCursor();
406    m_textEdit->moveCursor(QTextCursor::End);
407    m_textEdit->ensureCursorVisible();
408 }
409
410 /* 
411  * This should be moved into a bSocket class 
412  */
413 char *Console::msg()
414 {
415    if (m_sock) {
416       return m_sock->msg;
417    }
418    return NULL;
419 }
420
421 /* Send a command to the Director */
422 void Console::write_dir(const char *msg)
423 {
424    if (m_sock) {
425       mainWin->set_status(_("Processing command ..."));
426       QApplication::setOverrideCursor(Qt::WaitCursor);
427       write(msg);
428    } else {
429       mainWin->set_status(" Director not connected. Click on connect button.");
430       mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/disconnected.png")));
431       QBrush redBrush(Qt::red);
432       QTreeWidgetItem *item = mainWin->getFromHash(this);
433       item->setForeground(0, redBrush);
434       m_at_prompt = false;
435    }
436 }
437
438 int Console::write(const QString msg)
439 {
440    return write(msg.toUtf8().data());
441 }
442
443 int Console::write(const char *msg)
444 {
445    m_sock->msglen = pm_strcpy(m_sock->msg, msg);
446    m_at_prompt = false;
447    if (commDebug) Pmsg1(000, "send: %s\n", msg);
448    return m_sock->send();
449 }
450
451 /*
452  * Get to main command prompt 
453  */
454 void Console::beginNewCommand()
455 {
456    write(".\n");
457    while (read() > 0) {
458    }
459    write(".\n");
460    while (read() > 0) {
461    }
462    write(".\n");
463    while (read() > 0) {
464    }
465    display_text("\n");
466 }
467
468 void Console::displayToPrompt()
469
470    int stat;
471    if (commDebug) Pmsg0(000, "DisplaytoPrompt\n");
472    while (!m_at_prompt) {
473       if ((stat=read()) > 0) {
474          display_text(msg());
475       }
476    }
477    if (commDebug) Pmsg1(000, "endDisplaytoPrompt=%d\n", stat);
478 }
479
480 void Console::discardToPrompt()
481
482    int stat;
483    if (commDebug) Pmsg0(000, "discardToPrompt\n");
484    while (!m_at_prompt) {
485       stat = read();
486    }
487    if (commDebug) Pmsg1(000, "endDisplayToPrompt=%d\n", stat);
488 }
489
490
491 /* 
492  * Blocking read from director
493  */
494 int Console::read()
495 {
496    int stat = 0;
497    while (m_sock) {
498       for (;;) {
499          stat = bnet_wait_data_intr(m_sock, 1);
500          if (stat > 0) {
501             break;
502          } 
503          app->processEvents();
504          if (m_api_set && m_messages_pending) {
505             write_dir(".messages");
506             m_messages_pending = false;
507          }
508       }
509       stat = m_sock->recv();
510       if (stat >= 0) {
511          if (m_at_prompt) {
512             display_text("\n");
513             m_at_prompt = false;
514          }
515          if (commDebug) Pmsg1(000, "got: %s", m_sock->msg);
516       }
517       switch (m_sock->msglen) {
518       case BNET_SERVER_READY:
519          if (m_api_set && m_messages_pending) {
520             write_dir(".messages");
521             m_messages_pending = false;
522          }
523          m_at_prompt = true;
524          continue;
525       case BNET_MSGS_PENDING:
526          if (commDebug) Pmsg0(000, "MSGS PENDING\n");
527          m_messages_pending = true;
528          continue;
529       case BNET_CMD_OK:
530          if (commDebug) Pmsg0(000, "CMD OK\n");
531          m_at_prompt = false;
532          continue;
533       case BNET_CMD_BEGIN:
534          if (commDebug) Pmsg0(000, "CMD BEGIN\n");
535          m_at_prompt = false;
536          continue;
537       case BNET_PROMPT:
538          if (commDebug) Pmsg0(000, "PROMPT\n");
539          m_at_prompt = true;
540          mainWin->set_status(_("At prompt waiting for input ..."));
541          update_cursor();
542          QApplication::restoreOverrideCursor();
543          break;
544       case BNET_CMD_FAILED:
545          if (commDebug) Pmsg0(000, "CMD FAIL\n");
546          mainWin->set_status(_("Command failed. At prompt waiting for input ..."));
547          update_cursor();
548          QApplication::restoreOverrideCursor();
549          break;
550       /* We should not get this one */
551       case BNET_EOD:
552          if (commDebug) Pmsg0(000, "EOD\n");
553          mainWin->set_status_ready();
554          update_cursor();
555          QApplication::restoreOverrideCursor();
556          if (!m_api_set) {
557             break;
558          }
559          continue;
560       case BNET_START_SELECT:
561          new selectDialog(this);    
562          break;
563       case BNET_RUN_CMD:
564          new runCmdDialog(this);
565          break;
566       case BNET_ERROR_MSG:
567          m_sock->recv();              /* get the message */
568          display_text(msg());
569          QMessageBox::critical(this, "Error", msg(), QMessageBox::Ok);
570          break;
571       case BNET_WARNING_MSG:
572          m_sock->recv();              /* get the message */
573          display_text(msg());
574          QMessageBox::critical(this, "Warning", msg(), QMessageBox::Ok);
575          break;
576       case BNET_INFO_MSG:
577          m_sock->recv();              /* get the message */
578          display_text(msg());
579          mainWin->set_status(msg());
580          break;
581       }
582       if (is_bnet_stop(m_sock)) {         /* error or term request */
583          m_sock->close();
584          m_sock = NULL;
585          mainWin->actionConnect->setIcon(QIcon(QString::fromUtf8("images/disconnected.png")));
586          QBrush redBrush(Qt::red);
587          QTreeWidgetItem *item = mainWin->getFromHash(this);
588          item->setForeground(0, redBrush);
589          m_notifier->setEnabled(false);
590          delete m_notifier;
591          m_notifier = NULL;
592          mainWin->set_status(_("Director disconnected."));
593          QApplication::restoreOverrideCursor();
594          stat = BNET_HARDEOF;
595       }
596       break;
597    } 
598    return stat;
599 }
600
601 /* Called by signal when the Director has output for us */
602 void Console::read_dir(int fd)
603 {
604    int stat;
605    (void)fd;
606
607    if (commDebug) Pmsg0(000, "read_dir\n");
608    while ((stat = read()) >= 0) {
609       display_text(msg());
610    }
611 }
612
613 /*
614  * When the notifier is enabled, read_dir() will automatically be
615  * called by the Qt event loop when ever there is any output 
616  * from the Directory, and read_dir() will then display it on
617  * the console.
618  *
619  * When we are in a bat dialog, we want to control *all* output
620  * from the Directory, so we set notify to off.
621  *    m_console->notifiy(false);
622  */
623 void Console::notify(bool enable) 
624
625    m_notifier->setEnabled(enable);   
626 }
627
628 void Console::setDirectorTreeItem(QTreeWidgetItem *item)
629 {
630    m_directorTreeItem = item;
631 }
632
633 void Console::setDirRes(DIRRES *dir) 
634
635    m_dir = dir;
636 }
637
638 /*
639  * To have the ability to get the name of the director resource.
640  */
641 void Console::getDirResName(QString &name_returned)
642 {
643    name_returned = m_dir->name();
644 }
645
646 bool Console::is_connectedGui()
647 {
648    if (is_connected()) {
649       return true;
650    } else {
651       QString message("Director ");
652       message += m_dir->name();
653       message += " is curerntly disconnected\n  Please reconnect!!";
654       QMessageBox::warning(this, tr("Bat"),
655          tr(message.toUtf8().data()), QMessageBox::Ok );
656       return false;
657    }
658 }