]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/tray-monitor/tray-monitor.cpp
update configure
[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
31 int doconnect(monitoritem* item);
32 int docmd(monitoritem* item, const char* command);
33 static int authenticate_daemon(monitoritem* item, JCR *jcr);
34 /* Imported functions */
35 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
36 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
37 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
38 extern bool parse_tmon_config(CONFIG *config, const char *configfile, int exit_code);
39 void get_list(monitoritem* item, const char *cmd, QStringList &lst);
40
41 /* Dummy functions */
42 int generate_daemon_event(JCR *, const char *) { return 1; }
43
44 /* Static variables */
45 static char *configfile = NULL;
46 static MONITOR *monitor;
47 static JCR jcr;
48 static int nitems = 0;
49 static monitoritem items[32];
50 static CONFIG *config;
51 static TrayUI *tray;
52
53 /* Data received from DIR/FD/SD */
54 //static char OKqstatus[]   = "%c000 OK .status\n";
55 //static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
56
57
58 void updateStatusIcon(monitoritem* item);
59 void changeStatusMessage(monitoritem* item, const char *fmt,...);
60
61 #define CONFIG_FILE "./tray-monitor.conf"   /* default configuration file */
62
63 static void usage()
64 {
65    fprintf(stderr, _(
66 PROG_COPYRIGHT
67 "\nVersion: %s (%s) %s %s %s\n\n"
68 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
69 "       -c <file>     set configuration file to file\n"
70 "       -d <nn>       set debug level to <nn>\n"
71 "       -dt           print timestamp in debug output\n"
72 "       -t            test - read configuration and exit\n"
73 "       -?            print this message.\n"
74 "\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
75 }
76
77 int sm_line = 0;
78
79 void dotest()
80 {
81    for (int i = 0; i < nitems; i++) {
82       const char *cmd;
83
84       switch (items[i].type) {
85       case R_DIRECTOR:
86          cmd = ".jobs type=B";
87          tray->clearText(items[i].get_name());
88          docmd(&items[i], cmd);
89          break;
90       default:
91          break;
92       }
93    }
94 }
95
96 void get_list(monitoritem *item, const char *cmd, QStringList &lst)
97 {
98    int stat;
99    
100    doconnect(item);
101    item->writecmd(cmd);
102    while((stat = bnet_recv(item->D_sock)) >= 0) {
103       strip_trailing_junk(item->D_sock->msg);
104       if (*(item->D_sock->msg)) {
105          lst << QString(item->D_sock->msg);
106       }
107    }
108 }
109
110 void refresh_item()
111 {
112    for (int i = 0; i < nitems; i++) {
113       const char *cmd;
114       tray->clearText(items[i].get_name());
115       switch (items[i].type) {
116       case R_DIRECTOR:
117          cmd = "status dir";
118          break;
119       case R_CLIENT:
120          cmd = "status";
121          break;
122       case R_STORAGE:
123          cmd = "status";
124          break;          
125       default:
126          exit(1);
127          break;
128       }
129       docmd(&items[i], cmd);
130    }
131 }
132
133 /*********************************************************************
134  *
135  *         Main Bacula Tray Monitor -- User Interface Program
136  *
137  */
138 int main(int argc, char *argv[])
139 {   
140    int ch, i, dir_index=-1;
141    bool test_config = false;
142    DIRRES* dird;
143    CLIENT* filed;
144    STORE* stored;
145
146    setlocale(LC_ALL, "");
147    bindtextdomain("bacula", LOCALEDIR);
148    textdomain("bacula");
149
150    init_stack_dump();
151    my_name_is(argc, argv, "tray-monitor");
152    lmgr_init_thread();
153    init_msg(NULL, NULL);
154    working_directory = "/tmp";
155
156    struct sigaction sigignore;
157    sigignore.sa_flags = 0;
158    sigignore.sa_handler = SIG_IGN;
159    sigfillset(&sigignore.sa_mask);
160    sigaction(SIGPIPE, &sigignore, NULL);
161
162    while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
163       switch (ch) {
164       case 'c':                    /* configuration file */
165          if (configfile != NULL) {
166             free(configfile);
167          }
168          configfile = bstrdup(optarg);
169          break;
170
171       case 'd':
172          if (*optarg == 't') {
173             dbg_timestamp = true;
174          } else {
175             debug_level = atoi(optarg);
176             if (debug_level <= 0) {
177                debug_level = 1;
178             }
179          }
180          break;
181
182       case 't':
183          test_config = true;
184          break;
185
186       case 'h':
187       case '?':
188       default:
189          usage();
190          exit(1);
191       }
192    }
193    argc -= optind;
194    //argv += optind;
195
196    if (argc) {
197       usage();
198       exit(1);
199    }
200
201    if (configfile == NULL) {
202       configfile = bstrdup(CONFIG_FILE);
203    }
204
205    config = new_config_parser();
206    parse_tmon_config(config, configfile, M_ERROR_TERM);
207
208    LockRes();
209    nitems = 0;
210    foreach_res(monitor, R_MONITOR) {
211       nitems++;
212    }
213
214    if (nitems != 1) {
215       Emsg2(M_ERROR_TERM, 0,
216          _("Error: %d Monitor resources defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
217    }
218
219    nitems = 0;
220    foreach_res(dird, R_DIRECTOR) {
221       dir_index=nitems;
222       items[nitems].type = R_DIRECTOR;
223       items[nitems].resource = dird;
224       items[nitems].D_sock = NULL;
225       items[nitems].state = warn;
226       items[nitems].oldstate = warn;
227       nitems++;
228    }
229    foreach_res(filed, R_CLIENT) {
230       items[nitems].type = R_CLIENT;
231       items[nitems].resource = filed;
232       items[nitems].D_sock = NULL;
233       items[nitems].state = warn;
234       items[nitems].oldstate = warn;
235       nitems++;
236    }
237    foreach_res(stored, R_STORAGE) {
238       items[nitems].type = R_STORAGE;
239       items[nitems].resource = stored;
240       items[nitems].D_sock = NULL;
241       items[nitems].state = warn;
242       items[nitems].oldstate = warn;
243       nitems++;
244    }
245    UnlockRes();
246
247    if (nitems == 0) {
248       Emsg1(M_ERROR_TERM, 0, _("No Client, Storage or Director resource defined in %s\n"
249 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
250    }
251
252    if (test_config) {
253       exit(0);
254    }
255
256    (void)WSA_Init();                /* Initialize Windows sockets */
257
258    LockRes();
259    monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
260    UnlockRes();
261
262    if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
263       Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
264 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
265    }
266
267    sm_line = 0;
268    QApplication    app(argc, argv);
269    app.setQuitOnLastWindowClosed(false);
270    tray = new TrayUI();
271    tray->setupUi(tray);
272    tray->spinRefresh->setValue(monitor->RefreshInterval);
273    if (dir_index >= 0) {
274       tray->addDirector(&items[dir_index]);
275    }
276
277    for (i = 0; i < nitems; i++) {
278       const char *cmd;
279       tray->addTab(items[i].get_name());
280       switch (items[i].type) {
281       case R_DIRECTOR:
282          tray->addDirector(&items[i]);
283          cmd = "status dir";
284          break;
285       case R_CLIENT:
286          cmd = "status";
287          break;
288       case R_STORAGE:
289          cmd = "status";
290          break;          
291       default:
292          exit(1);
293          break;
294       }
295       docmd(&items[i], cmd);
296    }
297
298    tray->startTimer();
299
300    app.exec();
301
302    for (i = 0; i < nitems; i++) {
303       if (items[i].D_sock) {
304          items[i].writecmd("quit");
305          if (items[i].D_sock) {
306             bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
307             bnet_close(items[i].D_sock);
308          }
309       }
310    }
311
312
313    (void)WSACleanup();               /* Cleanup Windows sockets */
314    
315    config->free_resources();
316    free(config);
317    config = NULL;
318    term_msg();
319    return 0;
320 }
321
322 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
323    switch (item->type) {
324    case R_DIRECTOR:
325       return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
326    case R_CLIENT:
327       return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
328    case R_STORAGE:
329       return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
330    default:
331       printf(_("Error, currentitem is not a Client or a Storage..\n"));
332       return FALSE;
333    }
334    return false;
335 }
336
337 void changeStatusMessage(monitoritem*, const char *fmt,...) {
338    char buf[512];
339    va_list arg_ptr;
340
341    va_start(arg_ptr, fmt);
342    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
343    va_end(arg_ptr);
344    tray->statusbar->showMessage(QString(buf));
345 }
346
347 int doconnect(monitoritem* item) 
348 {
349    if (!item->D_sock) {
350       memset(&jcr, 0, sizeof(jcr));
351
352       DIRRES* dird;
353       CLIENT* filed;
354       STORE* stored;
355
356       switch (item->type) {
357       case R_DIRECTOR:
358          dird = (DIRRES*)item->resource;
359          changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
360          item->D_sock = bnet_connect(NULL, monitor->DIRConnectTimeout, 
361                                      0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
362          jcr.dir_bsock = item->D_sock;
363          break;
364       case R_CLIENT:
365          filed = (CLIENT*)item->resource;
366          changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
367          item->D_sock = bnet_connect(NULL, monitor->FDConnectTimeout, 
368                                      0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
369          jcr.file_bsock = item->D_sock;
370          break;
371       case R_STORAGE:
372          stored = (STORE*)item->resource;
373          changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
374          item->D_sock = bnet_connect(NULL, monitor->SDConnectTimeout, 
375                                      0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
376          jcr.store_bsock = item->D_sock;
377          break;
378       default:
379          printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
380          return 0;
381       }
382
383       if (item->D_sock == NULL) {
384          changeStatusMessage(item, _("Cannot connect to daemon."));
385          item->state = error;
386          item->oldstate = error;
387          return 0;
388       }
389
390       if (!authenticate_daemon(item, &jcr)) {
391          item->state = error;
392          item->oldstate = error;
393          changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
394          item->D_sock = NULL;
395          return 0;
396       }
397
398       switch (item->type) {
399       case R_DIRECTOR:
400          changeStatusMessage(item, _("Opened connection with Director daemon."));
401          break;
402       case R_CLIENT:
403          changeStatusMessage(item, _("Opened connection with File daemon."));
404          break;
405       case R_STORAGE:
406          changeStatusMessage(item, _("Opened connection with Storage daemon."));
407          break;
408       default:
409          printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
410          return 0;
411          break;
412       }
413
414       if (item->type == R_DIRECTOR) { /* Read connection messages... */
415          docmd(item, ""); /* Usually invalid, but no matter */
416       }
417    }
418    return 1;
419 }
420
421 int docmd(monitoritem* item, const char* command) 
422 {
423    int stat;
424    //qDebug() << "docmd(" << item->get_name() << "," << command << ")";
425    if (!doconnect(item)) {
426       return 0;
427    }
428
429    if (command[0] != 0)
430       item->writecmd(command);
431
432    while(1) {
433       if ((stat = bnet_recv(item->D_sock)) >= 0) {
434          strip_trailing_newline(item->D_sock->msg);
435          tray->appendText(item->get_name(), item->D_sock->msg);
436       }
437       else if (stat == BNET_SIGNAL) {
438          if (item->D_sock->msglen == BNET_EOD) {
439             // qDebug() << "<< EOD >>";
440             return 1;
441          }
442          else if (item->D_sock->msglen == BNET_SUB_PROMPT) {
443             // qDebug() << "<< PROMPT >>";
444             return 0;
445          }
446          else if (item->D_sock->msglen == BNET_HEARTBEAT) {
447             bnet_sig(item->D_sock, BNET_HB_RESPONSE);
448          }
449          else {
450             qDebug() << bnet_sig_to_ascii(item->D_sock);
451          }
452       }
453       else { /* BNET_HARDEOF || BNET_ERROR */
454          item->D_sock = NULL;
455          item->state = error;
456          item->oldstate = error;
457          changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
458          //fprintf(stderr, _("<< ERROR >>\n"));
459          return 0;
460       }
461
462       if (is_bnet_stop(item->D_sock)) {
463          item->D_sock = NULL;
464          item->state = error;
465          item->oldstate = error;
466          changeStatusMessage(item, _("Error : Connection closed."));
467          //fprintf(stderr, "<< STOP >>\n");
468          return 0;            /* error or term */
469       }
470    }
471 }