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 #define TRAY_DEBUG_MEMORY 0
39 /* Imported functions */
40 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
41 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
42 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
44 /* Forward referenced functions */
45 void writecmd(monitoritem* item, const char* command);
46 int docmd(monitoritem* item, const char* command, GSList** list);
47 void getstatus(monitoritem* item, int current, GString** str);
49 /* Static variables */
50 static char *configfile = NULL;
51 static MONITOR *monitor;
53 static int nitems = 0;
54 static int fullitem = 0; //Item to be display in detailled status window
55 static int lastupdated = -1; //Last item updated
56 static monitoritem items[32];
58 /* Data received from DIR/FD/SD */
59 static char OKqstatus[] = "%c000 OK .status\n";
60 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
62 /* UI variables and functions */
65 static gboolean fd_read(gpointer data);
66 static gboolean blink(gpointer data);
69 void trayMessage(const char *fmt,...);
70 void updateStatusIcon(monitoritem* item);
71 void changeStatusMessage(monitoritem* item, const char *fmt,...);
72 static const char** generateXPM(stateenum newstate, stateenum oldstate);
75 static void TrayIconActivate(GtkWidget *widget, gpointer data);
76 static void TrayIconExit(unsigned int activateTime, unsigned int button);
77 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
78 static void MonitorAbout(GtkWidget *widget, gpointer data);
79 static void MonitorRefresh(GtkWidget *widget, gpointer data);
80 static void IntervalChanged(GtkWidget *widget, gpointer data);
81 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
82 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
84 static guint timerTag;
85 static EggStatusIcon *mTrayIcon;
86 static GtkWidget *mTrayMenu;
87 static GtkWidget *window;
88 static GtkWidget *textview;
89 static GtkTextBuffer *buffer;
90 static GtkWidget *timeoutspinner;
91 char** xpm_generic_var;
92 static gboolean blinkstate = TRUE;
94 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
99 "Copyright (C) 2000-2004 Kern Sibbald and John Walker\n"
100 "Written by Nicolas Boichat (2004)\n"
101 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
102 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
103 " -c <file> set configuration file to file\n"
104 " -dnn set debug level to nn\n"
105 " -t test - read configuration and exit\n"
106 " -? print this message.\n"
107 "\n"), HOST_OS, DISTNAME, DISTVER);
110 static GtkWidget *new_image_button(const gchar *stock_id,
111 const gchar *label_text) {
117 button = gtk_button_new();
119 box = gtk_hbox_new(FALSE, 0);
120 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
121 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
122 label = gtk_label_new(label_text);
124 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
125 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
127 gtk_widget_show(image);
128 gtk_widget_show(label);
130 gtk_widget_show(box);
132 gtk_container_add(GTK_CONTAINER(button), box);
139 #if TRAY_DEBUG_MEMORY
140 gpointer smt_malloc(gsize n_bytes) {
141 return sm_malloc("GLib", sm_line, n_bytes);
144 gpointer smt_realloc(gpointer mem, gsize n_bytes) {
145 return sm_realloc("GLib", sm_line, mem, n_bytes);
148 gpointer smt_calloc(gsize n_blocks,
149 gsize n_block_bytes) {
150 return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes);
153 void smt_free(gpointer mem) {
154 sm_free("Glib", sm_line, mem);
158 /*********************************************************************
160 * Main Bacula Tray Monitor -- User Interface Program
163 int main(int argc, char *argv[])
165 #if TRAY_DEBUG_MEMORY
167 smvtable.malloc = &smt_malloc;
168 smvtable.realloc = &smt_realloc;
169 smvtable.free = &smt_free;
170 smvtable.calloc = &smt_calloc;
171 smvtable.try_malloc = NULL;
172 smvtable.try_realloc = NULL;
173 g_mem_set_vtable(&smvtable);
177 bool test_config = false;
183 my_name_is(argc, argv, "tray-monitor");
184 textdomain("bacula");
185 init_msg(NULL, NULL);
186 working_directory = "/tmp";
188 struct sigaction sigignore;
189 sigignore.sa_flags = 0;
190 sigignore.sa_handler = SIG_IGN;
191 sigfillset(&sigignore.sa_mask);
192 sigaction(SIGPIPE, &sigignore, NULL);
194 gtk_init (&argc, &argv);
196 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
198 case 'c': /* configuration file */
199 if (configfile != NULL) {
202 configfile = bstrdup(optarg);
206 debug_level = atoi(optarg);
207 if (debug_level <= 0) {
231 if (configfile == NULL) {
232 configfile = bstrdup(CONFIG_FILE);
235 parse_config(configfile);
239 foreach_res(monitor, R_MONITOR) {
244 Emsg2(M_ERROR_TERM, 0,
245 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
249 foreach_res(dird, R_DIRECTOR) {
250 items[nitems].type = R_DIRECTOR;
251 items[nitems].resource = dird;
252 items[nitems].D_sock = NULL;
253 items[nitems].state = warn;
254 items[nitems].oldstate = warn;
257 foreach_res(filed, R_CLIENT) {
258 items[nitems].type = R_CLIENT;
259 items[nitems].resource = filed;
260 items[nitems].D_sock = NULL;
261 items[nitems].state = warn;
262 items[nitems].oldstate = warn;
265 foreach_res(stored, R_STORAGE) {
266 items[nitems].type = R_STORAGE;
267 items[nitems].resource = stored;
268 items[nitems].D_sock = NULL;
269 items[nitems].state = warn;
270 items[nitems].oldstate = warn;
276 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n"
277 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
284 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
285 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
286 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
287 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
288 strcpy(xpm_generic_var[i], xpm_generic[i]);
291 (void)WSA_Init(); /* Initialize Windows sockets */
294 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
297 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
298 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
299 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
302 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
303 // This should be ideally replaced by a completely libpr0n-based icon rendering.
304 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
305 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
306 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
307 g_object_unref(G_OBJECT(pixbuf));
309 mTrayMenu = gtk_menu_new();
313 entry = gtk_menu_item_new_with_label("Open status window...");
314 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
315 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
317 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
319 entry = gtk_menu_item_new_with_label("Exit");
320 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
321 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
323 gtk_widget_show_all(mTrayMenu);
325 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
327 g_timeout_add( 1000, blink, NULL );
329 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
331 gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
333 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
334 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
336 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
338 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
340 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
342 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
344 GtkWidget* separator = gtk_hseparator_new();
345 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
348 GSList *group = NULL;
352 for (int i = 0; i < nitems; i++) {
353 switch (items[i].type) {
355 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
356 g_string_append(str, _(" (DIR)"));
359 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
360 g_string_append(str, _(" (FD)"));
363 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
364 g_string_append(str, _(" (SD)"));
370 radio = gtk_radio_button_new_with_label(group, str->str);
371 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
372 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
374 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
375 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
377 items[i].label = gtk_label_new(_("Unknown status."));
378 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
379 gtk_container_add(GTK_CONTAINER(align), items[i].label);
381 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
382 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
383 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
384 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
385 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
386 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
388 separator = gtk_hseparator_new();
389 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
391 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
394 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
396 textview = gtk_text_view_new();
398 buffer = gtk_text_buffer_new(NULL);
400 gtk_text_buffer_set_text(buffer, "", -1);
402 PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
403 gtk_widget_modify_font(textview, font_desc);
404 pango_font_description_free(font_desc);
406 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
407 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
409 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
411 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
413 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
415 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
417 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
418 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
419 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
420 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
421 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
422 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
423 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
424 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
426 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
427 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
428 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
430 button = new_image_button("gtk-help", _("About"));
431 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
432 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
434 button = new_image_button("gtk-close", _("Close"));
435 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
436 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
438 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
440 gtk_container_add(GTK_CONTAINER (window), vbox);
442 gtk_widget_show_all(vbox);
446 g_source_remove(timerTag);
450 for (i = 0; i < nitems; i++) {
451 if (items[i].D_sock) {
452 writecmd(&items[i], "quit");
453 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
454 bnet_close(items[i].D_sock);
458 (void)WSACleanup(); /* Cleanup Windows sockets */
460 //Free xpm_generic_var
461 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
462 g_free(xpm_generic_var[i]);
464 g_free(xpm_generic_var);
466 gtk_object_destroy(GTK_OBJECT(window));
467 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
470 #if TRAY_DEBUG_MEMORY
477 static void MonitorAbout(GtkWidget *widget, gpointer data) {
479 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
480 "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
481 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
482 "Written by Nicolas Boichat\n"
483 "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
484 ), HOST_OS, DISTNAME, DISTVER);
486 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
487 "Bacula Tray Monitor\n\n"
488 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
489 "Written by Nicolas Boichat\n"
490 "\nVersion: " VERSION " (" BDATE ") %s %s %s"
491 ), HOST_OS, DISTNAME, DISTVER);
493 gtk_dialog_run(GTK_DIALOG(about));
494 gtk_widget_destroy(about);
497 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
498 for (int i = 0; i < nitems; i++) {
503 static gboolean delete_event( GtkWidget *widget,
506 gtk_widget_hide(window);
507 return TRUE; /* do not destroy the window */
510 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
511 gtk_widget_show(window);
514 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
515 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
516 gtk_widget_show_all(mTrayMenu);
519 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
523 static void IntervalChanged(GtkWidget *widget, gpointer data) {
524 g_source_remove(timerTag);
525 timerTag = g_timeout_add(
527 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
531 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
532 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
534 for (int i = 0; i < nitems; i++) {
535 if (data == &(items[i])) {
540 g_return_if_fail(fullitem != -1);
542 int oldlastupdated = lastupdated;
543 lastupdated = fullitem-1;
545 lastupdated = oldlastupdated;
549 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
550 switch (item->type) {
552 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
555 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
558 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
561 printf("Error, currentitem is not a Client or a Storage..\n");
567 static gboolean blink(gpointer data) {
568 blinkstate = !blinkstate;
569 for (int i = 0; i < nitems; i++) {
570 updateStatusIcon(&items[i]);
572 updateStatusIcon(NULL);
576 static gboolean fd_read(gpointer data) {
578 #if TRAY_DEBUG_MEMORY
579 printf("sm_line=%d\n", sm_line);
581 GtkTextBuffer *newbuffer;
582 GtkTextIter start, stop, nstart, nstop;
586 GString *strlast, *strcurrent;
589 if (lastupdated == nitems) {
593 if (lastupdated == fullitem) {
594 newbuffer = gtk_text_buffer_new(NULL);
596 if (items[lastupdated].type == R_DIRECTOR)
597 docmd(&items[lastupdated], "status Director\n", &list);
599 docmd(&items[lastupdated], "status\n", &list);
603 gtk_text_buffer_get_end_iter(newbuffer, &stop);
604 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
605 if (it->data) g_string_free((GString*)it->data, TRUE);
606 } while ((it = it->next) != NULL);
610 /* Keep the selection if necessary */
611 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
612 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
613 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
616 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
618 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
619 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
623 g_object_unref(buffer);
626 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
629 getstatus(&items[lastupdated], 1, &strcurrent);
630 getstatus(&items[lastupdated], 0, &strlast);
631 updateStatusIcon(&items[lastupdated]);
633 changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
635 updateStatusIcon(NULL);
637 g_string_free(strcurrent, TRUE);
638 g_string_free(strlast, TRUE);
643 void getstatus(monitoritem* item, int current, GString** str) {
645 stateenum ret = error;
646 int jobid = 0, joberrors = 0;
647 char jobstatus = JS_ErrorTerminated;
651 *str = g_string_sized_new(128);
654 if (item->type == R_DIRECTOR)
655 docmd(&items[lastupdated], ".status dir current\n", &list);
657 docmd(&items[lastupdated], ".status current\n", &list);
660 if (item->type == R_DIRECTOR)
661 docmd(&items[lastupdated], ".status dir last\n", &list);
663 docmd(&items[lastupdated], ".status last\n", &list);
667 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
668 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
669 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
670 g_string_set_size(*str, (*str)->len-1);
674 else if ((it = it->next) == NULL) {
676 g_string_append(*str, _("No current job."));
679 g_string_append(*str, _("No last job."));
683 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
686 ret = (joberrors > 0) ? warn : running;
687 g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
690 ret = (joberrors > 0) ? warn : running;
691 g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
694 g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
698 g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
699 ret = (joberrors > 0) ? warn : idle;
701 case JS_ErrorTerminated:
702 g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
706 ret = (joberrors > 0) ? warn : running;
707 g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
710 g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
714 g_string_append_printf(*str, _("Job status: Verify differences (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
718 g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
722 g_string_append_printf(*str, _("Job status: Waiting on File daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
726 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
730 g_string_append_printf(*str, _("Job status: Waiting for new media (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
734 g_string_append_printf(*str, _("Job status: Waiting for Mount (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
737 case JS_WaitStoreRes:
738 g_string_append_printf(*str, _("Job status: Waiting for storage resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
742 g_string_append_printf(*str, _("Job status: Waiting for job resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
745 case JS_WaitClientRes:
746 g_string_append_printf(*str, _("Job status: Waiting for Client resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
750 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
753 case JS_WaitStartTime:
754 g_string_append_printf(*str, _("Job status: Waiting for start time (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
757 case JS_WaitPriority:
758 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
762 g_warning("Unknown job status %c.", jobstatus);
763 g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
769 fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
775 if (it->data) g_string_free((GString*)it->data, TRUE);
776 } while ((it = it->next) != NULL);
784 item->oldstate = ret;
788 int docmd(monitoritem* item, const char* command, GSList** list) {
792 *list = g_slist_alloc();
794 //str = g_string_sized_new(64);
797 memset(&jcr, 0, sizeof(jcr));
803 switch (item->type) {
805 dird = (DIRRES*)item->resource;
806 trayMessage("Connecting to Director %s:%d\n", dird->address, dird->DIRport);
807 changeStatusMessage(item, "Connecting to Director %s:%d", dird->address, dird->DIRport);
808 item->D_sock = bnet_connect(NULL, 0, 0, "Director daemon", dird->address, NULL, dird->DIRport, 0);
809 jcr.dir_bsock = item->D_sock;
812 filed = (CLIENT*)item->resource;
813 trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
814 changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
815 item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
816 jcr.file_bsock = item->D_sock;
819 stored = (STORE*)item->resource;
820 trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
821 changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
822 item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
823 jcr.store_bsock = item->D_sock;
826 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
831 if (item->D_sock == NULL) {
832 g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
833 changeStatusMessage(item, "Cannot connect to daemon.");
835 item->oldstate = error;
839 if (!authenticate_daemon(item, &jcr)) {
840 str = g_string_sized_new(64);
841 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
842 g_slist_append(*list, str);
844 item->oldstate = error;
845 changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
850 switch (item->type) {
852 trayMessage("Opened connection with Director daemon.\n");
853 changeStatusMessage(item, "Opened connection with Director daemon.");
856 trayMessage("Opened connection with File daemon.\n");
857 changeStatusMessage(item, "Opened connection with File daemon.");
860 trayMessage("Opened connection with Storage daemon.\n");
861 changeStatusMessage(item, "Opened connection with Storage daemon.");
864 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
870 if (item->type == R_DIRECTOR) { /* Read connection messages... */
872 docmd(item, "", &list); /* Usually invalid, but no matter */
875 if (it->data) g_string_free((GString*)it->data, TRUE);
876 } while ((it = it->next) != NULL);
883 writecmd(item, command);
886 if ((stat = bnet_recv(item->D_sock)) >= 0) {
887 g_slist_append(*list, g_string_new(item->D_sock->msg));
889 else if (stat == BNET_SIGNAL) {
890 if (item->D_sock->msglen == BNET_EOD) {
891 //fprintf(stderr, "<< EOD >>\n");
894 else if (item->D_sock->msglen == BNET_PROMPT) {
895 //fprintf(stderr, "<< PROMPT >>\n");
896 g_slist_append(*list, g_string_new("<< Error: BNET_PROMPT signal received. >>\n"));
899 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
900 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
901 g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
904 str = g_string_sized_new(64);
905 g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
906 g_slist_append(*list, str);
909 else { /* BNET_HARDEOF || BNET_ERROR */
910 g_slist_append(*list, g_string_new("<ERROR>\n"));
913 item->oldstate = error;
914 changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
915 //fprintf(stderr, "<< ERROR >>\n");
919 if (is_bnet_stop(item->D_sock)) {
920 g_string_append_printf(str, "<STOP>\n");
923 item->oldstate = error;
924 changeStatusMessage(item, "Error : Connection closed.");
925 //fprintf(stderr, "<< STOP >>\n");
926 return 0; /* error or term */
931 void writecmd(monitoritem* item, const char* command) {
933 item->D_sock->msglen = strlen(command);
934 pm_strcpy(&item->D_sock->msg, command);
935 bnet_send(item->D_sock);
939 /* Note: Does not seem to work either on Gnome nor KDE... */
940 void trayMessage(const char *fmt,...) {
944 va_start(arg_ptr, fmt);
945 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
948 fprintf(stderr, buf);
950 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
953 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
957 va_start(arg_ptr, fmt);
958 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
961 gtk_label_set_text(GTK_LABEL(item->label), buf);
964 void updateStatusIcon(monitoritem* item) {
968 /* For the current status, select the two worse for blinking,
969 but never blink a D_Sock == NULL error with idle. */
970 stateenum state1, state2, oldstate;
971 gboolean onenull = FALSE;
975 for (int i = 0; i < nitems; i++) {
976 if (items[i].D_sock == NULL) onenull = TRUE;
977 if (items[i].state >= state1) {
979 state1 = items[i].state;
981 else if (items[i].state > state2) {
982 state2 = items[i].state;
984 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
987 if ((onenull == TRUE) && (state2 == idle)) {
991 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
994 if ((blinkstate) && (item->D_sock != NULL)) {
995 if (item->state > 1) { //Warning or error while running
996 xpm = generateXPM(running, item->oldstate);
999 xpm = generateXPM(idle, item->oldstate);
1003 xpm = generateXPM(item->state, item->oldstate);
1007 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1009 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1010 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1013 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1015 g_object_unref(G_OBJECT(pixbuf));
1018 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1019 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1020 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1023 strcpy(address, "ff0000");
1026 strcpy(address, "ffffff");
1029 strcpy(address, "00ff00");
1032 strcpy(address, "ffff00");
1036 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1039 strcpy(address, "ff0000");
1042 strcpy(address, "ffffff");
1045 strcpy(address, "00ff00");
1048 strcpy(address, "ffff00");
1052 return (const char**)xpm_generic_var;