]> git.sur5r.net Git - bacula/bacula/commitdiff
Add new QT traymonitor
authorEric Bollengier <eric@eb.homelinux.org>
Thu, 6 Jan 2011 15:54:34 +0000 (16:54 +0100)
committerKern Sibbald <kern@sibbald.com>
Sat, 20 Apr 2013 12:39:49 +0000 (14:39 +0200)
bacula/src/qt-console/tray-monitor/authenticate.cpp [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/tray-monitor.conf.in [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/tray-monitor.cpp [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/tray-monitor.h [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/tray-monitor.pro.in [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/tray-ui.h [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/tray_conf.cpp [new file with mode: 0644]
bacula/src/qt-console/tray-monitor/tray_conf.h [new file with mode: 0644]

diff --git a/bacula/src/qt-console/tray-monitor/authenticate.cpp b/bacula/src/qt-console/tray-monitor/authenticate.cpp
new file mode 100644 (file)
index 0000000..35a4d19
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2004-2008 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version three of the GNU Affero General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+/*
+ *
+ *   Bacula authentication. Provides authentication with
+ *     File and Storage daemons.
+ *
+ *     Nicolas Boichat, August MMIV
+ *
+ *    This routine runs as a thread and must be thread reentrant.
+ *
+ *  Basic tasks done here:
+ *
+ */
+
+#include "tray-monitor.h"
+#include "jcr.h"
+
+void senditf(const char *fmt, ...);
+void sendit(const char *buf);
+
+/* Commands sent to Director */
+static char DIRhello[]    = "Hello %s calling\n";
+
+/* Response from Director */
+static char DIROKhello[]   = "1000 OK:";
+
+/* Commands sent to Storage daemon and File daemon and received
+ *  from the User Agent */
+static char SDFDhello[]    = "Hello Director %s calling\n";
+
+/* Response from SD */
+static char SDOKhello[]   = "3000 OK Hello\n";
+/* Response from FD */
+static char FDOKhello[] = "2000 OK Hello";
+
+/* Forward referenced functions */
+
+/*
+ * Authenticate Director
+ */
+int authenticate_director(JCR *jcr, MONITOR *mon, DIRRES *director)
+{
+   BSOCK *dir = jcr->dir_bsock;
+   int tls_local_need = BNET_TLS_NONE;
+   int tls_remote_need = BNET_TLS_NONE;
+   int compatible = true;
+   char bashed_name[MAX_NAME_LENGTH];
+   char *password;
+
+   bstrncpy(bashed_name, mon->hdr.name, sizeof(bashed_name));
+   bash_spaces(bashed_name);
+   password = mon->password;
+
+   /* Timeout Hello after 5 mins */
+   btimer_t *tid = start_bsock_timer(dir, 60 * 5);
+   dir->fsend(DIRhello, bashed_name);
+
+   if (!cram_md5_respond(dir, password, &tls_remote_need, &compatible) ||
+       !cram_md5_challenge(dir, password, tls_local_need, compatible)) {
+      stop_bsock_timer(tid);
+      Jmsg0(jcr, M_FATAL, 0, _("Director authorization problem.\n"
+            "Most likely the passwords do not agree.\n"
+       "Please see http://www.bacula.org/en/rel-manual/Bacula_Freque_Asked_Questi.html#SECTION003760000000000000000 for help.\n"));
+      return 0;
+   }
+
+   Dmsg1(6, ">dird: %s", dir->msg);
+   if (dir->recv() <= 0) {
+      stop_bsock_timer(tid);
+      Jmsg1(jcr, M_FATAL, 0, _("Bad response to Hello command: ERR=%s\n"),
+         dir->bstrerror());
+      return 0;
+   }
+   Dmsg1(10, "<dird: %s", dir->msg);
+   stop_bsock_timer(tid);
+   if (strncmp(dir->msg, DIROKhello, sizeof(DIROKhello)-1) != 0) {
+      Jmsg0(jcr, M_FATAL, 0, _("Director rejected Hello command\n"));
+      return 0;
+   } else {
+      Jmsg0(jcr, M_INFO, 0, dir->msg);
+   }
+   return 1;
+}
+
+/*
+ * Authenticate Storage daemon connection
+ */
+int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store)
+{
+   BSOCK *sd = jcr->store_bsock;
+   char dirname[MAX_NAME_LENGTH];
+   int tls_local_need = BNET_TLS_NONE;
+   int tls_remote_need = BNET_TLS_NONE;
+   int compatible = true;
+
+   /*
+    * Send my name to the Storage daemon then do authentication
+    */
+   bstrncpy(dirname, monitor->hdr.name, sizeof(dirname));
+   bash_spaces(dirname);
+   /* Timeout Hello after 5 mins */
+   btimer_t *tid = start_bsock_timer(sd, 60 * 5);
+   if (!sd->fsend(SDFDhello, dirname)) {
+      stop_bsock_timer(tid);
+      Jmsg(jcr, M_FATAL, 0, _("Error sending Hello to Storage daemon. ERR=%s\n"), bnet_strerror(sd));
+      return 0;
+   }
+   if (!cram_md5_respond(sd, store->password, &tls_remote_need, &compatible) ||
+       !cram_md5_challenge(sd, store->password, tls_local_need, compatible)) {
+      stop_bsock_timer(tid);
+      Jmsg0(jcr, M_FATAL, 0, _("Director and Storage daemon passwords or names not the same.\n"
+       "Please see http://www.bacula.org/en/rel-manual/Bacula_Freque_Asked_Questi.html#SECTION003760000000000000000 for help.\n"));
+      return 0;
+   }
+   Dmsg1(116, ">stored: %s", sd->msg);
+   if (sd->recv() <= 0) {
+      stop_bsock_timer(tid);
+      Jmsg1(jcr, M_FATAL, 0, _("bdird<stored: bad response to Hello command: ERR=%s\n"),
+         sd->bstrerror());
+      return 0;
+   }
+   Dmsg1(110, "<stored: %s", sd->msg);
+   stop_bsock_timer(tid);
+   if (strncmp(sd->msg, SDOKhello, sizeof(SDOKhello)) != 0) {
+      Jmsg0(jcr, M_FATAL, 0, _("Storage daemon rejected Hello command\n"));
+      return 0;
+   }
+   return 1;
+}
+
+/*
+ * Authenticate File daemon connection
+ */
+int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client)
+{
+   BSOCK *fd = jcr->file_bsock;
+   char dirname[MAX_NAME_LENGTH];
+   int tls_local_need = BNET_TLS_NONE;
+   int tls_remote_need = BNET_TLS_NONE;
+   int compatible = true;
+
+   /*
+    * Send my name to the File daemon then do authentication
+    */
+   bstrncpy(dirname, monitor->hdr.name, sizeof(dirname));
+   bash_spaces(dirname);
+   /* Timeout Hello after 5 mins */
+   btimer_t *tid = start_bsock_timer(fd, 60 * 5);
+   if (!fd->fsend(SDFDhello, dirname)) {
+      stop_bsock_timer(tid);
+      Jmsg(jcr, M_FATAL, 0, _("Error sending Hello to File daemon. ERR=%s\n"), bnet_strerror(fd));
+      return 0;
+   }
+   if (!cram_md5_respond(fd, client->password, &tls_remote_need, &compatible) ||
+       !cram_md5_challenge(fd, client->password, tls_local_need, compatible)) {
+      stop_bsock_timer(tid);
+      Jmsg(jcr, M_FATAL, 0, _("Director and File daemon passwords or names not the same.\n"
+       "Please see http://www.bacula.org/en/rel-manual/Bacula_Freque_Asked_Questi.html#SECTION003760000000000000000 for help.\n"));
+      return 0;
+   }
+   Dmsg1(116, ">filed: %s", fd->msg);
+   if (fd->recv() <= 0) {
+      stop_bsock_timer(tid);
+      Jmsg(jcr, M_FATAL, 0, _("Bad response from File daemon to Hello command: ERR=%s\n"),
+         fd->bstrerror());
+      return 0;
+   }
+   Dmsg1(110, "<stored: %s", fd->msg);
+   stop_bsock_timer(tid);
+   if (strncmp(fd->msg, FDOKhello, sizeof(FDOKhello)-1) != 0) {
+      Jmsg(jcr, M_FATAL, 0, _("File daemon rejected Hello command\n"));
+      return 0;
+   }
+   return 1;
+}
diff --git a/bacula/src/qt-console/tray-monitor/tray-monitor.conf.in b/bacula/src/qt-console/tray-monitor/tray-monitor.conf.in
new file mode 100644 (file)
index 0000000..30aea7d
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# Bacula Tray Monitor Configuration File
+#
+
+Monitor {
+  Name = @basename@-mon
+  Password = "@mon_dir_password@"         # password for the Directors   
+  RefreshInterval = 30 seconds
+}
+   
+Client {
+  Name = @basename@-fd
+  Address = @hostname@
+  FDPort = @fd_port@
+  Password = "@mon_fd_password@"          # password for FileDaemon
+}
+
+Storage {
+  Name = @basename@-sd
+  Address = @hostname@
+  SDPort = @sd_port@
+  Password = "@mon_sd_password@"          # password for StorageDaemon
+}
+
+Director {
+  Name = @basename@-dir
+  DIRport = @dir_port@
+  address = @hostname@
+}
diff --git a/bacula/src/qt-console/tray-monitor/tray-monitor.cpp b/bacula/src/qt-console/tray-monitor/tray-monitor.cpp
new file mode 100644 (file)
index 0000000..9c64f48
--- /dev/null
@@ -0,0 +1,472 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2004-2011 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version three of the GNU Affero General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+
+#include "tray-ui.h"
+#include "tray-monitor.h"
+
+int doconnect(monitoritem* item);
+int docmd(monitoritem* item, const char* command);
+static int authenticate_daemon(monitoritem* item, JCR *jcr);
+/* Imported functions */
+int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
+int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
+int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
+extern bool parse_tmon_config(CONFIG *config, const char *configfile, int exit_code);
+void get_list(monitoritem* item, const char *cmd, QStringList &lst);
+
+/* Dummy functions */
+int generate_daemon_event(JCR *, const char *) { return 1; }
+
+/* Static variables */
+static char *configfile = NULL;
+static MONITOR *monitor;
+static JCR jcr;
+static int nitems = 0;
+static monitoritem items[32];
+static CONFIG *config;
+static TrayUI *tray;
+
+/* Data received from DIR/FD/SD */
+static char OKqstatus[]   = "%c000 OK .status\n";
+static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
+
+
+void updateStatusIcon(monitoritem* item);
+void changeStatusMessage(monitoritem* item, const char *fmt,...);
+
+#define CONFIG_FILE "./tray-monitor.conf"   /* default configuration file */
+
+static void usage()
+{
+   fprintf(stderr, _(
+PROG_COPYRIGHT
+"\nVersion: %s (%s) %s %s %s\n\n"
+"Usage: tray-monitor [-c config_file] [-d debug_level]\n"
+"       -c <file>     set configuration file to file\n"
+"       -d <nn>       set debug level to <nn>\n"
+"       -dt           print timestamp in debug output\n"
+"       -t            test - read configuration and exit\n"
+"       -?            print this message.\n"
+"\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
+}
+
+int sm_line = 0;
+
+void dotest()
+{
+   for (int i = 0; i < nitems; i++) {
+      const char *cmd;
+
+      switch (items[i].type) {
+      case R_DIRECTOR:
+         cmd = ".jobs type=B";
+         tray->clearText(items[i].get_name());
+         docmd(&items[i], cmd);
+         break;
+      default:
+         break;
+      }
+   }
+}
+
+void get_list(monitoritem *item, const char *cmd, QStringList &lst)
+{
+   int stat;
+   
+   doconnect(item);
+   item->writecmd(cmd);
+   while((stat = bnet_recv(item->D_sock)) >= 0) {
+      strip_trailing_junk(item->D_sock->msg);
+      if (*(item->D_sock->msg)) {
+         lst << QString(item->D_sock->msg);
+      }
+   }
+}
+
+void refresh_item()
+{
+   for (int i = 0; i < nitems; i++) {
+      const char *cmd;
+      tray->clearText(items[i].get_name());
+      switch (items[i].type) {
+      case R_DIRECTOR:
+         cmd = "status dir";
+         break;
+      case R_CLIENT:
+         cmd = "status";
+         break;
+      case R_STORAGE:
+         cmd = "status";
+         break;          
+      default:
+         exit(1);
+         break;
+      }
+      docmd(&items[i], cmd);
+   }
+}
+
+/*********************************************************************
+ *
+ *         Main Bacula Tray Monitor -- User Interface Program
+ *
+ */
+int main(int argc, char *argv[])
+{   
+   int ch, i, dir_index=-1;
+   bool test_config = false;
+   DIRRES* dird;
+   CLIENT* filed;
+   STORE* stored;
+
+   setlocale(LC_ALL, "");
+   bindtextdomain("bacula", LOCALEDIR);
+   textdomain("bacula");
+
+   init_stack_dump();
+   my_name_is(argc, argv, "tray-monitor");
+   lmgr_init_thread();
+   init_msg(NULL, NULL);
+   working_directory = "/tmp";
+
+   struct sigaction sigignore;
+   sigignore.sa_flags = 0;
+   sigignore.sa_handler = SIG_IGN;
+   sigfillset(&sigignore.sa_mask);
+   sigaction(SIGPIPE, &sigignore, NULL);
+
+   while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
+      switch (ch) {
+      case 'c':                    /* configuration file */
+         if (configfile != NULL) {
+            free(configfile);
+         }
+         configfile = bstrdup(optarg);
+         break;
+
+      case 'd':
+         if (*optarg == 't') {
+            dbg_timestamp = true;
+         } else {
+            debug_level = atoi(optarg);
+            if (debug_level <= 0) {
+               debug_level = 1;
+            }
+         }
+         break;
+
+      case 't':
+         test_config = true;
+         break;
+
+      case 'h':
+      case '?':
+      default:
+         usage();
+         exit(1);
+      }
+   }
+   argc -= optind;
+   //argv += optind;
+
+   if (argc) {
+      usage();
+      exit(1);
+   }
+
+   if (configfile == NULL) {
+      configfile = bstrdup(CONFIG_FILE);
+   }
+
+   config = new_config_parser();
+   parse_tmon_config(config, configfile, M_ERROR_TERM);
+
+   LockRes();
+   nitems = 0;
+   foreach_res(monitor, R_MONITOR) {
+      nitems++;
+   }
+
+   if (nitems != 1) {
+      Emsg2(M_ERROR_TERM, 0,
+         _("Error: %d Monitor resources defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
+   }
+
+   nitems = 0;
+   foreach_res(dird, R_DIRECTOR) {
+      dir_index=nitems;
+      items[nitems].type = R_DIRECTOR;
+      items[nitems].resource = dird;
+      items[nitems].D_sock = NULL;
+      items[nitems].state = warn;
+      items[nitems].oldstate = warn;
+      nitems++;
+   }
+   foreach_res(filed, R_CLIENT) {
+      items[nitems].type = R_CLIENT;
+      items[nitems].resource = filed;
+      items[nitems].D_sock = NULL;
+      items[nitems].state = warn;
+      items[nitems].oldstate = warn;
+      nitems++;
+   }
+   foreach_res(stored, R_STORAGE) {
+      items[nitems].type = R_STORAGE;
+      items[nitems].resource = stored;
+      items[nitems].D_sock = NULL;
+      items[nitems].state = warn;
+      items[nitems].oldstate = warn;
+      nitems++;
+   }
+   UnlockRes();
+
+   if (nitems == 0) {
+      Emsg1(M_ERROR_TERM, 0, _("No Client, Storage or Director resource defined in %s\n"
+"Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
+   }
+
+   if (test_config) {
+      exit(0);
+   }
+
+   (void)WSA_Init();                /* Initialize Windows sockets */
+
+   LockRes();
+   monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
+   UnlockRes();
+
+   if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
+      Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
+"This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
+   }
+
+   sm_line = 0;
+   QApplication    app(argc, argv);
+   app.setQuitOnLastWindowClosed(false);
+   tray = new TrayUI();
+   tray->setupUi(tray);
+   tray->spinRefresh->setValue(monitor->RefreshInterval);
+   if (dir_index >= 0) {
+      tray->addDirector(&items[dir_index]);
+   }
+
+   for (i = 0; i < nitems; i++) {
+      const char *cmd;
+      tray->addTab(items[i].get_name());
+      switch (items[i].type) {
+      case R_DIRECTOR:
+         tray->addDirector(&items[i]);
+         cmd = "status dir";
+         break;
+      case R_CLIENT:
+         cmd = "status";
+         break;
+      case R_STORAGE:
+         cmd = "status";
+         break;          
+      default:
+         exit(1);
+         break;
+      }
+      docmd(&items[i], cmd);
+   }
+
+   tray->startTimer();
+
+   app.exec();
+
+   for (i = 0; i < nitems; i++) {
+      if (items[i].D_sock) {
+         items[i].writecmd("quit");
+         if (items[i].D_sock) {
+            bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
+            bnet_close(items[i].D_sock);
+         }
+      }
+   }
+
+
+   (void)WSACleanup();               /* Cleanup Windows sockets */
+   
+   config->free_resources();
+   free(config);
+   config = NULL;
+   term_msg();
+   return 0;
+}
+
+static int authenticate_daemon(monitoritem* item, JCR *jcr) {
+   switch (item->type) {
+   case R_DIRECTOR:
+      return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
+   case R_CLIENT:
+      return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
+   case R_STORAGE:
+      return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
+   default:
+      printf(_("Error, currentitem is not a Client or a Storage..\n"));
+      return FALSE;
+   }
+   return false;
+}
+
+void changeStatusMessage(monitoritem*, const char *fmt,...) {
+   char buf[512];
+   va_list arg_ptr;
+
+   va_start(arg_ptr, fmt);
+   bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
+   va_end(arg_ptr);
+   tray->statusbar->showMessage(QString(buf));
+}
+
+int doconnect(monitoritem* item) 
+{
+   if (!item->D_sock) {
+      memset(&jcr, 0, sizeof(jcr));
+
+      DIRRES* dird;
+      CLIENT* filed;
+      STORE* stored;
+
+      switch (item->type) {
+      case R_DIRECTOR:
+         dird = (DIRRES*)item->resource;
+         changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
+         item->D_sock = bnet_connect(NULL, monitor->DIRConnectTimeout, 
+                                     0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
+         jcr.dir_bsock = item->D_sock;
+         break;
+      case R_CLIENT:
+         filed = (CLIENT*)item->resource;
+         changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
+         item->D_sock = bnet_connect(NULL, monitor->FDConnectTimeout, 
+                                     0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
+         jcr.file_bsock = item->D_sock;
+         break;
+      case R_STORAGE:
+         stored = (STORE*)item->resource;
+         changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
+         item->D_sock = bnet_connect(NULL, monitor->SDConnectTimeout, 
+                                     0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
+         jcr.store_bsock = item->D_sock;
+         break;
+      default:
+         printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
+         return 0;
+      }
+
+      if (item->D_sock == NULL) {
+         changeStatusMessage(item, _("Cannot connect to daemon."));
+         item->state = error;
+         item->oldstate = error;
+         return 0;
+      }
+
+      if (!authenticate_daemon(item, &jcr)) {
+         item->state = error;
+         item->oldstate = error;
+         changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
+         item->D_sock = NULL;
+         return 0;
+      }
+
+      switch (item->type) {
+      case R_DIRECTOR:
+         changeStatusMessage(item, _("Opened connection with Director daemon."));
+         break;
+      case R_CLIENT:
+         changeStatusMessage(item, _("Opened connection with File daemon."));
+         break;
+      case R_STORAGE:
+         changeStatusMessage(item, _("Opened connection with Storage daemon."));
+         break;
+      default:
+         printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
+         return 0;
+         break;
+      }
+
+      if (item->type == R_DIRECTOR) { /* Read connection messages... */
+         docmd(item, ""); /* Usually invalid, but no matter */
+      }
+   }
+   return 1;
+}
+
+int docmd(monitoritem* item, const char* command) 
+{
+   int stat;
+   //qDebug() << "docmd(" << item->get_name() << "," << command << ")";
+   if (!doconnect(item)) {
+      return 0;
+   }
+
+   if (command[0] != 0)
+      item->writecmd(command);
+
+   while(1) {
+      if ((stat = bnet_recv(item->D_sock)) >= 0) {
+         strip_trailing_newline(item->D_sock->msg);
+         tray->appendText(item->get_name(), item->D_sock->msg);
+      }
+      else if (stat == BNET_SIGNAL) {
+         if (item->D_sock->msglen == BNET_EOD) {
+            // qDebug() << "<< EOD >>";
+            return 1;
+         }
+         else if (item->D_sock->msglen == BNET_SUB_PROMPT) {
+            // qDebug() << "<< PROMPT >>";
+            return 0;
+         }
+         else if (item->D_sock->msglen == BNET_HEARTBEAT) {
+            bnet_sig(item->D_sock, BNET_HB_RESPONSE);
+         }
+         else {
+            qDebug() << bnet_sig_to_ascii(item->D_sock);
+         }
+      }
+      else { /* BNET_HARDEOF || BNET_ERROR */
+         item->D_sock = NULL;
+         item->state = error;
+         item->oldstate = error;
+         changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
+         //fprintf(stderr, _("<< ERROR >>\n"));
+         return 0;
+      }
+
+      if (is_bnet_stop(item->D_sock)) {
+         item->D_sock = NULL;
+         item->state = error;
+         item->oldstate = error;
+         changeStatusMessage(item, _("Error : Connection closed."));
+         //fprintf(stderr, "<< STOP >>\n");
+         return 0;            /* error or term */
+      }
+   }
+}
diff --git a/bacula/src/qt-console/tray-monitor/tray-monitor.h b/bacula/src/qt-console/tray-monitor/tray-monitor.h
new file mode 100644 (file)
index 0000000..7eba3df
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2004-2011 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version three of the GNU Affero General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+/*
+ * Includes specific to the tray monitor
+ *
+ *     Nicolas Boichat, August MMIV
+ *
+ */
+
+#ifndef TRAY_MONITOR_H
+#define TRAY_MONITOR_H
+
+#include <QString>
+#include <QStringList>
+#include "bacula.h"
+#include "tray_conf.h"
+#include "jcr.h"
+
+struct job_defaults {
+   QString job_name;
+   QString pool_name;
+   QString messages_name;
+   QString client_name;
+   QString store_name;
+   QString where;
+   QString level;
+   QString type;
+   QString fileset_name;
+   QString catalog_name;
+   bool enabled;
+};
+
+struct resources {
+   QStringList job_list;
+   QStringList pool_list;
+   QStringList client_list;
+   QStringList storage_list;
+   QStringList levels;
+   QStringList fileset_list;
+   QStringList messages_list;
+};
+
+enum stateenum {
+   idle = 0,
+   running = 1,
+   warn = 2,
+   error = 3
+};
+
+class monitoritem;
+int doconnect(monitoritem* item);
+void get_list(monitoritem* item, const char *cmd, QStringList &lst);
+
+class  monitoritem {
+public:
+   rescode type; /* R_DIRECTOR, R_CLIENT or R_STORAGE */
+   void* resource; /* DIRRES*, CLIENT* or STORE* */
+   BSOCK *D_sock;
+   stateenum state;
+   stateenum oldstate;
+
+   char *get_name() {
+      return ((URES*)resource)->hdr.name;
+   }
+
+   void writecmd(const char* command) {
+      if (this->D_sock) {
+         this->D_sock->msglen = pm_strcpy(&this->D_sock->msg, command);
+         bnet_send(this->D_sock);
+      }
+   }
+
+   bool get_job_defaults(struct job_defaults &job_defs)
+   {
+      int stat;
+      char *def;
+      BSOCK *dircomm;
+      bool rtn = false;
+      QString scmd = QString(".defaults job=\"%1\"").arg(job_defs.job_name);
+
+      if (job_defs.job_name == "") {
+         return rtn;
+      }
+
+      if (!doconnect(this)) {
+         return rtn;
+      }
+      dircomm = this->D_sock;
+      dircomm->fsend("%s", scmd.toUtf8().data());
+
+      while ((stat = dircomm->recv()) > 0) {
+         def = strchr(dircomm->msg, '=');
+         if (!def) {
+            continue;
+         }
+         /* Pointer to default value */
+         *def++ = 0;
+         strip_trailing_junk(def);
+         
+         if (strcmp(dircomm->msg, "job") == 0) {
+            if (strcmp(def, job_defs.job_name.toUtf8().data()) != 0) {
+               goto bail_out;
+            }
+            continue;
+         }
+         if (strcmp(dircomm->msg, "pool") == 0) {
+            job_defs.pool_name = def;
+            continue;
+         }
+         if (strcmp(dircomm->msg, "messages") == 0) {
+            job_defs.messages_name = def;
+            continue;
+         }
+         if (strcmp(dircomm->msg, "client") == 0) {
+            job_defs.client_name = def;
+            continue;
+         }
+         if (strcmp(dircomm->msg, "storage") == 0) {
+            job_defs.store_name = def;
+            continue;
+         }
+         if (strcmp(dircomm->msg, "where") == 0) {
+            job_defs.where = def;
+            continue;
+         }
+         if (strcmp(dircomm->msg, "level") == 0) {
+            job_defs.level = def;
+            continue;
+         }
+         if (strcmp(dircomm->msg, "type") == 0) {
+            job_defs.type = def;
+            continue;
+         }
+         if (strcmp(dircomm->msg, "fileset") == 0) {
+            job_defs.fileset_name = def;
+            continue;
+         }
+         if (strcmp(dircomm->msg, "catalog") == 0) {
+            job_defs.catalog_name = def;
+            continue;
+         }
+         if (strcmp(dircomm->msg, "enabled") == 0) {
+            job_defs.enabled = *def == '1' ? true : false;
+            continue;
+         }
+      }
+      rtn = true;
+      /* Fall through wanted */
+   bail_out:
+      return rtn;
+   }
+};
+
+#endif  /* TRAY_MONITOR_H */
diff --git a/bacula/src/qt-console/tray-monitor/tray-monitor.pro.in b/bacula/src/qt-console/tray-monitor/tray-monitor.pro.in
new file mode 100644 (file)
index 0000000..ab60692
--- /dev/null
@@ -0,0 +1,46 @@
+######################################################################
+# Version $Id$
+#
+#   !!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+#
+#      Edit only bat.pro.in  -- bat.pro is built by the ./configure program
+#
+#   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+#    
+CONFIG += qt debug 
+
+cross-win32 {
+#  LIBS       +=  ../../win32/dll/bacula.a
+  LIBS       +=  -mwindows -L../win32/release32 -lbacula
+}
+!cross-win32 {
+  LIBS        += -L../../lib -lbaccfg -lbac -L../findlib -lbacfind @OPENSSL_LIBS@
+}
+
+
+bins.path = /$(DESTDIR)@sbindir@
+bins.files = bacula-tray-monitor
+confs.path = /$(DESTDIR)@sysconfdir@
+confs.commands = ./install_conf_file
+
+TEMPLATE     = app
+TARGET       = bacula-tray-monitor
+DEPENDPATH  += .
+INCLUDEPATH += ../.. . 
+LIBTOOL_LINK = @QMAKE_LIBTOOL@ --silent --tag=CXX --mode=link
+LIBTOOL_INSTALL = @QMAKE_LIBTOOL@ --silent --mode=install
+QMAKE_LINK   = $${LIBTOOL_LINK} $(CXX)
+QMAKE_INSTALL_PROGRAM = $${LIBTOOL_INSTALL} install -m @SBINPERM@ -p
+QMAKE_CLEAN += .libs/* bacula-tray-monitor release/bacula-tray-monitor
+
+RESOURCES    = ../main.qrc
+MOC_DIR      = moc
+OBJECTS_DIR  = obj
+UI_DIR       = ui
+
+# Main directory
+HEADERS += tray_conf.h  tray-monitor.h  tray-ui.h 
+SOURCES += authenticate.cpp  tray_conf.cpp  tray-monitor.cpp 
+
+FORMS += ../run/run.ui
+
diff --git a/bacula/src/qt-console/tray-monitor/tray-ui.h b/bacula/src/qt-console/tray-monitor/tray-ui.h
new file mode 100644 (file)
index 0000000..9b77440
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2011-2011 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version three of the GNU Affero General Public
+   License as published by the Free Software Foundation, which is 
+   listed in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+
+#ifndef TRAYUI_H
+#define TRAYUI_H
+
+#include <QAction>
+#include <QApplication>
+#include <QButtonGroup>
+#include <QDialogButtonBox>
+#include <QHeaderView>
+#include <QMainWindow>
+#include <QPlainTextEdit>
+#include <QStatusBar>
+#include <QTabWidget>
+#include <QVBoxLayout>
+#include <QWidget>
+#include <QLabel>
+#include <QSpinBox>
+#include <QMenu>
+#include <QIcon>
+#include <QSystemTrayIcon>
+#include <QTimer>
+#include <QDebug>
+#include <QMessageBox>
+#include <QFont>
+#include "version.h"
+#include "ui/ui_run.h"
+#include "tray-monitor.h"
+
+
+class RunDlg: public QDialog, public Ui::runForm
+{
+   Q_OBJECT
+
+public:
+   monitoritem *item;
+
+   void fill(QComboBox *cb, QStringList &lst) {
+      if (lst.length()) {
+         cb->addItems(lst);
+      } else {
+         cb->setEnabled(false);
+      }
+   }
+   RunDlg(monitoritem *i) {
+      struct resources res;
+      struct job_defaults jdefault;
+      QDateTime dt;
+      item = i;
+
+      qDebug() << "start getting elements";
+      get_list(item, ".jobs type=B", res.job_list);
+      
+      if (res.job_list.length() == 0) {
+         QMessageBox msgBox;
+         msgBox.setText("This restricted console doesn't have access to Backup jobs");
+         msgBox.setIcon(QMessageBox::Warning);
+         msgBox.exec();
+         this->deleteLater();
+         return;
+      }
+
+      get_list(item, ".pools", res.pool_list);
+      get_list(item, ".clients", res.client_list);
+      get_list(item, ".storage", res.storage_list);
+      res.levels << "Full" << "Incremental" << "Differential";
+      get_list(item, ".filesets", res.fileset_list);
+      get_list(item, ".messages", res.messages_list);
+
+      setupUi(this);
+
+      qDebug() << "  -> done";
+      label_5->setVisible(false);
+      bootstrap->setVisible(false);
+      jobCombo->addItems(res.job_list);
+      fill(filesetCombo, res.fileset_list);
+      fill(levelCombo, res.levels);
+      fill(clientCombo, res.client_list);
+      fill(poolCombo, res.pool_list);
+      fill(storageCombo, res.storage_list);
+      dateTimeEdit->setDateTime(dt.currentDateTime());
+      fill(messagesCombo, res.messages_list);
+      messagesCombo->setEnabled(false);
+      job_name_change(0);
+      connect(jobCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(job_name_change(int)));
+      connect(cancelButton, SIGNAL(pressed()), this, SLOT(deleteLater()));
+      connect(okButton, SIGNAL(pressed()), this, SLOT(okButtonPushed()));
+      show();
+   }
+
+private slots:
+
+   void okButtonPushed()
+   {
+      QString cmd;
+      cmd = "run";
+      if (jobCombo->isEnabled()) {
+         cmd += " job=\"" + jobCombo->currentText() + "\"" ;
+      }
+      if (filesetCombo->isEnabled()) {
+         cmd += " fileset=\"" + filesetCombo->currentText() + "\"";
+      }
+      cmd += " level=\"" + levelCombo->currentText() + "\"";
+      if (clientCombo->isEnabled()) {
+         cmd += " client=\"" + clientCombo->currentText() + "\"" ;
+      }
+      if (poolCombo->isEnabled()) {
+         cmd += " pool=\"" + poolCombo->currentText() + "\"";
+      }
+      if (storageCombo->isEnabled()) {
+         cmd += " storage=\"" + storageCombo->currentText() + "\"";
+      }
+      cmd += " priority=\"" + QString().setNum(prioritySpin->value()) + "\"";
+      cmd += " when=\"" + dateTimeEdit->dateTime().toString("yyyy-MM-dd hh:mm:ss") + "\"";
+#ifdef xxx
+      " messages=\"" << messagesCombo->currentText() << "\"";
+     /* FIXME when there is an option to modify the messages resoruce associated
+      * with a  job */
+#endif
+      cmd += " yes";
+      qDebug() << cmd;
+      item->D_sock->fsend("%s", cmd.toUtf8().data());
+      QString output;
+      while(bnet_recv(item->D_sock) >= 0) {output += item->D_sock->msg;}
+      QMessageBox msgBox;
+      msgBox.setText(output);
+      msgBox.exec();
+      deleteLater();
+   }
+
+   void job_name_change(int)
+   {
+      job_defaults job_defs;
+      job_defs.job_name = jobCombo->currentText();
+
+      if (item->get_job_defaults(job_defs)) {
+         typeLabel->setText("<H3>"+job_defs.type+"</H3>");
+         filesetCombo->setCurrentIndex(filesetCombo->findText(job_defs.fileset_name, Qt::MatchExactly));
+         levelCombo->setCurrentIndex(levelCombo->findText(job_defs.level, Qt::MatchExactly));
+         clientCombo->setCurrentIndex(clientCombo->findText(job_defs.client_name, Qt::MatchExactly));
+         poolCombo->setCurrentIndex(poolCombo->findText(job_defs.pool_name, Qt::MatchExactly));
+         storageCombo->setCurrentIndex(storageCombo->findText(job_defs.store_name, Qt::MatchExactly));
+         messagesCombo->setCurrentIndex(messagesCombo->findText(job_defs.messages_name, Qt::MatchExactly));
+
+      } else {
+
+      }
+   }
+};
+
+void refresh_item();
+void dotest();
+
+class TrayUI: public QMainWindow
+{
+   Q_OBJECT
+
+public:
+    QWidget *centralwidget;
+    QTabWidget *tabWidget;
+    QStatusBar *statusbar;
+    QHash<QString, QPlainTextEdit*> hash;
+    monitoritem* director;
+
+    QSystemTrayIcon *tray;
+    QSpinBox *spinRefresh;
+    QTimer *timer;
+
+    QPlainTextEdit *getTextEdit(char *title)
+    {
+       return hash.value(QString(title));
+    }
+
+    void clearText(char *title) 
+    {
+       QPlainTextEdit *w = getTextEdit(title);
+       if (!w) {
+          return;
+       }
+       w->clear();
+    }
+
+    void appendText(char *title, char *line) 
+    {
+       QPlainTextEdit *w = getTextEdit(title);
+       if (!w) {
+          return;
+       }
+       w->appendPlainText(QString(line));
+    }
+
+    void addDirector(monitoritem *item)
+    {
+       director = item;
+    }
+
+    void addTab(char *title)
+    {
+       QString t = QString(title);
+       QWidget *tab = new QWidget();
+       QVBoxLayout *vLayout = new QVBoxLayout(tab);
+       QPlainTextEdit *plainTextEdit = new QPlainTextEdit(tab);
+       plainTextEdit->setObjectName(t);
+       plainTextEdit->setReadOnly(true);
+       plainTextEdit->setFont(QFont("courier"));
+       vLayout->addWidget(plainTextEdit);
+       hash.insert(t, plainTextEdit);
+       tabWidget->addTab(tab, t);
+    }
+
+    void startTimer()
+    {
+       if (!timer) {
+          timer = new QTimer(this);
+          connect(timer, SIGNAL(timeout()), this, SLOT(refresh_screen()));
+       }
+       timer->start(spinRefresh->value()*1000);
+    }
+
+    void setupUi(QMainWindow *TrayMonitor)
+    {
+        timer = NULL;
+        director = NULL;
+        if (TrayMonitor->objectName().isEmpty())
+            TrayMonitor->setObjectName(QString::fromUtf8("TrayMonitor"));
+        TrayMonitor->setWindowIcon(QIcon(":/images/cartridge1.png")); 
+        TrayMonitor->resize(789, 595);
+        centralwidget = new QWidget(TrayMonitor);
+        centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
+        QVBoxLayout *verticalLayout = new QVBoxLayout(centralwidget);
+        verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
+        tabWidget = new QTabWidget(centralwidget);
+        tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
+        tabWidget->setTabPosition(QTabWidget::North);
+        tabWidget->setTabShape(QTabWidget::Rounded);
+        tabWidget->setTabsClosable(false);
+        verticalLayout->addWidget(tabWidget);
+
+        QDialogButtonBox *buttonBox = new QDialogButtonBox(centralwidget);
+        buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
+        buttonBox->setStandardButtons(QDialogButtonBox::Close);
+        connect(buttonBox, SIGNAL(rejected()), this, SLOT(cb_show()));
+
+        TrayMonitor->setCentralWidget(centralwidget);
+        statusbar = new QStatusBar(TrayMonitor);
+        statusbar->setObjectName(QString::fromUtf8("statusbar"));
+        TrayMonitor->setStatusBar(statusbar);
+
+        QHBoxLayout *hLayout = new QHBoxLayout();
+        QLabel *refreshlabel = new QLabel(centralwidget);
+        refreshlabel->setText("Refresh:");
+        hLayout->addWidget(refreshlabel);
+        spinRefresh = new QSpinBox(centralwidget);
+        QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+        sizePolicy.setHorizontalStretch(0);
+        sizePolicy.setVerticalStretch(0);
+        sizePolicy.setHeightForWidth(spinRefresh->sizePolicy().hasHeightForWidth());
+        spinRefresh->setSizePolicy(sizePolicy);
+        spinRefresh->setMinimum(1);
+        spinRefresh->setMaximum(600);
+        spinRefresh->setSingleStep(10);
+        spinRefresh->setValue(60);
+        hLayout->addWidget(spinRefresh);
+        hLayout->addWidget(buttonBox);
+
+        verticalLayout->addLayout(hLayout);
+
+        tray = new QSystemTrayIcon(TrayMonitor);
+        QMenu* stmenu = new QMenu(TrayMonitor);
+
+        QAction *actShow = new QAction(QApplication::translate("TrayMonitor",
+                               "Display",
+                                0, QApplication::UnicodeUTF8),TrayMonitor);
+        QAction* actQuit = new QAction(QApplication::translate("TrayMonitor",
+                               "Quit",
+                                0, QApplication::UnicodeUTF8),TrayMonitor);
+        QAction* actAbout = new QAction(QApplication::translate("TrayMonitor",
+                               "About",
+                                0, QApplication::UnicodeUTF8),TrayMonitor);
+       QAction* actRun = new QAction(QApplication::translate("TrayMonitor",
+                               "Run...",
+                                0, QApplication::UnicodeUTF8),TrayMonitor);
+       stmenu->addAction(actShow);
+        stmenu->addAction(actRun);
+        stmenu->addAction(actAbout);
+        stmenu->addSeparator();
+       stmenu->addAction(actQuit);
+        
+        connect(actRun, SIGNAL(triggered()), this, SLOT(cb_run()));
+        connect(actShow, SIGNAL(triggered()), this, SLOT(cb_show()));
+        connect(actQuit, SIGNAL(triggered()), this, SLOT(cb_quit()));
+        connect(actAbout, SIGNAL(triggered()), this, SLOT(cb_about()));
+        connect(spinRefresh, SIGNAL(valueChanged(int)), this, SLOT(cb_refresh(int)));
+        connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
+                this, SLOT(cb_trayIconActivated(QSystemTrayIcon::ActivationReason)));
+
+       tray->setContextMenu(stmenu);
+       QIcon icon(":/images/cartridge1.png");
+       tray->setIcon(icon);
+        tray->setToolTip(QString("Bacula Tray Monitor"));
+       tray->show();
+
+        retranslateUi(TrayMonitor);
+        QMetaObject::connectSlotsByName(TrayMonitor);
+    } // setupUi
+
+    void retranslateUi(QMainWindow *TrayMonitor)
+    {
+       TrayMonitor->setWindowTitle(QApplication::translate("TrayMonitor", "Bacula Tray Monitor", 0, QApplication::UnicodeUTF8));
+    } // retranslateUi
+
+private slots:
+    void cb_quit() {
+       QApplication::quit();
+    }
+
+    void cb_refresh(int val) {
+       if (timer) {
+          timer->setInterval(val*1000);
+       }
+    }
+
+    void cb_about() {
+       QMessageBox::about(this, "Bacula Tray Monitor", "Bacula Tray Monitor\n"
+                          "For more information, see: www.baculasystems.com\n"
+                          "Copyright (C) 1999-2010, Bacula Systems(R) SA\n"
+                          "Licensed under GNU AGPLv3.");
+    }
+
+    void cb_run() {
+       if (director) {
+          RunDlg *runbox = new RunDlg(director);
+          runbox->show();
+       }
+    }
+
+    void cb_trayIconActivated(QSystemTrayIcon::ActivationReason r) {
+       if (r == QSystemTrayIcon::Trigger) {
+          cb_show();
+       }
+    }
+
+    void refresh_screen() {
+//       qDebug() << "refresh_screen()";
+       if (isVisible()) {
+          refresh_item();
+//          qDebug() << "  -> OK";
+       }
+    }
+
+    void cb_show() {
+       if (isVisible()) {
+          hide();
+       } else {
+          refresh_item();
+          show();
+       }
+    }
+};
+
+
+#endif  /* TRAYUI_H */
diff --git a/bacula/src/qt-console/tray-monitor/tray_conf.cpp b/bacula/src/qt-console/tray-monitor/tray_conf.cpp
new file mode 100644 (file)
index 0000000..c56fdf7
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2004-2011 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version three of the GNU Affero General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+/*
+*   Main configuration file parser for Bacula Tray Monitor.
+*
+*   Adapted from dird_conf.c
+*
+*   Note, the configuration file parser consists of three parts
+*
+*   1. The generic lexical scanner in lib/lex.c and lib/lex.h
+*
+*   2. The generic config  scanner in lib/parse_config.c and
+*       lib/parse_config.h.
+*       These files contain the parser code, some utility
+*       routines, and the common store routines (name, int,
+*       string).
+*
+*   3. The daemon specific file, which contains the Resource
+*       definitions as well as any specific store routines
+*       for the resource records.
+*
+*     Nicolas Boichat, August MMIV
+*
+*/
+
+#include "bacula.h"
+#include "tray_conf.h"
+
+/* Define the first and last resource ID record
+* types. Note, these should be unique for each
+* daemon though not a requirement.
+*/
+int32_t r_first = R_FIRST;
+int32_t r_last  = R_LAST;
+static RES *sres_head[R_LAST - R_FIRST + 1];
+RES **res_head = sres_head;
+
+/* We build the current resource here as we are
+* scanning the resource configuration definition,
+* then move it to allocated memory when the resource
+* scan is complete.
+*/
+URES res_all;
+int32_t res_all_size = sizeof(res_all);
+
+
+/* Definition of records permitted within each
+* resource with the routine to process the record
+* information.  NOTE! quoted names must be in lower case.
+*/
+/*
+*    Monitor Resource
+*
+*   name           handler     value                 code flags    default_value
+*/
+static RES_ITEM mon_items[] = {
+   {"name",        store_name,     ITEM(res_monitor.hdr.name), 0, ITEM_REQUIRED, 0},
+   {"description", store_str,      ITEM(res_monitor.hdr.desc), 0, 0, 0},
+   {"requiressl",  store_bool,     ITEM(res_monitor.require_ssl), 1, ITEM_DEFAULT, 0},
+   {"password",    store_password, ITEM(res_monitor.password), 0, ITEM_REQUIRED, 0},
+   {"refreshinterval",  store_time,ITEM(res_monitor.RefreshInterval),    0, ITEM_DEFAULT, 60},
+   {"fdconnecttimeout", store_time,ITEM(res_monitor.FDConnectTimeout),   0, ITEM_DEFAULT, 10},
+   {"sdconnecttimeout", store_time,ITEM(res_monitor.SDConnectTimeout),   0, ITEM_DEFAULT, 10},
+   {"dirconnecttimeout", store_time,ITEM(res_monitor.DIRConnectTimeout), 0, ITEM_DEFAULT, 10},
+   {NULL, NULL, {0}, 0, 0, 0}
+};
+
+/*  Director's that we can contact */
+static RES_ITEM dir_items[] = {
+   {"name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
+   {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
+   {"dirport",     store_pint32,   ITEM(res_dir.DIRport),  0, ITEM_DEFAULT, 9101},
+   {"address",     store_str,      ITEM(res_dir.address),  0, ITEM_REQUIRED, 0},
+   {"enablessl",   store_bool,     ITEM(res_dir.enable_ssl), 1, ITEM_DEFAULT, 0},
+   {NULL, NULL, {0}, 0, 0, 0}
+};
+
+/*
+*    Client or File daemon resource
+*
+*   name           handler     value                 code flags    default_value
+*/
+
+static RES_ITEM cli_items[] = {
+   {"name",     store_name,       ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
+   {"description", store_str,     ITEM(res_client.hdr.desc), 0, 0, 0},
+   {"address",  store_str,        ITEM(res_client.address),  0, ITEM_REQUIRED, 0},
+   {"fdport",   store_pint32,     ITEM(res_client.FDport),   0, ITEM_DEFAULT, 9102},
+   {"password", store_password,   ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
+   {"enablessl", store_bool,      ITEM(res_client.enable_ssl), 1, ITEM_DEFAULT, 0},
+   {NULL, NULL, {0}, 0, 0, 0}
+};
+
+/* Storage daemon resource
+*
+*   name           handler     value                 code flags    default_value
+*/
+static RES_ITEM store_items[] = {
+   {"name",        store_name,     ITEM(res_store.hdr.name),   0, ITEM_REQUIRED, 0},
+   {"description", store_str,      ITEM(res_store.hdr.desc),   0, 0, 0},
+   {"sdport",      store_pint32,   ITEM(res_store.SDport),     0, ITEM_DEFAULT, 9103},
+   {"address",     store_str,      ITEM(res_store.address),    0, ITEM_REQUIRED, 0},
+   {"sdaddress",   store_str,      ITEM(res_store.address),    0, 0, 0},
+   {"password",    store_password, ITEM(res_store.password),   0, ITEM_REQUIRED, 0},
+   {"sdpassword",  store_password, ITEM(res_store.password),   0, 0, 0},
+   {"enablessl",   store_bool,     ITEM(res_store.enable_ssl),  1, ITEM_DEFAULT, 0},
+   {NULL, NULL, {0}, 0, 0, 0}
+};
+
+static RES_ITEM con_font_items[] = {
+   {"name",        store_name,     ITEM(con_font.hdr.name), 0, ITEM_REQUIRED, 0},
+   {"description", store_str,      ITEM(con_font.hdr.desc), 0, 0, 0},
+   {"font",        store_str,      ITEM(con_font.fontface), 0, 0, 0},
+   {NULL, NULL, {0}, 0, 0, 0}
+};
+
+/*
+* This is the master resource definition.
+* It must have one item for each of the resources.
+*
+*  NOTE!!! keep it in the same order as the R_codes
+*    or eliminate all resources[rindex].name
+*
+*  name      items        rcode        res_head
+*/
+RES_TABLE resources[] = {
+   {"monitor",      mon_items,    R_MONITOR},
+   {"director",     dir_items,    R_DIRECTOR},
+   {"client",       cli_items,    R_CLIENT},
+   {"storage",      store_items,  R_STORAGE},
+   {"consolefont",   con_font_items, R_CONSOLE_FONT},
+   {NULL,           NULL,         0}
+};
+
+/* Dump contents of resource */
+void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
+{
+   URES *res = (URES *)reshdr;
+   bool recurse = true;
+   char ed1[100], ed2[100];
+
+   if (res == NULL) {
+      sendit(sock, _("No %s resource defined\n"), res_to_str(type));
+      return;
+   }
+   if (type < 0) {                    /* no recursion */
+      type = - type;
+      recurse = false;
+   }
+   switch (type) {
+   case R_MONITOR:
+      sendit(sock, _("Monitor: name=%s FDtimeout=%s SDtimeout=%s\n"), 
+             reshdr->name,
+             edit_uint64(res->res_monitor.FDConnectTimeout, ed1),
+             edit_uint64(res->res_monitor.SDConnectTimeout, ed2));
+      break;
+   case R_DIRECTOR:
+      sendit(sock, _("Director: name=%s address=%s FDport=%d\n"),
+             res->res_dir.hdr.name, res->res_dir.address, res->res_dir.DIRport);
+      break;
+   case R_CLIENT:
+      sendit(sock, _("Client: name=%s address=%s FDport=%d\n"),
+             res->res_client.hdr.name, res->res_client.address, res->res_client.FDport);
+      break;
+   case R_STORAGE:
+      sendit(sock, _("Storage: name=%s address=%s SDport=%d\n"),
+             res->res_store.hdr.name, res->res_store.address, res->res_store.SDport);
+      break;
+   case R_CONSOLE_FONT:
+      sendit(sock, _("ConsoleFont: name=%s font face=%s\n"),
+             reshdr->name, NPRT(res->con_font.fontface));
+      break;
+   default:
+      sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
+      break;
+   }
+   if (recurse && res->res_monitor.hdr.next) {
+      dump_resource(type, res->res_monitor.hdr.next, sendit, sock);
+   }
+}
+
+
+/*
+* Free memory of resource -- called when daemon terminates.
+* NB, we don't need to worry about freeing any references
+* to other resources as they will be freed when that
+* resource chain is traversed.  Mainly we worry about freeing
+* allocated strings (names).
+*/
+void free_resource(RES *sres, int type)
+{
+   RES *nres;                         /* next resource if linked */
+   URES *res = (URES *)sres;
+
+   if (res == NULL)
+      return;
+
+   /* common stuff -- free the resource name and description */
+   nres = (RES *)res->res_monitor.hdr.next;
+   if (res->res_monitor.hdr.name) {
+      free(res->res_monitor.hdr.name);
+   }
+   if (res->res_monitor.hdr.desc) {
+      free(res->res_monitor.hdr.desc);
+   }
+
+   switch (type) {
+   case R_MONITOR:
+      break;
+   case R_CLIENT:
+      if (res->res_client.address) {
+         free(res->res_client.address);
+      }
+      if (res->res_client.password) {
+         free(res->res_client.password);
+      }
+      break;
+   case R_STORAGE:
+      if (res->res_store.address) {
+         free(res->res_store.address);
+      }
+      if (res->res_store.password) {
+         free(res->res_store.password);
+      }
+      break;
+   case R_CONSOLE_FONT:
+      if (res->con_font.fontface) {
+         free(res->con_font.fontface);
+      }
+      break;
+   default:
+      printf(_("Unknown resource type %d in free_resource.\n"), type);
+   }
+
+   /* Common stuff again -- free the resource, recurse to next one */
+   if (res) {
+      free(res);
+   }
+   if (nres) {
+      free_resource(nres, type);
+   }
+}
+
+/*
+* Save the new resource by chaining it into the head list for
+* the resource. If this is pass 2, we update any resource
+* pointers because they may not have been defined until
+* later in pass 1.
+*/
+void save_resource(int type, RES_ITEM *items, int pass)
+{
+   URES *res;
+   int rindex = type - r_first;
+   int i, size;
+   int error = 0;
+
+   /*
+   * Ensure that all required items are present
+   */
+   for (i=0; items[i].name; i++) {
+      if (items[i].flags & ITEM_REQUIRED) {
+         if (!bit_is_set(i, res_all.res_monitor.hdr.item_present)) {
+               Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
+                  items[i].name, resources[rindex]);
+         }
+      }
+      /* If this triggers, take a look at lib/parse_conf.h */
+      if (i >= MAX_RES_ITEMS) {
+         Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
+      }
+   }
+
+   /*
+   * During pass 2 in each "store" routine, we looked up pointers
+   * to all the resources referrenced in the current resource, now we
+   * must copy their addresses from the static record to the allocated
+   * record.
+   */
+   if (pass == 2) {
+      switch (type) {
+      /* Resources not containing a resource */
+      case R_MONITOR:
+      case R_CLIENT:
+      case R_STORAGE:
+      case R_DIRECTOR:
+      case R_CONSOLE_FONT:
+         break;
+      default:
+         Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
+         error = 1;
+         break;
+      }
+      /* Note, the resource name was already saved during pass 1,
+      * so here, we can just release it.
+      */
+      if (res_all.res_monitor.hdr.name) {
+         free(res_all.res_monitor.hdr.name);
+         res_all.res_monitor.hdr.name = NULL;
+      }
+      if (res_all.res_monitor.hdr.desc) {
+         free(res_all.res_monitor.hdr.desc);
+         res_all.res_monitor.hdr.desc = NULL;
+      }
+      return;
+   }
+
+   /*
+   * The following code is only executed during pass 1
+   */
+   switch (type) {
+   case R_MONITOR:
+      size = sizeof(MONITOR);
+      break;
+   case R_DIRECTOR:
+      size = sizeof(DIRRES);
+      break;
+   case R_CLIENT:
+      size = sizeof(CLIENT);
+      break;
+   case R_STORAGE:
+      size = sizeof(STORE);
+      break;
+   case R_CONSOLE_FONT:
+      size = sizeof(CONFONTRES);
+      break;
+   default:
+      printf(_("Unknown resource type %d in save_resource.\n"), type);
+      error = 1;
+      size = 1;
+      break;
+   }
+   /* Common */
+   if (!error) {
+      res = (URES *)malloc(size);
+      memcpy(res, &res_all, size);
+      if (!res_head[rindex]) {
+        res_head[rindex] = (RES *)res; /* store first entry */
+         Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
+         res->res_monitor.hdr.name, rindex);
+      } else {
+         RES *next, *last;
+         /* Add new res to end of chain */
+         for (last=next=res_head[rindex]; next; next=next->next) {
+            last = next;
+            if (strcmp(next->name, res->res_monitor.hdr.name) == 0) {
+               Emsg2(M_ERROR_TERM, 0,
+                  _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
+               resources[rindex].name, res->res_monitor.hdr.name);
+            }
+         }
+         last->next = (RES *)res;
+         Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(type),
+         res->res_monitor.hdr.name, rindex, pass);
+      }
+   }
+}
+
+bool parse_tmon_config(CONFIG *config, const char *configfile, int exit_code)
+{
+   config->init(configfile, NULL, exit_code, (void *)&res_all, res_all_size,
+      r_first, r_last, resources, res_head);
+   return config->parse_config();
+}
diff --git a/bacula/src/qt-console/tray-monitor/tray_conf.h b/bacula/src/qt-console/tray-monitor/tray_conf.h
new file mode 100644 (file)
index 0000000..140ce34
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2004-2011 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version three of the GNU Affero General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU Affero General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+/*
+ * Tray Monitor specific configuration and defines
+ *
+ *   Adapted from dird_conf.c
+ *
+ *     Nicolas Boichat, August MMIV
+ *
+ */
+
+/* NOTE:  #includes at the end of this file */
+
+/*
+ * Resource codes -- they must be sequential for indexing
+ */
+enum rescode {
+   R_MONITOR = 1001,
+   R_DIRECTOR,
+   R_CLIENT,
+   R_STORAGE,
+   R_CONSOLE_FONT,
+   R_FIRST = R_MONITOR,
+   R_LAST  = R_CONSOLE_FONT                /* keep this updated */
+};
+
+
+/*
+ * Some resource attributes
+ */
+enum {
+   R_NAME = 1020,
+   R_ADDRESS,
+   R_PASSWORD,
+   R_TYPE,
+   R_BACKUP
+};
+
+/* Director */
+struct DIRRES {
+   RES   hdr;
+   uint32_t DIRport;                  /* UA server port */
+   char *address;                     /* UA server address */
+   bool enable_ssl;                   /* Use SSL */
+};
+
+/*
+ *   Tray Monitor Resource
+ *
+ */
+struct MONITOR {
+   RES   hdr;
+   bool require_ssl;                  /* Require SSL for all connections */
+   MSGS *messages;                    /* Daemon message handler */
+   char *password;                    /* UA server password */
+   utime_t RefreshInterval;           /* Status refresh interval */
+   utime_t FDConnectTimeout;          /* timeout for connect in seconds */
+   utime_t SDConnectTimeout;          /* timeout in seconds */
+   utime_t DIRConnectTimeout;         /* timeout in seconds */
+};
+
+
+/*
+ *   Client Resource
+ *
+ */
+struct CLIENT {
+   RES   hdr;
+
+   uint32_t FDport;                   /* Where File daemon listens */
+   char *address;
+   char *password;
+   bool enable_ssl;                   /* Use SSL */
+};
+
+/*
+ *   Store Resource
+ *
+ */
+struct STORE {
+   RES   hdr;
+
+   uint32_t SDport;                   /* port where Directors connect */
+   char *address;
+   char *password;
+   bool enable_ssl;                   /* Use SSL */
+};
+
+struct CONFONTRES {
+   RES   hdr;
+   char *fontface;                    /* Console Font specification */
+};
+
+/* Define the Union of all the above
+ * resource structure definitions.
+ */
+union URES {
+   MONITOR    res_monitor;
+   DIRRES     res_dir;
+   CLIENT     res_client;
+   STORE      res_store;
+   CONFONTRES con_font;
+   RES        hdr;
+};