2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2011 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 #include "tray-monitor.h"
32 int doconnect(monitoritem* item);
33 int docmd(monitoritem* item, const char* command);
34 static int authenticate_daemon(monitoritem* item, JCR *jcr);
35 /* Imported functions */
36 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
37 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
38 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
39 extern bool parse_tmon_config(CONFIG *config, const char *configfile, int exit_code);
40 void get_list(monitoritem* item, const char *cmd, QStringList &lst);
43 int generate_daemon_event(JCR *, const char *) { return 1; }
45 /* Static variables */
46 static char *configfile = NULL;
47 static MONITOR *monitor;
49 static int nitems = 0;
50 static monitoritem items[32];
51 static CONFIG *config;
54 /* Data received from DIR/FD/SD */
55 static char OKqstatus[] = "%c000 OK .status\n";
56 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
59 void updateStatusIcon(monitoritem* item);
60 void changeStatusMessage(monitoritem* item, const char *fmt,...);
62 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
68 "\nVersion: %s (%s) %s %s %s\n\n"
69 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
70 " -c <file> set configuration file to file\n"
71 " -d <nn> set debug level to <nn>\n"
72 " -dt print timestamp in debug output\n"
73 " -t test - read configuration and exit\n"
74 " -? print this message.\n"
75 "\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
82 for (int i = 0; i < nitems; i++) {
85 switch (items[i].type) {
88 tray->clearText(items[i].get_name());
89 docmd(&items[i], cmd);
97 void get_list(monitoritem *item, const char *cmd, QStringList &lst)
103 while((stat = bnet_recv(item->D_sock)) >= 0) {
104 strip_trailing_junk(item->D_sock->msg);
105 if (*(item->D_sock->msg)) {
106 lst << QString(item->D_sock->msg);
113 for (int i = 0; i < nitems; i++) {
115 tray->clearText(items[i].get_name());
116 switch (items[i].type) {
130 docmd(&items[i], cmd);
134 /*********************************************************************
136 * Main Bacula Tray Monitor -- User Interface Program
139 int main(int argc, char *argv[])
141 int ch, i, dir_index=-1;
142 bool test_config = false;
147 setlocale(LC_ALL, "");
148 bindtextdomain("bacula", LOCALEDIR);
149 textdomain("bacula");
152 my_name_is(argc, argv, "tray-monitor");
154 init_msg(NULL, NULL);
155 working_directory = "/tmp";
157 struct sigaction sigignore;
158 sigignore.sa_flags = 0;
159 sigignore.sa_handler = SIG_IGN;
160 sigfillset(&sigignore.sa_mask);
161 sigaction(SIGPIPE, &sigignore, NULL);
163 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
165 case 'c': /* configuration file */
166 if (configfile != NULL) {
169 configfile = bstrdup(optarg);
173 if (*optarg == 't') {
174 dbg_timestamp = true;
176 debug_level = atoi(optarg);
177 if (debug_level <= 0) {
202 if (configfile == NULL) {
203 configfile = bstrdup(CONFIG_FILE);
206 config = new_config_parser();
207 parse_tmon_config(config, configfile, M_ERROR_TERM);
211 foreach_res(monitor, R_MONITOR) {
216 Emsg2(M_ERROR_TERM, 0,
217 _("Error: %d Monitor resources defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
221 foreach_res(dird, R_DIRECTOR) {
223 items[nitems].type = R_DIRECTOR;
224 items[nitems].resource = dird;
225 items[nitems].D_sock = NULL;
226 items[nitems].state = warn;
227 items[nitems].oldstate = warn;
230 foreach_res(filed, R_CLIENT) {
231 items[nitems].type = R_CLIENT;
232 items[nitems].resource = filed;
233 items[nitems].D_sock = NULL;
234 items[nitems].state = warn;
235 items[nitems].oldstate = warn;
238 foreach_res(stored, R_STORAGE) {
239 items[nitems].type = R_STORAGE;
240 items[nitems].resource = stored;
241 items[nitems].D_sock = NULL;
242 items[nitems].state = warn;
243 items[nitems].oldstate = warn;
249 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage or Director resource defined in %s\n"
250 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
257 (void)WSA_Init(); /* Initialize Windows sockets */
260 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
263 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
264 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
265 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
269 QApplication app(argc, argv);
270 app.setQuitOnLastWindowClosed(false);
273 tray->spinRefresh->setValue(monitor->RefreshInterval);
274 if (dir_index >= 0) {
275 tray->addDirector(&items[dir_index]);
278 for (i = 0; i < nitems; i++) {
280 tray->addTab(items[i].get_name());
281 switch (items[i].type) {
283 tray->addDirector(&items[i]);
296 docmd(&items[i], cmd);
303 for (i = 0; i < nitems; i++) {
304 if (items[i].D_sock) {
305 items[i].writecmd("quit");
306 if (items[i].D_sock) {
307 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
308 bnet_close(items[i].D_sock);
314 (void)WSACleanup(); /* Cleanup Windows sockets */
316 config->free_resources();
323 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
324 switch (item->type) {
326 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
328 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
330 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
332 printf(_("Error, currentitem is not a Client or a Storage..\n"));
338 void changeStatusMessage(monitoritem*, const char *fmt,...) {
342 va_start(arg_ptr, fmt);
343 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
345 tray->statusbar->showMessage(QString(buf));
348 int doconnect(monitoritem* item)
351 memset(&jcr, 0, sizeof(jcr));
357 switch (item->type) {
359 dird = (DIRRES*)item->resource;
360 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
361 item->D_sock = bnet_connect(NULL, monitor->DIRConnectTimeout,
362 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
363 jcr.dir_bsock = item->D_sock;
366 filed = (CLIENT*)item->resource;
367 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
368 item->D_sock = bnet_connect(NULL, monitor->FDConnectTimeout,
369 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
370 jcr.file_bsock = item->D_sock;
373 stored = (STORE*)item->resource;
374 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
375 item->D_sock = bnet_connect(NULL, monitor->SDConnectTimeout,
376 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
377 jcr.store_bsock = item->D_sock;
380 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
384 if (item->D_sock == NULL) {
385 changeStatusMessage(item, _("Cannot connect to daemon."));
387 item->oldstate = error;
391 if (!authenticate_daemon(item, &jcr)) {
393 item->oldstate = error;
394 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
399 switch (item->type) {
401 changeStatusMessage(item, _("Opened connection with Director daemon."));
404 changeStatusMessage(item, _("Opened connection with File daemon."));
407 changeStatusMessage(item, _("Opened connection with Storage daemon."));
410 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
415 if (item->type == R_DIRECTOR) { /* Read connection messages... */
416 docmd(item, ""); /* Usually invalid, but no matter */
422 int docmd(monitoritem* item, const char* command)
425 //qDebug() << "docmd(" << item->get_name() << "," << command << ")";
426 if (!doconnect(item)) {
431 item->writecmd(command);
434 if ((stat = bnet_recv(item->D_sock)) >= 0) {
435 strip_trailing_newline(item->D_sock->msg);
436 tray->appendText(item->get_name(), item->D_sock->msg);
438 else if (stat == BNET_SIGNAL) {
439 if (item->D_sock->msglen == BNET_EOD) {
440 // qDebug() << "<< EOD >>";
443 else if (item->D_sock->msglen == BNET_SUB_PROMPT) {
444 // qDebug() << "<< PROMPT >>";
447 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
448 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
451 qDebug() << bnet_sig_to_ascii(item->D_sock);
454 else { /* BNET_HARDEOF || BNET_ERROR */
457 item->oldstate = error;
458 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
459 //fprintf(stderr, _("<< ERROR >>\n"));
463 if (is_bnet_stop(item->D_sock)) {
466 item->oldstate = error;
467 changeStatusMessage(item, _("Error : Connection closed."));
468 //fprintf(stderr, "<< STOP >>\n");
469 return 0; /* error or term */