3 * Bacula Gnome Tray Monitor
5 * Nicolas Boichat, August MMIV
11 Copyright (C) 2004-2006 Kern Sibbald
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License
15 version 2 as amended with additional clauses defined in the
16 file LICENSE in the main source directory.
18 This program 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
21 the file LICENSE for additional details.
26 #include "tray-monitor.h"
28 #include "eggstatusicon.h"
30 #include "generic.xpm"
32 #define TRAY_DEBUG_MEMORY 0
34 /* Imported functions */
35 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
36 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
37 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
40 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
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;
51 static int nitems = 0;
52 static int fullitem = 0; //Item to be display in detailled status window
53 static int lastupdated = -1; //Last item updated
54 static monitoritem items[32];
56 /* Data received from DIR/FD/SD */
57 static char OKqstatus[] = "%c000 OK .status\n";
58 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
60 /* UI variables and functions */
63 static gboolean fd_read(gpointer data);
64 static gboolean blink(gpointer data);
67 void trayMessage(const char *fmt,...);
68 void updateStatusIcon(monitoritem* item);
69 void changeStatusMessage(monitoritem* item, const char *fmt,...);
70 static const char** generateXPM(stateenum newstate, stateenum oldstate);
73 static void TrayIconActivate(GtkWidget *widget, gpointer data);
74 static void TrayIconExit(unsigned int activateTime, unsigned int button);
75 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
76 static void MonitorAbout(GtkWidget *widget, gpointer data);
77 static void MonitorRefresh(GtkWidget *widget, gpointer data);
78 static void IntervalChanged(GtkWidget *widget, gpointer data);
79 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
80 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
82 static guint timerTag;
83 static EggStatusIcon *mTrayIcon;
84 static GtkWidget *mTrayMenu;
85 static GtkWidget *window;
86 static GtkWidget *textview;
87 static GtkTextBuffer *buffer;
88 static GtkWidget *timeoutspinner;
89 char** xpm_generic_var;
90 static gboolean blinkstate = TRUE;
92 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
97 "Copyright (C) 2000-%s Kern Sibbald\n"
98 "Written by Nicolas Boichat (2004)\n"
99 "\nVersion: %s (%s) %s %s %s\n\n"
100 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
101 " -c <file> set configuration file to file\n"
102 " -dnn set debug level to nn\n"
103 " -t test - read configuration and exit\n"
104 " -? print this message.\n"
105 "\n"), BYEAR, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
108 static GtkWidget *new_image_button(const gchar *stock_id,
109 const gchar *label_text) {
115 button = gtk_button_new();
117 box = gtk_hbox_new(FALSE, 0);
118 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
119 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
120 label = gtk_label_new(label_text);
122 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
123 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
125 gtk_widget_show(image);
126 gtk_widget_show(label);
128 gtk_widget_show(box);
130 gtk_container_add(GTK_CONTAINER(button), box);
137 #if TRAY_DEBUG_MEMORY
138 gpointer smt_malloc(gsize n_bytes) {
139 return sm_malloc("GLib", sm_line, n_bytes);
142 gpointer smt_realloc(gpointer mem, gsize n_bytes) {
143 return sm_realloc("GLib", sm_line, mem, n_bytes);
146 gpointer smt_calloc(gsize n_blocks,
147 gsize n_block_bytes) {
148 return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes);
151 void smt_free(gpointer mem) {
152 sm_free("Glib", sm_line, mem);
156 /*********************************************************************
158 * Main Bacula Tray Monitor -- User Interface Program
161 int main(int argc, char *argv[])
163 #if TRAY_DEBUG_MEMORY
165 smvtable.malloc = &smt_malloc;
166 smvtable.realloc = &smt_realloc;
167 smvtable.free = &smt_free;
168 smvtable.calloc = &smt_calloc;
169 smvtable.try_malloc = NULL;
170 smvtable.try_realloc = NULL;
171 g_mem_set_vtable(&smvtable);
175 bool test_config = false;
180 setlocale(LC_ALL, "");
181 bindtextdomain("bacula", LOCALEDIR);
182 textdomain("bacula");
185 my_name_is(argc, argv, "tray-monitor");
186 init_msg(NULL, NULL);
187 working_directory = "/tmp";
189 struct sigaction sigignore;
190 sigignore.sa_flags = 0;
191 sigignore.sa_handler = SIG_IGN;
192 sigfillset(&sigignore.sa_mask);
193 sigaction(SIGPIPE, &sigignore, NULL);
195 gtk_init (&argc, &argv);
197 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
199 case 'c': /* configuration file */
200 if (configfile != NULL) {
203 configfile = bstrdup(optarg);
207 debug_level = atoi(optarg);
208 if (debug_level <= 0) {
232 if (configfile == NULL) {
233 configfile = bstrdup(CONFIG_FILE);
236 parse_config(configfile);
240 foreach_res(monitor, R_MONITOR) {
245 Emsg2(M_ERROR_TERM, 0,
246 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
250 foreach_res(dird, R_DIRECTOR) {
251 items[nitems].type = R_DIRECTOR;
252 items[nitems].resource = dird;
253 items[nitems].D_sock = NULL;
254 items[nitems].state = warn;
255 items[nitems].oldstate = warn;
258 foreach_res(filed, R_CLIENT) {
259 items[nitems].type = R_CLIENT;
260 items[nitems].resource = filed;
261 items[nitems].D_sock = NULL;
262 items[nitems].state = warn;
263 items[nitems].oldstate = warn;
266 foreach_res(stored, R_STORAGE) {
267 items[nitems].type = R_STORAGE;
268 items[nitems].resource = stored;
269 items[nitems].D_sock = NULL;
270 items[nitems].state = warn;
271 items[nitems].oldstate = warn;
277 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n"
278 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
285 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
286 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
287 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
288 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
289 strcpy(xpm_generic_var[i], xpm_generic[i]);
292 (void)WSA_Init(); /* Initialize Windows sockets */
295 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
298 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
299 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
300 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
303 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
304 // This should be ideally replaced by a completely libpr0n-based icon rendering.
305 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
306 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
307 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
308 g_object_unref(G_OBJECT(pixbuf));
310 mTrayMenu = gtk_menu_new();
314 entry = gtk_menu_item_new_with_label(_("Open status window..."));
315 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
316 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
318 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
320 entry = gtk_menu_item_new_with_label(_("Exit"));
321 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
322 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
324 gtk_widget_show_all(mTrayMenu);
326 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
328 g_timeout_add( 1000, blink, NULL );
330 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
332 gtk_window_set_title(GTK_WINDOW(window), _("Bacula tray monitor"));
334 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
335 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
337 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
339 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
341 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
343 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
345 GtkWidget* separator = gtk_hseparator_new();
346 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
349 GSList *group = NULL;
353 for (int i = 0; i < nitems; i++) {
354 switch (items[i].type) {
356 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
357 g_string_append(str, _(" (DIR)"));
360 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
361 g_string_append(str, _(" (FD)"));
364 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
365 g_string_append(str, _(" (SD)"));
371 radio = gtk_radio_button_new_with_label(group, str->str);
372 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
373 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
375 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
376 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
378 items[i].label = gtk_label_new(_("Unknown status."));
379 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
380 gtk_container_add(GTK_CONTAINER(align), items[i].label);
382 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
383 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
384 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
385 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
386 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
387 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
389 separator = gtk_hseparator_new();
390 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
392 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
395 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
397 textview = gtk_text_view_new();
399 buffer = gtk_text_buffer_new(NULL);
401 gtk_text_buffer_set_text(buffer, "", -1);
403 PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
404 gtk_widget_modify_font(textview, font_desc);
405 pango_font_description_free(font_desc);
407 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
408 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
410 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
412 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
414 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
416 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
418 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
419 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
420 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
421 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
422 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
423 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
424 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
425 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
427 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
428 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
429 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
431 button = new_image_button("gtk-help", _("About"));
432 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
433 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
435 button = new_image_button("gtk-close", _("Close"));
436 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
437 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
439 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
441 gtk_container_add(GTK_CONTAINER (window), vbox);
443 gtk_widget_show_all(vbox);
447 g_source_remove(timerTag);
451 for (i = 0; i < nitems; i++) {
452 if (items[i].D_sock) {
453 switch (items[i].type) {
455 trayMessage(_("Disconnecting from Director %s:%d\n"), ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport);
458 trayMessage(_("Disconnecting from Client %s:%d\n"), ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport);
461 trayMessage(_("Disconnecting from Storage %s:%d\n"), ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport);
466 //writecmd(&items[i], "quit");
467 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
468 bnet_close(items[i].D_sock);
472 (void)WSACleanup(); /* Cleanup Windows sockets */
474 //Free xpm_generic_var
475 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
476 g_free(xpm_generic_var[i]);
478 g_free(xpm_generic_var);
480 gtk_object_destroy(GTK_OBJECT(window));
481 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
484 #if TRAY_DEBUG_MEMORY
491 static void MonitorAbout(GtkWidget *widget, gpointer data) {
493 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
494 "<span size='x-large' weight='bold'>%s</span>\n\n"
496 "\n<small>%s: %s (%s) %s %s %s</small>",
497 _("Bacula Tray Monitor"),
498 _("Copyright (C) 2004-2006 Kern Sibbald\n"
499 "Written by Nicolas Boichat\n"),
501 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
503 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
506 "\n%s %s (%s) %s %s %s",
507 _("Bacula Tray Monitor"),
508 _("Copyright (C) 2004-2006 Kern Sibbald\n"
509 "Written by Nicolas Boichat\n"),
511 BYEAR, VERSION, BDATE, HOST_OS, DISTNAME);
513 gtk_dialog_run(GTK_DIALOG(about));
514 gtk_widget_destroy(about);
517 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
518 for (int i = 0; i < nitems; i++) {
523 static gboolean delete_event( GtkWidget *widget,
526 gtk_widget_hide(window);
527 return TRUE; /* do not destroy the window */
530 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
531 gtk_widget_show(window);
534 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
535 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
536 gtk_widget_show_all(mTrayMenu);
539 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
543 static void IntervalChanged(GtkWidget *widget, gpointer data) {
544 g_source_remove(timerTag);
545 timerTag = g_timeout_add(
547 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
551 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
552 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
554 for (int i = 0; i < nitems; i++) {
555 if (data == &(items[i])) {
560 g_return_if_fail(fullitem != -1);
562 int oldlastupdated = lastupdated;
563 lastupdated = fullitem-1;
565 lastupdated = oldlastupdated;
569 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
570 switch (item->type) {
572 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
574 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
576 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
578 printf(_("Error, currentitem is not a Client or a Storage..\n"));
585 static gboolean blink(gpointer data) {
586 blinkstate = !blinkstate;
587 for (int i = 0; i < nitems; i++) {
588 updateStatusIcon(&items[i]);
590 updateStatusIcon(NULL);
594 static gboolean fd_read(gpointer data)
597 #if TRAY_DEBUG_MEMORY
598 printf("sm_line=%d\n", sm_line);
600 GtkTextBuffer *newbuffer;
601 GtkTextIter start, stop, nstart, nstop;
605 GString *strlast, *strcurrent;
608 if (lastupdated == nitems) {
612 if (lastupdated == fullitem) {
613 newbuffer = gtk_text_buffer_new(NULL);
615 if (items[lastupdated].type == R_DIRECTOR)
616 docmd(&items[lastupdated], "status Director\n", &list);
618 docmd(&items[lastupdated], "status\n", &list);
622 gtk_text_buffer_get_end_iter(newbuffer, &stop);
623 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
624 if (it->data) g_string_free((GString*)it->data, TRUE);
625 } while ((it = it->next) != NULL);
629 /* Keep the selection if necessary */
630 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
631 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
632 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
635 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
637 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
638 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
642 g_object_unref(buffer);
645 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
648 getstatus(&items[lastupdated], 1, &strcurrent);
649 getstatus(&items[lastupdated], 0, &strlast);
650 updateStatusIcon(&items[lastupdated]);
652 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
654 updateStatusIcon(NULL);
656 g_string_free(strcurrent, TRUE);
657 g_string_free(strlast, TRUE);
662 void append_error_string(GString* str, int joberrors) {
664 g_string_append_printf(str, _(" (%d errors)"), joberrors);
667 g_string_append_printf(str, _(" (%d error)"), joberrors);
671 void getstatus(monitoritem* item, int current, GString** str)
674 stateenum ret = error;
675 int jobid = 0, joberrors = 0;
676 char jobstatus = JS_ErrorTerminated;
680 *str = g_string_sized_new(128);
683 if (item->type == R_DIRECTOR)
684 docmd(&items[lastupdated], ".status dir current\n", &list);
686 docmd(&items[lastupdated], ".status current\n", &list);
689 if (item->type == R_DIRECTOR)
690 docmd(&items[lastupdated], ".status dir last\n", &list);
692 docmd(&items[lastupdated], ".status last\n", &list);
696 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
697 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
698 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
699 g_string_set_size(*str, (*str)->len-1);
703 else if ((it = it->next) == NULL) {
705 g_string_append(*str, _("No current job."));
708 g_string_append(*str, _("No last job."));
712 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
715 ret = (joberrors > 0) ? warn : running;
716 g_string_append_printf(*str, _("Job status: Created"));
717 append_error_string(*str, joberrors);
720 ret = (joberrors > 0) ? warn : running;
721 g_string_append_printf(*str, _("Job status: Running"));
722 append_error_string(*str, joberrors);
725 g_string_append_printf(*str, _("Job status: Blocked"));
726 append_error_string(*str, joberrors);
730 g_string_append_printf(*str, _("Job status: Terminated"));
731 append_error_string(*str, joberrors);
732 ret = (joberrors > 0) ? warn : idle;
734 case JS_ErrorTerminated:
735 g_string_append_printf(*str, _("Job status: Terminated in error"));
736 append_error_string(*str, joberrors);
740 ret = (joberrors > 0) ? warn : running;
741 g_string_append_printf(*str, _("Job status: Error"));
742 append_error_string(*str, joberrors);
745 g_string_append_printf(*str, _("Job status: Fatal error"));
746 append_error_string(*str, joberrors);
750 g_string_append_printf(*str, _("Job status: Verify differences"));
751 append_error_string(*str, joberrors);
755 g_string_append_printf(*str, _("Job status: Canceled"));
756 append_error_string(*str, joberrors);
760 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
761 append_error_string(*str, joberrors);
765 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
766 append_error_string(*str, joberrors);
770 g_string_append_printf(*str, _("Job status: Waiting for new media"));
771 append_error_string(*str, joberrors);
775 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
776 append_error_string(*str, joberrors);
779 case JS_WaitStoreRes:
780 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
781 append_error_string(*str, joberrors);
785 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
786 append_error_string(*str, joberrors);
789 case JS_WaitClientRes:
790 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
791 append_error_string(*str, joberrors);
795 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
796 append_error_string(*str, joberrors);
799 case JS_WaitStartTime:
800 g_string_append_printf(*str, _("Job status: Waiting for start time"));
801 append_error_string(*str, joberrors);
804 case JS_WaitPriority:
805 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
806 append_error_string(*str, joberrors);
810 g_warning(_("Unknown job status %c."), jobstatus);
811 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
812 append_error_string(*str, joberrors);
818 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
825 g_string_free((GString*)it->data, TRUE);
827 } while ((it = it->next) != NULL);
835 item->oldstate = ret;
839 int docmd(monitoritem* item, const char* command, GSList** list)
844 *list = g_slist_alloc();
846 //str = g_string_sized_new(64);
849 memset(&jcr, 0, sizeof(jcr));
855 switch (item->type) {
857 dird = (DIRRES*)item->resource;
858 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
859 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
860 item->D_sock = bnet_connect(NULL, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
861 jcr.dir_bsock = item->D_sock;
864 filed = (CLIENT*)item->resource;
865 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
866 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
867 item->D_sock = bnet_connect(NULL, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
868 jcr.file_bsock = item->D_sock;
871 stored = (STORE*)item->resource;
872 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
873 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
874 item->D_sock = bnet_connect(NULL, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
875 jcr.store_bsock = item->D_sock;
878 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
883 if (item->D_sock == NULL) {
884 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
885 changeStatusMessage(item, _("Cannot connect to daemon."));
887 item->oldstate = error;
891 if (!authenticate_daemon(item, &jcr)) {
892 str = g_string_sized_new(64);
893 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
894 *list = g_slist_append(*list, str);
896 item->oldstate = error;
897 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
902 switch (item->type) {
904 trayMessage(_("Opened connection with Director daemon.\n"));
905 changeStatusMessage(item, _("Opened connection with Director daemon."));
908 trayMessage(_("Opened connection with File daemon.\n"));
909 changeStatusMessage(item, _("Opened connection with File daemon."));
912 trayMessage(_("Opened connection with Storage daemon.\n"));
913 changeStatusMessage(item, _("Opened connection with Storage daemon."));
916 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
922 if (item->type == R_DIRECTOR) { /* Read connection messages... */
924 docmd(item, "", &tlist); /* Usually invalid, but no matter */
928 g_string_free((GString*)it->data, TRUE);
930 } while ((it = it->next) != NULL);
937 writecmd(item, command);
940 if ((stat = bnet_recv(item->D_sock)) >= 0) {
941 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
943 else if (stat == BNET_SIGNAL) {
944 if (item->D_sock->msglen == BNET_EOD) {
945 //fprintf(stderr, "<< EOD >>\n");
948 else if (item->D_sock->msglen == BNET_PROMPT) {
949 //fprintf(stderr, "<< PROMPT >>\n");
950 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
953 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
954 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
955 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
958 str = g_string_sized_new(64);
959 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
960 *list = g_slist_append(*list, str);
963 else { /* BNET_HARDEOF || BNET_ERROR */
964 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
967 item->oldstate = error;
968 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
969 //fprintf(stderr, _("<< ERROR >>\n"));
973 if (is_bnet_stop(item->D_sock)) {
974 g_string_append_printf(str, _("<STOP>\n"));
977 item->oldstate = error;
978 changeStatusMessage(item, _("Error : Connection closed."));
979 //fprintf(stderr, "<< STOP >>\n");
980 return 0; /* error or term */
985 void writecmd(monitoritem* item, const char* command) {
987 item->D_sock->msglen = strlen(command);
988 pm_strcpy(&item->D_sock->msg, command);
989 bnet_send(item->D_sock);
993 /* Note: Does not seem to work either on Gnome nor KDE... */
994 void trayMessage(const char *fmt,...)
999 va_start(arg_ptr, fmt);
1000 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1003 fprintf(stderr, buf);
1005 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1008 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1012 va_start(arg_ptr, fmt);
1013 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1016 gtk_label_set_text(GTK_LABEL(item->label), buf);
1019 void updateStatusIcon(monitoritem* item) {
1023 /* For the current status, select the two worse for blinking,
1024 but never blink a D_Sock == NULL error with idle. */
1025 stateenum state1, state2, oldstate;
1026 gboolean onenull = FALSE;
1030 for (int i = 0; i < nitems; i++) {
1031 if (items[i].D_sock == NULL) onenull = TRUE;
1032 if (items[i].state >= state1) {
1034 state1 = items[i].state;
1036 else if (items[i].state > state2) {
1037 state2 = items[i].state;
1039 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1042 if ((onenull == TRUE) && (state2 == idle)) {
1046 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1049 if ((blinkstate) && (item->D_sock != NULL)) {
1050 if (item->state > 1) { //Warning or error while running
1051 xpm = generateXPM(running, item->oldstate);
1054 xpm = generateXPM(idle, item->oldstate);
1058 xpm = generateXPM(item->state, item->oldstate);
1062 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1064 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1065 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1068 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1070 g_object_unref(G_OBJECT(pixbuf));
1073 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1074 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1075 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1078 strcpy(address, "ff0000");
1081 strcpy(address, "ffffff");
1084 strcpy(address, "00ff00");
1087 strcpy(address, "ffff00");
1091 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1094 strcpy(address, "ff0000");
1097 strcpy(address, "ffffff");
1100 strcpy(address, "00ff00");
1103 strcpy(address, "ffff00");
1107 return (const char**)xpm_generic_var;