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