--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+#
+# 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@
+}
--- /dev/null
+/*
+ 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 */
+ }
+ }
+}
--- /dev/null
+/*
+ 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 */
--- /dev/null
+######################################################################
+# 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
+
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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();
+}
--- /dev/null
+/*
+ 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;
+};