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