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