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