From 94e817e7f05aecc09b672b173d810c96ef9fecdf Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Thu, 6 Jan 2011 16:54:34 +0100 Subject: [PATCH] Add new QT traymonitor --- .../qt-console/tray-monitor/authenticate.cpp | 201 ++++++++ .../tray-monitor/tray-monitor.conf.in | 29 ++ .../qt-console/tray-monitor/tray-monitor.cpp | 472 ++++++++++++++++++ .../qt-console/tray-monitor/tray-monitor.h | 179 +++++++ .../tray-monitor/tray-monitor.pro.in | 46 ++ bacula/src/qt-console/tray-monitor/tray-ui.h | 389 +++++++++++++++ .../src/qt-console/tray-monitor/tray_conf.cpp | 388 ++++++++++++++ .../src/qt-console/tray-monitor/tray_conf.h | 129 +++++ 8 files changed, 1833 insertions(+) create mode 100644 bacula/src/qt-console/tray-monitor/authenticate.cpp create mode 100644 bacula/src/qt-console/tray-monitor/tray-monitor.conf.in create mode 100644 bacula/src/qt-console/tray-monitor/tray-monitor.cpp create mode 100644 bacula/src/qt-console/tray-monitor/tray-monitor.h create mode 100644 bacula/src/qt-console/tray-monitor/tray-monitor.pro.in create mode 100644 bacula/src/qt-console/tray-monitor/tray-ui.h create mode 100644 bacula/src/qt-console/tray-monitor/tray_conf.cpp create mode 100644 bacula/src/qt-console/tray-monitor/tray_conf.h diff --git a/bacula/src/qt-console/tray-monitor/authenticate.cpp b/bacula/src/qt-console/tray-monitor/authenticate.cpp new file mode 100644 index 0000000000..35a4d19bf8 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/authenticate.cpp @@ -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, "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, _("bdirdbstrerror()); + return 0; + } + Dmsg1(110, "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, "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 index 0000000000..30aea7db37 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/tray-monitor.conf.in @@ -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 index 0000000000..9c64f48bd8 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/tray-monitor.cpp @@ -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 set configuration file to file\n" +" -d set debug level to \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 index 0000000000..7eba3dfe75 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/tray-monitor.h @@ -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 +#include +#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 index 0000000000..ab606921ca --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/tray-monitor.pro.in @@ -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 index 0000000000..9b77440881 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/tray-ui.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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("

"+job_defs.type+"

"); + 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 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 index 0000000000..c56fdf76c6 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/tray_conf.cpp @@ -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 index 0000000000..140ce3455d --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/tray_conf.h @@ -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; +}; -- 2.39.5