3 * Bacula Gnome Tray Monitor
5 * Nicolas Boichat, August MMIV
11 Copyright (C) 2004 Kern Sibbald and John Walker
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2.1 of the License, or (at your option) any later version.
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the Free
25 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
31 #include "tray-monitor.h"
33 #include "eggstatusicon.h"
35 #include "generic.xpm"
37 /* Imported functions */
38 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
39 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
40 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
42 /* Forward referenced functions */
43 void writecmd(monitoritem* item, const char* command);
44 int docmd(monitoritem* item, const char* command, GSList** list);
45 void getstatus(monitoritem* item, int current, GString** str);
47 /* Static variables */
48 static char *configfile = NULL;
49 static MONITOR *monitor;
52 static int nitems = 0;
53 static int fullitem = 0; //Item to be display in detailled status window
54 static int lastupdated = -1; //Last item updated
55 static monitoritem items[32];
57 /* Data received from DIR/FD/SD */
58 static char OKqstatus[] = "%c000 OK .status\n";
59 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
61 /* UI variables and functions */
63 static gboolean fd_read(gpointer data);
64 static gboolean blink(gpointer data);
65 void trayMessage(const char *fmt,...);
66 void updateStatusIcon(monitoritem* item);
67 void changeStatusMessage(monitoritem* item, const char *fmt,...);
68 static const char** generateXPM(stateenum newstate, stateenum oldstate);
71 static void TrayIconActivate(GtkWidget *widget, gpointer data);
72 static void TrayIconExit(unsigned int activateTime, unsigned int button);
73 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
74 static void MonitorAbout(GtkWidget *widget, gpointer data);
75 static void MonitorRefresh(GtkWidget *widget, gpointer data);
76 static void IntervalChanged(GtkWidget *widget, gpointer data);
77 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
78 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
80 static guint timerTag;
81 static EggStatusIcon *mTrayIcon;
82 static GtkWidget *mTrayMenu;
83 static GtkWidget *window;
84 static GtkWidget *textview;
85 static GtkTextBuffer *buffer;
86 static GtkWidget *timeoutspinner;
87 char** xpm_generic_var;
88 static gboolean blinkstate = TRUE;
90 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
95 "Copyright (C) 2000-2004 Kern Sibbald and John Walker\n"
96 "Written by Nicolas Boichat (2004)\n"
97 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
98 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
99 " -c <file> set configuration file to file\n"
100 " -dnn set debug level to nn\n"
101 " -t test - read configuration and exit\n"
102 " -? print this message.\n"
103 "\n"), HOST_OS, DISTNAME, DISTVER);
106 static GtkWidget *new_image_button(const gchar *stock_id,
107 const gchar *label_text) {
113 button = gtk_button_new();
115 box = gtk_hbox_new(FALSE, 0);
116 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
117 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
118 label = gtk_label_new(label_text);
120 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
121 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
123 gtk_widget_show(image);
124 gtk_widget_show(label);
126 gtk_widget_show(box);
128 gtk_container_add(GTK_CONTAINER(button), box);
133 /*********************************************************************
135 * Main Bacula Tray Monitor -- User Interface Program
138 int main(int argc, char *argv[])
141 bool test_config = false;
147 my_name_is(argc, argv, "tray-monitor");
148 textdomain("bacula");
149 init_msg(NULL, NULL);
150 working_directory = "/tmp";
151 args = get_pool_memory(PM_FNAME);
153 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
155 case 'c': /* configuration file */
156 if (configfile != NULL) {
159 configfile = bstrdup(optarg);
163 debug_level = atoi(optarg);
164 if (debug_level <= 0) {
188 if (configfile == NULL) {
189 configfile = bstrdup(CONFIG_FILE);
192 parse_config(configfile);
196 foreach_res(monitor, R_MONITOR) {
201 Emsg2(M_ERROR_TERM, 0,
202 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
206 foreach_res(dird, R_DIRECTOR) {
207 items[nitems].type = R_DIRECTOR;
208 items[nitems].resource = dird;
209 items[nitems].D_sock = NULL;
210 items[nitems].state = warn;
211 items[nitems].oldstate = warn;
214 foreach_res(filed, R_CLIENT) {
215 items[nitems].type = R_CLIENT;
216 items[nitems].resource = filed;
217 items[nitems].D_sock = NULL;
218 items[nitems].state = warn;
219 items[nitems].oldstate = warn;
222 foreach_res(stored, R_STORAGE) {
223 items[nitems].type = R_STORAGE;
224 items[nitems].resource = stored;
225 items[nitems].D_sock = NULL;
226 items[nitems].state = warn;
227 items[nitems].oldstate = warn;
233 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n\
234 Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
241 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
242 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
243 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
244 g_assert((xpm_generic_var[i] = (char*)g_malloc(strlen(xpm_generic[i])*sizeof(char))));
245 strcpy(xpm_generic_var[i], xpm_generic[i]);
248 (void)WSA_Init(); /* Initialize Windows sockets */
251 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
254 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
255 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n\
256 This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
259 gtk_init (&argc, &argv);
261 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
262 // This should be ideally replaced by a completely libpr0n-based icon rendering.
263 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
264 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
265 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
266 g_object_unref(G_OBJECT(pixbuf));
268 mTrayMenu = gtk_menu_new();
272 entry = gtk_menu_item_new_with_label("Open status window...");
273 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
274 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
276 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
278 entry = gtk_menu_item_new_with_label("Exit");
279 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
280 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
282 gtk_widget_show_all(mTrayMenu);
284 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
286 g_timeout_add( 1000, blink, NULL );
288 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
290 gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
292 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
293 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
295 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
297 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
299 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
301 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
303 GtkWidget* separator = gtk_hseparator_new();
304 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
307 GSList *group = NULL;
311 for (int i = 0; i < nitems; i++) {
312 switch (items[i].type) {
314 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
315 g_string_append(str, _(" (DIR)"));
318 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
319 g_string_append(str, _(" (FD)"));
322 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
323 g_string_append(str, _(" (SD)"));
329 radio = gtk_radio_button_new_with_label(group, str->str);
330 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
331 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
333 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
334 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
336 items[i].label = gtk_label_new(_("Unknown status."));
337 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
338 gtk_container_add(GTK_CONTAINER(align), items[i].label);
340 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
341 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
342 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
343 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
344 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
345 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
347 separator = gtk_hseparator_new();
348 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
350 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
353 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
355 textview = gtk_text_view_new();
357 buffer = gtk_text_buffer_new(NULL);
359 gtk_text_buffer_set_text(buffer, "", -1);
361 PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
362 gtk_widget_modify_font(textview, font_desc);
363 pango_font_description_free(font_desc);
365 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
366 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
368 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
370 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
372 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
374 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
376 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
377 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
378 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
379 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
380 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
381 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
382 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
383 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
385 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
386 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
387 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
389 button = new_image_button("gtk-help", _("About"));
390 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
391 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
393 button = new_image_button("gtk-close", _("Close"));
394 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
395 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
397 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
399 gtk_container_add(GTK_CONTAINER (window), vbox);
401 gtk_widget_show_all(vbox);
405 for (i = 0; i < nitems; i++) {
406 if (items[i].D_sock) {
407 writecmd(&items[i], "quit");
408 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
409 bnet_close(items[i].D_sock);
413 free_pool_memory(args);
414 (void)WSACleanup(); /* Cleanup Windows sockets */
416 //Free xpm_generic_var
417 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
418 g_free(xpm_generic_var[i]);
420 g_free(xpm_generic_var);
425 static void MonitorAbout(GtkWidget *widget, gpointer data) {
427 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
428 "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
429 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
430 "Written by Nicolas Boichat\n"
431 "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
432 ), HOST_OS, DISTNAME, DISTVER);
434 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
435 "Bacula Tray Monitor\n\n"
436 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
437 "Written by Nicolas Boichat\n"
438 "\nVersion: " VERSION " (" BDATE ") %s %s %s"
439 ), HOST_OS, DISTNAME, DISTVER);
441 gtk_dialog_run(GTK_DIALOG(about));
442 gtk_widget_destroy(about);
445 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
446 for (int i = 0; i < nitems; i++) {
451 static gboolean delete_event( GtkWidget *widget,
454 gtk_widget_hide(window);
455 return TRUE; /* do not destroy the window */
458 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
459 gtk_widget_show(window);
462 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
463 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
464 gtk_widget_show_all(mTrayMenu);
467 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
471 static void IntervalChanged(GtkWidget *widget, gpointer data) {
472 g_source_remove(timerTag);
473 timerTag = g_timeout_add(
475 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
479 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
480 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
482 for (int i = 0; i < nitems; i++) {
483 if (data == &(items[i])) {
488 g_return_if_fail(fullitem != -1);
490 int oldlastupdated = lastupdated;
491 lastupdated = fullitem-1;
493 lastupdated = oldlastupdated;
497 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
498 switch (item->type) {
500 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
503 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
506 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
509 printf("Error, currentitem is not a Client or a Storage..\n");
515 static gboolean blink(gpointer data) {
516 blinkstate = !blinkstate;
517 for (int i = 0; i < nitems; i++) {
518 updateStatusIcon(&items[i]);
520 updateStatusIcon(NULL);
524 static gboolean fd_read(gpointer data) {
525 GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
526 GtkTextIter start, stop, nstart, nstop;
530 GString *strlast, *strcurrent;
533 if (lastupdated == nitems) {
537 if (lastupdated == fullitem) {
538 if (items[lastupdated].type == R_DIRECTOR)
539 docmd(&items[lastupdated], "status Director\n", &list);
541 docmd(&items[lastupdated], "status\n", &list);
545 gtk_text_buffer_get_end_iter(newbuffer, &stop);
546 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
547 if (it->data) g_string_free((GString*)it->data, TRUE);
548 } while ((it = it->next) != NULL);
550 /* Keep the selection if necessary */
551 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
552 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
553 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
556 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
558 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
559 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
563 g_object_unref(buffer);
566 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
569 getstatus(&items[lastupdated], 1, &strcurrent);
570 getstatus(&items[lastupdated], 0, &strlast);
571 updateStatusIcon(&items[lastupdated]);
573 changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
575 updateStatusIcon(NULL);
577 g_string_free(strcurrent, TRUE);
578 g_string_free(strlast, TRUE);
583 void getstatus(monitoritem* item, int current, GString** str) {
585 stateenum ret = error;
586 int jobid = 0, joberrors = 0;
587 char jobstatus = JS_ErrorTerminated;
591 *str = g_string_sized_new(128);
594 if (item->type == R_DIRECTOR)
595 docmd(&items[lastupdated], ".status dir current\n", &list);
597 docmd(&items[lastupdated], ".status current\n", &list);
600 if (item->type == R_DIRECTOR)
601 docmd(&items[lastupdated], ".status dir last\n", &list);
603 docmd(&items[lastupdated], ".status last\n", &list);
607 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
608 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
609 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
610 g_string_set_size(*str, (*str)->len-1);
614 else if ((it = it->next) == NULL) {
616 g_string_append(*str, _("No current job."));
619 g_string_append(*str, _("No last job."));
623 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
626 ret = (joberrors > 0) ? warn : running;
627 g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
630 ret = (joberrors > 0) ? warn : running;
631 g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
634 ret = (joberrors > 0) ? warn : running;
635 g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
638 g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
639 ret = (joberrors > 0) ? warn : idle;
641 case JS_ErrorTerminated:
642 g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
646 g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
650 g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
654 g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
658 g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
664 fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
670 if (it->data) g_string_free((GString*)it->data, TRUE);
671 } while ((it = it->next) != NULL);
679 item->oldstate = ret;
683 int docmd(monitoritem* item, const char* command, GSList** list) {
687 *list = g_slist_alloc();
689 //str = g_string_sized_new(64);
692 memset(&jcr, 0, sizeof(jcr));
698 switch (item->type) {
700 dird = (DIRRES*)item->resource;
701 trayMessage("Connecting to Director %s:%d\n", dird->address, dird->DIRport);
702 changeStatusMessage(item, "Connecting to Director %s:%d", dird->address, dird->DIRport);
703 item->D_sock = bnet_connect(NULL, 0, 0, "Director daemon", dird->address, NULL, dird->DIRport, 0);
704 jcr.dir_bsock = item->D_sock;
707 filed = (CLIENT*)item->resource;
708 trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
709 changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
710 item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
711 jcr.file_bsock = item->D_sock;
714 stored = (STORE*)item->resource;
715 trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
716 changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
717 item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
718 jcr.store_bsock = item->D_sock;
721 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
726 if (item->D_sock == NULL) {
727 g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
728 changeStatusMessage(item, "Cannot connect to daemon.");
730 item->oldstate = error;
734 if (!authenticate_daemon(item, &jcr)) {
735 str = g_string_sized_new(64);
736 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
737 g_slist_append(*list, str);
739 item->oldstate = error;
740 changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
745 switch (item->type) {
747 trayMessage("Opened connection with Director daemon.\n");
748 changeStatusMessage(item, "Opened connection with Director daemon.");
751 trayMessage("Opened connection with File daemon.\n");
752 changeStatusMessage(item, "Opened connection with File daemon.");
755 trayMessage("Opened connection with Storage daemon.\n");
756 changeStatusMessage(item, "Opened connection with Storage daemon.");
759 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
765 if (item->type == R_DIRECTOR) { /* Read connection messages... */
767 docmd(item, "", &list); /* Usually invalid, but no matter */
770 if (it->data) g_string_free((GString*)it->data, TRUE);
771 } while ((it = it->next) != NULL);
778 writecmd(item, command);
781 if ((stat = bnet_recv(item->D_sock)) >= 0) {
782 g_slist_append(*list, g_string_new(item->D_sock->msg));
784 else if (stat == BNET_SIGNAL) {
785 if (item->D_sock->msglen == BNET_EOD) {
786 //fprintf(stderr, "<< EOD >>\n");
789 else if (item->D_sock->msglen == BNET_PROMPT) {
790 //fprintf(stderr, "<< PROMPT >>\n");
791 g_slist_append(*list, g_string_new("<< Error: BNET_PROMPT signal received. >>\n"));
794 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
795 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
796 g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
799 str = g_string_sized_new(64);
800 g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
801 g_slist_append(*list, str);
804 else { /* BNET_HARDEOF || BNET_ERROR */
805 g_slist_append(*list, g_string_new("<ERROR>\n"));
808 item->oldstate = error;
809 changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
810 //fprintf(stderr, "<< ERROR >>\n");
814 if (is_bnet_stop(item->D_sock)) {
815 g_string_append_printf(str, "<STOP>\n");
818 item->oldstate = error;
819 changeStatusMessage(item, "Error : Connection closed.");
820 //fprintf(stderr, "<< STOP >>\n");
821 return 0; /* error or term */
826 void writecmd(monitoritem* item, const char* command) {
828 item->D_sock->msglen = strlen(command);
829 pm_strcpy(&item->D_sock->msg, command);
830 bnet_send(item->D_sock);
834 /* Note: Does not seem to work either on Gnome nor KDE... */
835 void trayMessage(const char *fmt,...) {
839 va_start(arg_ptr, fmt);
840 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
843 fprintf(stderr, buf);
845 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
848 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
852 va_start(arg_ptr, fmt);
853 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
856 gtk_label_set_text(GTK_LABEL(item->label), buf);
859 void updateStatusIcon(monitoritem* item) {
863 /* For the current status, select the two worse for blinking,
864 but never blink a D_Sock == NULL error with idle. */
865 stateenum state1, state2, oldstate;
866 gboolean onenull = FALSE;
870 for (int i = 0; i < nitems; i++) {
871 if (items[i].D_sock == NULL) onenull = TRUE;
872 if (items[i].state >= state1) {
874 state1 = items[i].state;
876 else if (items[i].state > state2) {
877 state2 = items[i].state;
879 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
882 if ((onenull == TRUE) && (state2 == idle)) {
886 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
889 if ((blinkstate) && (item->D_sock != NULL)) {
890 if (item->state > 1) { //Warning or error while running
891 xpm = generateXPM(running, item->oldstate);
894 xpm = generateXPM(idle, item->oldstate);
898 xpm = generateXPM(item->state, item->oldstate);
902 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
904 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
905 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
908 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
912 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
913 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
914 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
917 strcpy(address, "ff0000");
920 strcpy(address, "ffffff");
923 strcpy(address, "00ff00");
926 strcpy(address, "ffff00");
930 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
933 strcpy(address, "ff0000");
936 strcpy(address, "ffffff");
939 strcpy(address, "00ff00");
942 strcpy(address, "ffff00");
946 return (const char**)xpm_generic_var;