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