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