3 * Bacula Gnome Tray Monitor
5 * Nicolas Boichat, August MMIV
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2004-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
38 #include "tray-monitor.h"
40 #include "eggstatusicon.h"
42 #include "generic.xpm"
44 #define TRAY_DEBUG_MEMORY 0
46 /* Imported functions */
47 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
48 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
49 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
52 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
54 /* Forward referenced functions */
55 void writecmd(monitoritem* item, const char* command);
56 int docmd(monitoritem* item, const char* command, GSList** list);
57 void getstatus(monitoritem* item, int current, GString** str);
59 /* Static variables */
60 static char *configfile = NULL;
61 static MONITOR *monitor;
63 static int nitems = 0;
64 static int fullitem = 0; //Item to be display in detailled status window
65 static int lastupdated = -1; //Last item updated
66 static monitoritem items[32];
68 /* Data received from DIR/FD/SD */
69 static char OKqstatus[] = "%c000 OK .status\n";
70 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
72 /* UI variables and functions */
75 static gboolean fd_read(gpointer data);
76 static gboolean blink(gpointer data);
79 void trayMessage(const char *fmt,...);
80 void updateStatusIcon(monitoritem* item);
81 void changeStatusMessage(monitoritem* item, const char *fmt,...);
82 static const char** generateXPM(stateenum newstate, stateenum oldstate);
85 static void TrayIconActivate(GtkWidget *widget, gpointer data);
86 static void TrayIconExit(unsigned int activateTime, unsigned int button);
87 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
88 static void MonitorAbout(GtkWidget *widget, gpointer data);
89 static void MonitorRefresh(GtkWidget *widget, gpointer data);
90 static void IntervalChanged(GtkWidget *widget, gpointer data);
91 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
92 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
94 static guint timerTag;
95 static EggStatusIcon *mTrayIcon;
96 static GtkWidget *mTrayMenu;
97 static GtkWidget *window;
98 static GtkWidget *textview;
99 static GtkTextBuffer *buffer;
100 static GtkWidget *timeoutspinner;
101 char** xpm_generic_var;
102 static gboolean blinkstate = TRUE;
104 PangoFontDescription *font_desc = NULL;
106 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
112 "Written by Nicolas Boichat (2004)\n"
113 "\nVersion: %s (%s) %s %s %s\n\n"
114 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
115 " -c <file> set configuration file to file\n"
116 " -dnn set debug level to nn\n"
117 " -t test - read configuration and exit\n"
118 " -? print this message.\n"
119 "\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
122 static GtkWidget *new_image_button(const gchar *stock_id,
123 const gchar *label_text) {
129 button = gtk_button_new();
131 box = gtk_hbox_new(FALSE, 0);
132 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
133 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
134 label = gtk_label_new(label_text);
136 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
137 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
139 gtk_widget_show(image);
140 gtk_widget_show(label);
142 gtk_widget_show(box);
144 gtk_container_add(GTK_CONTAINER(button), box);
151 #if TRAY_DEBUG_MEMORY
152 gpointer smt_malloc(gsize n_bytes) {
153 return sm_malloc("GLib", sm_line, n_bytes);
156 gpointer smt_realloc(gpointer mem, gsize n_bytes) {
157 return sm_realloc("GLib", sm_line, mem, n_bytes);
160 gpointer smt_calloc(gsize n_blocks,
161 gsize n_block_bytes) {
162 return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes);
165 void smt_free(gpointer mem) {
166 sm_free("Glib", sm_line, mem);
170 /*********************************************************************
172 * Main Bacula Tray Monitor -- User Interface Program
175 int main(int argc, char *argv[])
177 #if TRAY_DEBUG_MEMORY
179 smvtable.malloc = &smt_malloc;
180 smvtable.realloc = &smt_realloc;
181 smvtable.free = &smt_free;
182 smvtable.calloc = &smt_calloc;
183 smvtable.try_malloc = NULL;
184 smvtable.try_realloc = NULL;
185 g_mem_set_vtable(&smvtable);
189 bool test_config = false;
193 CONFONTRES *con_font;
195 setlocale(LC_ALL, "");
196 bindtextdomain("bacula", LOCALEDIR);
197 textdomain("bacula");
200 my_name_is(argc, argv, "tray-monitor");
201 init_msg(NULL, NULL);
202 working_directory = "/tmp";
204 struct sigaction sigignore;
205 sigignore.sa_flags = 0;
206 sigignore.sa_handler = SIG_IGN;
207 sigfillset(&sigignore.sa_mask);
208 sigaction(SIGPIPE, &sigignore, NULL);
210 gtk_init (&argc, &argv);
212 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
214 case 'c': /* configuration file */
215 if (configfile != NULL) {
218 configfile = bstrdup(optarg);
222 debug_level = atoi(optarg);
223 if (debug_level <= 0) {
247 if (configfile == NULL) {
248 configfile = bstrdup(CONFIG_FILE);
251 parse_config(configfile);
255 foreach_res(monitor, R_MONITOR) {
260 Emsg2(M_ERROR_TERM, 0,
261 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
265 foreach_res(dird, R_DIRECTOR) {
266 items[nitems].type = R_DIRECTOR;
267 items[nitems].resource = dird;
268 items[nitems].D_sock = NULL;
269 items[nitems].state = warn;
270 items[nitems].oldstate = warn;
273 foreach_res(filed, R_CLIENT) {
274 items[nitems].type = R_CLIENT;
275 items[nitems].resource = filed;
276 items[nitems].D_sock = NULL;
277 items[nitems].state = warn;
278 items[nitems].oldstate = warn;
281 foreach_res(stored, R_STORAGE) {
282 items[nitems].type = R_STORAGE;
283 items[nitems].resource = stored;
284 items[nitems].D_sock = NULL;
285 items[nitems].state = warn;
286 items[nitems].oldstate = warn;
292 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n"
293 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
300 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
301 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
302 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
303 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
304 strcpy(xpm_generic_var[i], xpm_generic[i]);
307 (void)WSA_Init(); /* Initialize Windows sockets */
310 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
313 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
314 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
315 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
318 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
319 // This should be ideally replaced by a completely libpr0n-based icon rendering.
320 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
321 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
322 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
323 g_object_unref(G_OBJECT(pixbuf));
325 mTrayMenu = gtk_menu_new();
329 entry = gtk_menu_item_new_with_label(_("Open status window..."));
330 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
331 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
333 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
335 entry = gtk_menu_item_new_with_label(_("Exit"));
336 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
337 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
339 gtk_widget_show_all(mTrayMenu);
341 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
343 g_timeout_add( 1000, blink, NULL );
345 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
347 gtk_window_set_title(GTK_WINDOW(window), _("Bacula tray monitor"));
349 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
350 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
352 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
354 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
356 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
358 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
360 GtkWidget* separator = gtk_hseparator_new();
361 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
364 GSList *group = NULL;
368 for (int i = 0; i < nitems; i++) {
369 switch (items[i].type) {
371 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
372 g_string_append(str, _(" (DIR)"));
375 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
376 g_string_append(str, _(" (FD)"));
379 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
380 g_string_append(str, _(" (SD)"));
386 radio = gtk_radio_button_new_with_label(group, str->str);
387 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
388 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
390 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
391 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
393 items[i].label = gtk_label_new(_("Unknown status."));
394 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
395 gtk_container_add(GTK_CONTAINER(align), items[i].label);
397 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
398 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
399 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
400 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
401 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
402 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
404 separator = gtk_hseparator_new();
405 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
407 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
410 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
412 textview = gtk_text_view_new();
414 buffer = gtk_text_buffer_new(NULL);
416 gtk_text_buffer_set_text(buffer, "", -1);
419 * Gtk2/pango have different font names. Gnome2 comes with "Monospace 10"
423 foreach_res(con_font, R_CONSOLE_FONT) {
424 if (!con_font->fontface) {
425 Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
428 Dmsg1(100, "Now loading: %s\n",con_font->fontface);
429 font_desc = pango_font_description_from_string(con_font->fontface);
430 if (font_desc == NULL) {
431 Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
432 con_font->hdr.name, con_font->fontface);
434 Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
435 con_font->hdr.name, con_font->fontface);
442 font_desc = pango_font_description_from_string("Monospace 10");
445 font_desc = pango_font_description_from_string("monospace");
448 gtk_widget_modify_font(textview, font_desc);
449 pango_font_description_free(font_desc);
451 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
452 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
454 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
456 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
458 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
460 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
462 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
463 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
464 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
465 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
466 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
467 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
468 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
469 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
471 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
472 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
473 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
475 button = new_image_button("gtk-help", _("About"));
476 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
477 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
479 button = new_image_button("gtk-close", _("Close"));
480 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
481 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
483 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
485 gtk_container_add(GTK_CONTAINER (window), vbox);
487 gtk_widget_show_all(vbox);
491 g_source_remove(timerTag);
495 for (i = 0; i < nitems; i++) {
496 if (items[i].D_sock) {
497 switch (items[i].type) {
499 trayMessage(_("Disconnecting from Director %s:%d\n"), ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport);
502 trayMessage(_("Disconnecting from Client %s:%d\n"), ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport);
505 trayMessage(_("Disconnecting from Storage %s:%d\n"), ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport);
510 //writecmd(&items[i], "quit");
511 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
512 bnet_close(items[i].D_sock);
516 (void)WSACleanup(); /* Cleanup Windows sockets */
518 //Free xpm_generic_var
519 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
520 g_free(xpm_generic_var[i]);
522 g_free(xpm_generic_var);
524 gtk_object_destroy(GTK_OBJECT(window));
525 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
528 #if TRAY_DEBUG_MEMORY
535 static void MonitorAbout(GtkWidget *widget, gpointer data) {
537 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
538 "<span size='x-large' weight='bold'>%s</span>\n\n"
541 "\n<small>%s: %s (%s) %s %s %s</small>",
542 _("Bacula Tray Monitor"),
544 _("Written by Nicolas Boichat\n"),
546 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
548 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
552 "\n%s: %s (%s) %s %s %s",
553 _("Bacula Tray Monitor"),
555 _("Written by Nicolas Boichat\n"),
557 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
559 gtk_dialog_run(GTK_DIALOG(about));
560 gtk_widget_destroy(about);
563 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
564 for (int i = 0; i < nitems; i++) {
569 static gboolean delete_event( GtkWidget *widget,
572 gtk_widget_hide(window);
573 return TRUE; /* do not destroy the window */
576 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
577 gtk_widget_show(window);
580 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
581 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
582 gtk_widget_show_all(mTrayMenu);
585 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
589 static void IntervalChanged(GtkWidget *widget, gpointer data) {
590 g_source_remove(timerTag);
591 timerTag = g_timeout_add(
593 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
597 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
598 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
600 for (int i = 0; i < nitems; i++) {
601 if (data == &(items[i])) {
606 g_return_if_fail(fullitem != -1);
608 int oldlastupdated = lastupdated;
609 lastupdated = fullitem-1;
611 lastupdated = oldlastupdated;
615 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
616 switch (item->type) {
618 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
620 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
622 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
624 printf(_("Error, currentitem is not a Client or a Storage..\n"));
631 static gboolean blink(gpointer data) {
632 blinkstate = !blinkstate;
633 for (int i = 0; i < nitems; i++) {
634 updateStatusIcon(&items[i]);
636 updateStatusIcon(NULL);
640 static gboolean fd_read(gpointer data)
643 #if TRAY_DEBUG_MEMORY
644 printf("sm_line=%d\n", sm_line);
646 GtkTextBuffer *newbuffer;
647 GtkTextIter start, stop, nstart, nstop;
651 GString *strlast, *strcurrent;
654 if (lastupdated == nitems) {
658 if (lastupdated == fullitem) {
659 newbuffer = gtk_text_buffer_new(NULL);
661 if (items[lastupdated].type == R_DIRECTOR)
662 docmd(&items[lastupdated], "status Director\n", &list);
664 docmd(&items[lastupdated], "status\n", &list);
668 gtk_text_buffer_get_end_iter(newbuffer, &stop);
669 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
670 if (it->data) g_string_free((GString*)it->data, TRUE);
671 } while ((it = it->next) != NULL);
675 /* Keep the selection if necessary */
676 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
677 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
678 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
681 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
683 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
684 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
688 g_object_unref(buffer);
691 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
694 getstatus(&items[lastupdated], 1, &strcurrent);
695 getstatus(&items[lastupdated], 0, &strlast);
696 updateStatusIcon(&items[lastupdated]);
698 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
700 updateStatusIcon(NULL);
702 g_string_free(strcurrent, TRUE);
703 g_string_free(strlast, TRUE);
708 void append_error_string(GString* str, int joberrors) {
710 g_string_append_printf(str, _(" (%d errors)"), joberrors);
713 g_string_append_printf(str, _(" (%d error)"), joberrors);
717 void getstatus(monitoritem* item, int current, GString** str)
720 stateenum ret = error;
721 int jobid = 0, joberrors = 0;
722 char jobstatus = JS_ErrorTerminated;
726 *str = g_string_sized_new(128);
729 if (item->type == R_DIRECTOR)
730 docmd(&items[lastupdated], ".status dir current\n", &list);
732 docmd(&items[lastupdated], ".status current\n", &list);
735 if (item->type == R_DIRECTOR)
736 docmd(&items[lastupdated], ".status dir last\n", &list);
738 docmd(&items[lastupdated], ".status last\n", &list);
742 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
743 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
744 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
745 g_string_set_size(*str, (*str)->len-1);
749 else if ((it = it->next) == NULL) {
751 g_string_append(*str, _("No current job."));
754 g_string_append(*str, _("No last job."));
758 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
761 ret = (joberrors > 0) ? warn : running;
762 g_string_append_printf(*str, _("Job status: Created"));
763 append_error_string(*str, joberrors);
766 ret = (joberrors > 0) ? warn : running;
767 g_string_append_printf(*str, _("Job status: Running"));
768 append_error_string(*str, joberrors);
771 g_string_append_printf(*str, _("Job status: Blocked"));
772 append_error_string(*str, joberrors);
776 g_string_append_printf(*str, _("Job status: Terminated"));
777 append_error_string(*str, joberrors);
778 ret = (joberrors > 0) ? warn : idle;
780 case JS_ErrorTerminated:
781 g_string_append_printf(*str, _("Job status: Terminated in error"));
782 append_error_string(*str, joberrors);
786 ret = (joberrors > 0) ? warn : running;
787 g_string_append_printf(*str, _("Job status: Error"));
788 append_error_string(*str, joberrors);
791 g_string_append_printf(*str, _("Job status: Fatal error"));
792 append_error_string(*str, joberrors);
796 g_string_append_printf(*str, _("Job status: Verify differences"));
797 append_error_string(*str, joberrors);
801 g_string_append_printf(*str, _("Job status: Canceled"));
802 append_error_string(*str, joberrors);
806 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
807 append_error_string(*str, joberrors);
811 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
812 append_error_string(*str, joberrors);
816 g_string_append_printf(*str, _("Job status: Waiting for new media"));
817 append_error_string(*str, joberrors);
821 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
822 append_error_string(*str, joberrors);
825 case JS_WaitStoreRes:
826 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
827 append_error_string(*str, joberrors);
831 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
832 append_error_string(*str, joberrors);
835 case JS_WaitClientRes:
836 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
837 append_error_string(*str, joberrors);
841 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
842 append_error_string(*str, joberrors);
845 case JS_WaitStartTime:
846 g_string_append_printf(*str, _("Job status: Waiting for start time"));
847 append_error_string(*str, joberrors);
850 case JS_WaitPriority:
851 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
852 append_error_string(*str, joberrors);
856 g_warning(_("Unknown job status %c."), jobstatus);
857 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
858 append_error_string(*str, joberrors);
864 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
871 g_string_free((GString*)it->data, TRUE);
873 } while ((it = it->next) != NULL);
881 item->oldstate = ret;
885 int docmd(monitoritem* item, const char* command, GSList** list)
890 *list = g_slist_alloc();
892 //str = g_string_sized_new(64);
895 memset(&jcr, 0, sizeof(jcr));
901 switch (item->type) {
903 dird = (DIRRES*)item->resource;
904 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
905 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
906 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
907 jcr.dir_bsock = item->D_sock;
910 filed = (CLIENT*)item->resource;
911 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
912 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
913 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
914 jcr.file_bsock = item->D_sock;
917 stored = (STORE*)item->resource;
918 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
919 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
920 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
921 jcr.store_bsock = item->D_sock;
924 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
929 if (item->D_sock == NULL) {
930 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
931 changeStatusMessage(item, _("Cannot connect to daemon."));
933 item->oldstate = error;
937 if (!authenticate_daemon(item, &jcr)) {
938 str = g_string_sized_new(64);
939 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
940 *list = g_slist_append(*list, str);
942 item->oldstate = error;
943 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
948 switch (item->type) {
950 trayMessage(_("Opened connection with Director daemon.\n"));
951 changeStatusMessage(item, _("Opened connection with Director daemon."));
954 trayMessage(_("Opened connection with File daemon.\n"));
955 changeStatusMessage(item, _("Opened connection with File daemon."));
958 trayMessage(_("Opened connection with Storage daemon.\n"));
959 changeStatusMessage(item, _("Opened connection with Storage daemon."));
962 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
968 if (item->type == R_DIRECTOR) { /* Read connection messages... */
970 docmd(item, "", &tlist); /* Usually invalid, but no matter */
974 g_string_free((GString*)it->data, TRUE);
976 } while ((it = it->next) != NULL);
983 writecmd(item, command);
986 if ((stat = bnet_recv(item->D_sock)) >= 0) {
987 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
989 else if (stat == BNET_SIGNAL) {
990 if (item->D_sock->msglen == BNET_EOD) {
991 //fprintf(stderr, "<< EOD >>\n");
994 else if (item->D_sock->msglen == BNET_PROMPT) {
995 //fprintf(stderr, "<< PROMPT >>\n");
996 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
999 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
1000 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
1001 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
1004 str = g_string_sized_new(64);
1005 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
1006 *list = g_slist_append(*list, str);
1009 else { /* BNET_HARDEOF || BNET_ERROR */
1010 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
1011 item->D_sock = NULL;
1012 item->state = error;
1013 item->oldstate = error;
1014 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
1015 //fprintf(stderr, _("<< ERROR >>\n"));
1019 if (is_bnet_stop(item->D_sock)) {
1020 g_string_append_printf(str, _("<STOP>\n"));
1021 item->D_sock = NULL;
1022 item->state = error;
1023 item->oldstate = error;
1024 changeStatusMessage(item, _("Error : Connection closed."));
1025 //fprintf(stderr, "<< STOP >>\n");
1026 return 0; /* error or term */
1031 void writecmd(monitoritem* item, const char* command) {
1033 item->D_sock->msglen = strlen(command);
1034 pm_strcpy(&item->D_sock->msg, command);
1035 bnet_send(item->D_sock);
1039 /* Note: Does not seem to work either on Gnome nor KDE... */
1040 void trayMessage(const char *fmt,...)
1045 va_start(arg_ptr, fmt);
1046 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1049 fprintf(stderr, buf);
1051 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1054 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1058 va_start(arg_ptr, fmt);
1059 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1062 gtk_label_set_text(GTK_LABEL(item->label), buf);
1065 void updateStatusIcon(monitoritem* item) {
1069 /* For the current status, select the two worse for blinking,
1070 but never blink a D_Sock == NULL error with idle. */
1071 stateenum state1, state2, oldstate;
1072 gboolean onenull = FALSE;
1076 for (int i = 0; i < nitems; i++) {
1077 if (items[i].D_sock == NULL) onenull = TRUE;
1078 if (items[i].state >= state1) {
1080 state1 = items[i].state;
1082 else if (items[i].state > state2) {
1083 state2 = items[i].state;
1085 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1088 if ((onenull == TRUE) && (state2 == idle)) {
1092 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1095 if ((blinkstate) && (item->D_sock != NULL)) {
1096 if (item->state > 1) { //Warning or error while running
1097 xpm = generateXPM(running, item->oldstate);
1100 xpm = generateXPM(idle, item->oldstate);
1104 xpm = generateXPM(item->state, item->oldstate);
1108 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1110 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1111 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1114 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1116 g_object_unref(G_OBJECT(pixbuf));
1119 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1120 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1121 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1124 strcpy(address, "ff0000");
1127 strcpy(address, "ffffff");
1130 strcpy(address, "00ff00");
1133 strcpy(address, "ffff00");
1137 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1140 strcpy(address, "ff0000");
1143 strcpy(address, "ffffff");
1146 strcpy(address, "00ff00");
1149 strcpy(address, "ffff00");
1153 return (const char**)xpm_generic_var;