]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/tray-monitor/tray-monitor.cpp
Add new QT traymonitor
[bacula/bacula] / bacula / src / qt-console / tray-monitor / tray-monitor.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2011 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU 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
21    02110-1301, USA.
22
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.
27 */
28
29 #include "tray-ui.h"
30 #include "tray-monitor.h"
31
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);
41
42 /* Dummy functions */
43 int generate_daemon_event(JCR *, const char *) { return 1; }
44
45 /* Static variables */
46 static char *configfile = NULL;
47 static MONITOR *monitor;
48 static JCR jcr;
49 static int nitems = 0;
50 static monitoritem items[32];
51 static CONFIG *config;
52 static TrayUI *tray;
53
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";
57
58
59 void updateStatusIcon(monitoritem* item);
60 void changeStatusMessage(monitoritem* item, const char *fmt,...);
61
62 #define CONFIG_FILE "./tray-monitor.conf"   /* default configuration file */
63
64 static void usage()
65 {
66    fprintf(stderr, _(
67 PROG_COPYRIGHT
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);
76 }
77
78 int sm_line = 0;
79
80 void dotest()
81 {
82    for (int i = 0; i < nitems; i++) {
83       const char *cmd;
84
85       switch (items[i].type) {
86       case R_DIRECTOR:
87          cmd = ".jobs type=B";
88          tray->clearText(items[i].get_name());
89          docmd(&items[i], cmd);
90          break;
91       default:
92          break;
93       }
94    }
95 }
96
97 void get_list(monitoritem *item, const char *cmd, QStringList &lst)
98 {
99    int stat;
100    
101    doconnect(item);
102    item->writecmd(cmd);
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);
107       }
108    }
109 }
110
111 void refresh_item()
112 {
113    for (int i = 0; i < nitems; i++) {
114       const char *cmd;
115       tray->clearText(items[i].get_name());
116       switch (items[i].type) {
117       case R_DIRECTOR:
118          cmd = "status dir";
119          break;
120       case R_CLIENT:
121          cmd = "status";
122          break;
123       case R_STORAGE:
124          cmd = "status";
125          break;          
126       default:
127          exit(1);
128          break;
129       }
130       docmd(&items[i], cmd);
131    }
132 }
133
134 /*********************************************************************
135  *
136  *         Main Bacula Tray Monitor -- User Interface Program
137  *
138  */
139 int main(int argc, char *argv[])
140 {   
141    int ch, i, dir_index=-1;
142    bool test_config = false;
143    DIRRES* dird;
144    CLIENT* filed;
145    STORE* stored;
146
147    setlocale(LC_ALL, "");
148    bindtextdomain("bacula", LOCALEDIR);
149    textdomain("bacula");
150
151    init_stack_dump();
152    my_name_is(argc, argv, "tray-monitor");
153    lmgr_init_thread();
154    init_msg(NULL, NULL);
155    working_directory = "/tmp";
156
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);
162
163    while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
164       switch (ch) {
165       case 'c':                    /* configuration file */
166          if (configfile != NULL) {
167             free(configfile);
168          }
169          configfile = bstrdup(optarg);
170          break;
171
172       case 'd':
173          if (*optarg == 't') {
174             dbg_timestamp = true;
175          } else {
176             debug_level = atoi(optarg);
177             if (debug_level <= 0) {
178                debug_level = 1;
179             }
180          }
181          break;
182
183       case 't':
184          test_config = true;
185          break;
186
187       case 'h':
188       case '?':
189       default:
190          usage();
191          exit(1);
192       }
193    }
194    argc -= optind;
195    //argv += optind;
196
197    if (argc) {
198       usage();
199       exit(1);
200    }
201
202    if (configfile == NULL) {
203       configfile = bstrdup(CONFIG_FILE);
204    }
205
206    config = new_config_parser();
207    parse_tmon_config(config, configfile, M_ERROR_TERM);
208
209    LockRes();
210    nitems = 0;
211    foreach_res(monitor, R_MONITOR) {
212       nitems++;
213    }
214
215    if (nitems != 1) {
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);
218    }
219
220    nitems = 0;
221    foreach_res(dird, R_DIRECTOR) {
222       dir_index=nitems;
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;
228       nitems++;
229    }
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;
236       nitems++;
237    }
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;
244       nitems++;
245    }
246    UnlockRes();
247
248    if (nitems == 0) {
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);
251    }
252
253    if (test_config) {
254       exit(0);
255    }
256
257    (void)WSA_Init();                /* Initialize Windows sockets */
258
259    LockRes();
260    monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
261    UnlockRes();
262
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);
266    }
267
268    sm_line = 0;
269    QApplication    app(argc, argv);
270    app.setQuitOnLastWindowClosed(false);
271    tray = new TrayUI();
272    tray->setupUi(tray);
273    tray->spinRefresh->setValue(monitor->RefreshInterval);
274    if (dir_index >= 0) {
275       tray->addDirector(&items[dir_index]);
276    }
277
278    for (i = 0; i < nitems; i++) {
279       const char *cmd;
280       tray->addTab(items[i].get_name());
281       switch (items[i].type) {
282       case R_DIRECTOR:
283          tray->addDirector(&items[i]);
284          cmd = "status dir";
285          break;
286       case R_CLIENT:
287          cmd = "status";
288          break;
289       case R_STORAGE:
290          cmd = "status";
291          break;          
292       default:
293          exit(1);
294          break;
295       }
296       docmd(&items[i], cmd);
297    }
298
299    tray->startTimer();
300
301    app.exec();
302
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);
309          }
310       }
311    }
312
313
314    (void)WSACleanup();               /* Cleanup Windows sockets */
315    
316    config->free_resources();
317    free(config);
318    config = NULL;
319    term_msg();
320    return 0;
321 }
322
323 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
324    switch (item->type) {
325    case R_DIRECTOR:
326       return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
327    case R_CLIENT:
328       return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
329    case R_STORAGE:
330       return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
331    default:
332       printf(_("Error, currentitem is not a Client or a Storage..\n"));
333       return FALSE;
334    }
335    return false;
336 }
337
338 void changeStatusMessage(monitoritem*, const char *fmt,...) {
339    char buf[512];
340    va_list arg_ptr;
341
342    va_start(arg_ptr, fmt);
343    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
344    va_end(arg_ptr);
345    tray->statusbar->showMessage(QString(buf));
346 }
347
348 int doconnect(monitoritem* item) 
349 {
350    if (!item->D_sock) {
351       memset(&jcr, 0, sizeof(jcr));
352
353       DIRRES* dird;
354       CLIENT* filed;
355       STORE* stored;
356
357       switch (item->type) {
358       case R_DIRECTOR:
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;
364          break;
365       case R_CLIENT:
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;
371          break;
372       case R_STORAGE:
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;
378          break;
379       default:
380          printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
381          return 0;
382       }
383
384       if (item->D_sock == NULL) {
385          changeStatusMessage(item, _("Cannot connect to daemon."));
386          item->state = error;
387          item->oldstate = error;
388          return 0;
389       }
390
391       if (!authenticate_daemon(item, &jcr)) {
392          item->state = error;
393          item->oldstate = error;
394          changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
395          item->D_sock = NULL;
396          return 0;
397       }
398
399       switch (item->type) {
400       case R_DIRECTOR:
401          changeStatusMessage(item, _("Opened connection with Director daemon."));
402          break;
403       case R_CLIENT:
404          changeStatusMessage(item, _("Opened connection with File daemon."));
405          break;
406       case R_STORAGE:
407          changeStatusMessage(item, _("Opened connection with Storage daemon."));
408          break;
409       default:
410          printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
411          return 0;
412          break;
413       }
414
415       if (item->type == R_DIRECTOR) { /* Read connection messages... */
416          docmd(item, ""); /* Usually invalid, but no matter */
417       }
418    }
419    return 1;
420 }
421
422 int docmd(monitoritem* item, const char* command) 
423 {
424    int stat;
425    //qDebug() << "docmd(" << item->get_name() << "," << command << ")";
426    if (!doconnect(item)) {
427       return 0;
428    }
429
430    if (command[0] != 0)
431       item->writecmd(command);
432
433    while(1) {
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);
437       }
438       else if (stat == BNET_SIGNAL) {
439          if (item->D_sock->msglen == BNET_EOD) {
440             // qDebug() << "<< EOD >>";
441             return 1;
442          }
443          else if (item->D_sock->msglen == BNET_SUB_PROMPT) {
444             // qDebug() << "<< PROMPT >>";
445             return 0;
446          }
447          else if (item->D_sock->msglen == BNET_HEARTBEAT) {
448             bnet_sig(item->D_sock, BNET_HB_RESPONSE);
449          }
450          else {
451             qDebug() << bnet_sig_to_ascii(item->D_sock);
452          }
453       }
454       else { /* BNET_HARDEOF || BNET_ERROR */
455          item->D_sock = NULL;
456          item->state = error;
457          item->oldstate = error;
458          changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
459          //fprintf(stderr, _("<< ERROR >>\n"));
460          return 0;
461       }
462
463       if (is_bnet_stop(item->D_sock)) {
464          item->D_sock = NULL;
465          item->state = error;
466          item->oldstate = error;
467          changeStatusMessage(item, _("Error : Connection closed."));
468          //fprintf(stderr, "<< STOP >>\n");
469          return 0;            /* error or term */
470       }
471    }
472 }