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 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
109 "Copyright (C) 2000-%s Kern Sibbald\n"
110 "Written by Nicolas Boichat (2004)\n"
111 "\nVersion: %s (%s) %s %s %s\n\n"
112 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
113 " -c <file> set configuration file to file\n"
114 " -dnn set debug level to nn\n"
115 " -t test - read configuration and exit\n"
116 " -? print this message.\n"
117 "\n"), BYEAR, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
120 static GtkWidget *new_image_button(const gchar *stock_id,
121 const gchar *label_text) {
127 button = gtk_button_new();
129 box = gtk_hbox_new(FALSE, 0);
130 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
131 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
132 label = gtk_label_new(label_text);
134 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
135 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
137 gtk_widget_show(image);
138 gtk_widget_show(label);
140 gtk_widget_show(box);
142 gtk_container_add(GTK_CONTAINER(button), box);
149 #if TRAY_DEBUG_MEMORY
150 gpointer smt_malloc(gsize n_bytes) {
151 return sm_malloc("GLib", sm_line, n_bytes);
154 gpointer smt_realloc(gpointer mem, gsize n_bytes) {
155 return sm_realloc("GLib", sm_line, mem, n_bytes);
158 gpointer smt_calloc(gsize n_blocks,
159 gsize n_block_bytes) {
160 return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes);
163 void smt_free(gpointer mem) {
164 sm_free("Glib", sm_line, mem);
168 /*********************************************************************
170 * Main Bacula Tray Monitor -- User Interface Program
173 int main(int argc, char *argv[])
175 #if TRAY_DEBUG_MEMORY
177 smvtable.malloc = &smt_malloc;
178 smvtable.realloc = &smt_realloc;
179 smvtable.free = &smt_free;
180 smvtable.calloc = &smt_calloc;
181 smvtable.try_malloc = NULL;
182 smvtable.try_realloc = NULL;
183 g_mem_set_vtable(&smvtable);
187 bool test_config = false;
192 setlocale(LC_ALL, "");
193 bindtextdomain("bacula", LOCALEDIR);
194 textdomain("bacula");
197 my_name_is(argc, argv, "tray-monitor");
198 init_msg(NULL, NULL);
199 working_directory = "/tmp";
201 struct sigaction sigignore;
202 sigignore.sa_flags = 0;
203 sigignore.sa_handler = SIG_IGN;
204 sigfillset(&sigignore.sa_mask);
205 sigaction(SIGPIPE, &sigignore, NULL);
207 gtk_init (&argc, &argv);
209 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
211 case 'c': /* configuration file */
212 if (configfile != NULL) {
215 configfile = bstrdup(optarg);
219 debug_level = atoi(optarg);
220 if (debug_level <= 0) {
244 if (configfile == NULL) {
245 configfile = bstrdup(CONFIG_FILE);
248 parse_config(configfile);
252 foreach_res(monitor, R_MONITOR) {
257 Emsg2(M_ERROR_TERM, 0,
258 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
262 foreach_res(dird, R_DIRECTOR) {
263 items[nitems].type = R_DIRECTOR;
264 items[nitems].resource = dird;
265 items[nitems].D_sock = NULL;
266 items[nitems].state = warn;
267 items[nitems].oldstate = warn;
270 foreach_res(filed, R_CLIENT) {
271 items[nitems].type = R_CLIENT;
272 items[nitems].resource = filed;
273 items[nitems].D_sock = NULL;
274 items[nitems].state = warn;
275 items[nitems].oldstate = warn;
278 foreach_res(stored, R_STORAGE) {
279 items[nitems].type = R_STORAGE;
280 items[nitems].resource = stored;
281 items[nitems].D_sock = NULL;
282 items[nitems].state = warn;
283 items[nitems].oldstate = warn;
289 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n"
290 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
297 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
298 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
299 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
300 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
301 strcpy(xpm_generic_var[i], xpm_generic[i]);
304 (void)WSA_Init(); /* Initialize Windows sockets */
307 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
310 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
311 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
312 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
315 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
316 // This should be ideally replaced by a completely libpr0n-based icon rendering.
317 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
318 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
319 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
320 g_object_unref(G_OBJECT(pixbuf));
322 mTrayMenu = gtk_menu_new();
326 entry = gtk_menu_item_new_with_label(_("Open status window..."));
327 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
328 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
330 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
332 entry = gtk_menu_item_new_with_label(_("Exit"));
333 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
334 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
336 gtk_widget_show_all(mTrayMenu);
338 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
340 g_timeout_add( 1000, blink, NULL );
342 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
344 gtk_window_set_title(GTK_WINDOW(window), _("Bacula tray monitor"));
346 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
347 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
349 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
351 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
353 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
355 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
357 GtkWidget* separator = gtk_hseparator_new();
358 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
361 GSList *group = NULL;
365 for (int i = 0; i < nitems; i++) {
366 switch (items[i].type) {
368 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
369 g_string_append(str, _(" (DIR)"));
372 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
373 g_string_append(str, _(" (FD)"));
376 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
377 g_string_append(str, _(" (SD)"));
383 radio = gtk_radio_button_new_with_label(group, str->str);
384 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
385 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
387 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
388 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
390 items[i].label = gtk_label_new(_("Unknown status."));
391 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
392 gtk_container_add(GTK_CONTAINER(align), items[i].label);
394 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
395 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
396 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
397 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
398 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
399 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
401 separator = gtk_hseparator_new();
402 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
404 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
407 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
409 textview = gtk_text_view_new();
411 buffer = gtk_text_buffer_new(NULL);
413 gtk_text_buffer_set_text(buffer, "", -1);
415 PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
416 gtk_widget_modify_font(textview, font_desc);
417 pango_font_description_free(font_desc);
419 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
420 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
422 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
424 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
426 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
428 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
430 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
431 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
432 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
433 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
434 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
435 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
436 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
437 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
439 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
440 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
441 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
443 button = new_image_button("gtk-help", _("About"));
444 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
445 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
447 button = new_image_button("gtk-close", _("Close"));
448 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
449 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
451 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
453 gtk_container_add(GTK_CONTAINER (window), vbox);
455 gtk_widget_show_all(vbox);
459 g_source_remove(timerTag);
463 for (i = 0; i < nitems; i++) {
464 if (items[i].D_sock) {
465 switch (items[i].type) {
467 trayMessage(_("Disconnecting from Director %s:%d\n"), ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport);
470 trayMessage(_("Disconnecting from Client %s:%d\n"), ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport);
473 trayMessage(_("Disconnecting from Storage %s:%d\n"), ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport);
478 //writecmd(&items[i], "quit");
479 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
480 bnet_close(items[i].D_sock);
484 (void)WSACleanup(); /* Cleanup Windows sockets */
486 //Free xpm_generic_var
487 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
488 g_free(xpm_generic_var[i]);
490 g_free(xpm_generic_var);
492 gtk_object_destroy(GTK_OBJECT(window));
493 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
496 #if TRAY_DEBUG_MEMORY
503 static void MonitorAbout(GtkWidget *widget, gpointer data) {
505 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
506 "<span size='x-large' weight='bold'>%s</span>\n\n"
508 "\n<small>%s: %s (%s) %s %s %s</small>",
509 _("Bacula Tray Monitor"),
510 _("Copyright (C) 2004-2006 Kern Sibbald\n"
511 "Written by Nicolas Boichat\n"),
513 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
515 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
518 "\n%s %s (%s) %s %s %s",
519 _("Bacula Tray Monitor"),
520 _("Copyright (C) 2004-2006 Kern Sibbald\n"
521 "Written by Nicolas Boichat\n"),
523 BYEAR, VERSION, BDATE, HOST_OS, DISTNAME);
525 gtk_dialog_run(GTK_DIALOG(about));
526 gtk_widget_destroy(about);
529 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
530 for (int i = 0; i < nitems; i++) {
535 static gboolean delete_event( GtkWidget *widget,
538 gtk_widget_hide(window);
539 return TRUE; /* do not destroy the window */
542 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
543 gtk_widget_show(window);
546 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
547 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
548 gtk_widget_show_all(mTrayMenu);
551 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
555 static void IntervalChanged(GtkWidget *widget, gpointer data) {
556 g_source_remove(timerTag);
557 timerTag = g_timeout_add(
559 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
563 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
564 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
566 for (int i = 0; i < nitems; i++) {
567 if (data == &(items[i])) {
572 g_return_if_fail(fullitem != -1);
574 int oldlastupdated = lastupdated;
575 lastupdated = fullitem-1;
577 lastupdated = oldlastupdated;
581 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
582 switch (item->type) {
584 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
586 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
588 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
590 printf(_("Error, currentitem is not a Client or a Storage..\n"));
597 static gboolean blink(gpointer data) {
598 blinkstate = !blinkstate;
599 for (int i = 0; i < nitems; i++) {
600 updateStatusIcon(&items[i]);
602 updateStatusIcon(NULL);
606 static gboolean fd_read(gpointer data)
609 #if TRAY_DEBUG_MEMORY
610 printf("sm_line=%d\n", sm_line);
612 GtkTextBuffer *newbuffer;
613 GtkTextIter start, stop, nstart, nstop;
617 GString *strlast, *strcurrent;
620 if (lastupdated == nitems) {
624 if (lastupdated == fullitem) {
625 newbuffer = gtk_text_buffer_new(NULL);
627 if (items[lastupdated].type == R_DIRECTOR)
628 docmd(&items[lastupdated], "status Director\n", &list);
630 docmd(&items[lastupdated], "status\n", &list);
634 gtk_text_buffer_get_end_iter(newbuffer, &stop);
635 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
636 if (it->data) g_string_free((GString*)it->data, TRUE);
637 } while ((it = it->next) != NULL);
641 /* Keep the selection if necessary */
642 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
643 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
644 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
647 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
649 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
650 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
654 g_object_unref(buffer);
657 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
660 getstatus(&items[lastupdated], 1, &strcurrent);
661 getstatus(&items[lastupdated], 0, &strlast);
662 updateStatusIcon(&items[lastupdated]);
664 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
666 updateStatusIcon(NULL);
668 g_string_free(strcurrent, TRUE);
669 g_string_free(strlast, TRUE);
674 void append_error_string(GString* str, int joberrors) {
676 g_string_append_printf(str, _(" (%d errors)"), joberrors);
679 g_string_append_printf(str, _(" (%d error)"), joberrors);
683 void getstatus(monitoritem* item, int current, GString** str)
686 stateenum ret = error;
687 int jobid = 0, joberrors = 0;
688 char jobstatus = JS_ErrorTerminated;
692 *str = g_string_sized_new(128);
695 if (item->type == R_DIRECTOR)
696 docmd(&items[lastupdated], ".status dir current\n", &list);
698 docmd(&items[lastupdated], ".status current\n", &list);
701 if (item->type == R_DIRECTOR)
702 docmd(&items[lastupdated], ".status dir last\n", &list);
704 docmd(&items[lastupdated], ".status last\n", &list);
708 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
709 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
710 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
711 g_string_set_size(*str, (*str)->len-1);
715 else if ((it = it->next) == NULL) {
717 g_string_append(*str, _("No current job."));
720 g_string_append(*str, _("No last job."));
724 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
727 ret = (joberrors > 0) ? warn : running;
728 g_string_append_printf(*str, _("Job status: Created"));
729 append_error_string(*str, joberrors);
732 ret = (joberrors > 0) ? warn : running;
733 g_string_append_printf(*str, _("Job status: Running"));
734 append_error_string(*str, joberrors);
737 g_string_append_printf(*str, _("Job status: Blocked"));
738 append_error_string(*str, joberrors);
742 g_string_append_printf(*str, _("Job status: Terminated"));
743 append_error_string(*str, joberrors);
744 ret = (joberrors > 0) ? warn : idle;
746 case JS_ErrorTerminated:
747 g_string_append_printf(*str, _("Job status: Terminated in error"));
748 append_error_string(*str, joberrors);
752 ret = (joberrors > 0) ? warn : running;
753 g_string_append_printf(*str, _("Job status: Error"));
754 append_error_string(*str, joberrors);
757 g_string_append_printf(*str, _("Job status: Fatal error"));
758 append_error_string(*str, joberrors);
762 g_string_append_printf(*str, _("Job status: Verify differences"));
763 append_error_string(*str, joberrors);
767 g_string_append_printf(*str, _("Job status: Canceled"));
768 append_error_string(*str, joberrors);
772 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
773 append_error_string(*str, joberrors);
777 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
778 append_error_string(*str, joberrors);
782 g_string_append_printf(*str, _("Job status: Waiting for new media"));
783 append_error_string(*str, joberrors);
787 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
788 append_error_string(*str, joberrors);
791 case JS_WaitStoreRes:
792 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
793 append_error_string(*str, joberrors);
797 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
798 append_error_string(*str, joberrors);
801 case JS_WaitClientRes:
802 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
803 append_error_string(*str, joberrors);
807 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
808 append_error_string(*str, joberrors);
811 case JS_WaitStartTime:
812 g_string_append_printf(*str, _("Job status: Waiting for start time"));
813 append_error_string(*str, joberrors);
816 case JS_WaitPriority:
817 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
818 append_error_string(*str, joberrors);
822 g_warning(_("Unknown job status %c."), jobstatus);
823 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
824 append_error_string(*str, joberrors);
830 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
837 g_string_free((GString*)it->data, TRUE);
839 } while ((it = it->next) != NULL);
847 item->oldstate = ret;
851 int docmd(monitoritem* item, const char* command, GSList** list)
856 *list = g_slist_alloc();
858 //str = g_string_sized_new(64);
861 memset(&jcr, 0, sizeof(jcr));
867 switch (item->type) {
869 dird = (DIRRES*)item->resource;
870 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
871 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
872 item->D_sock = bnet_connect(NULL, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
873 jcr.dir_bsock = item->D_sock;
876 filed = (CLIENT*)item->resource;
877 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
878 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
879 item->D_sock = bnet_connect(NULL, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
880 jcr.file_bsock = item->D_sock;
883 stored = (STORE*)item->resource;
884 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
885 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
886 item->D_sock = bnet_connect(NULL, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
887 jcr.store_bsock = item->D_sock;
890 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
895 if (item->D_sock == NULL) {
896 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
897 changeStatusMessage(item, _("Cannot connect to daemon."));
899 item->oldstate = error;
903 if (!authenticate_daemon(item, &jcr)) {
904 str = g_string_sized_new(64);
905 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
906 *list = g_slist_append(*list, str);
908 item->oldstate = error;
909 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
914 switch (item->type) {
916 trayMessage(_("Opened connection with Director daemon.\n"));
917 changeStatusMessage(item, _("Opened connection with Director daemon."));
920 trayMessage(_("Opened connection with File daemon.\n"));
921 changeStatusMessage(item, _("Opened connection with File daemon."));
924 trayMessage(_("Opened connection with Storage daemon.\n"));
925 changeStatusMessage(item, _("Opened connection with Storage daemon."));
928 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
934 if (item->type == R_DIRECTOR) { /* Read connection messages... */
936 docmd(item, "", &tlist); /* Usually invalid, but no matter */
940 g_string_free((GString*)it->data, TRUE);
942 } while ((it = it->next) != NULL);
949 writecmd(item, command);
952 if ((stat = bnet_recv(item->D_sock)) >= 0) {
953 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
955 else if (stat == BNET_SIGNAL) {
956 if (item->D_sock->msglen == BNET_EOD) {
957 //fprintf(stderr, "<< EOD >>\n");
960 else if (item->D_sock->msglen == BNET_PROMPT) {
961 //fprintf(stderr, "<< PROMPT >>\n");
962 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
965 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
966 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
967 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
970 str = g_string_sized_new(64);
971 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
972 *list = g_slist_append(*list, str);
975 else { /* BNET_HARDEOF || BNET_ERROR */
976 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
979 item->oldstate = error;
980 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
981 //fprintf(stderr, _("<< ERROR >>\n"));
985 if (is_bnet_stop(item->D_sock)) {
986 g_string_append_printf(str, _("<STOP>\n"));
989 item->oldstate = error;
990 changeStatusMessage(item, _("Error : Connection closed."));
991 //fprintf(stderr, "<< STOP >>\n");
992 return 0; /* error or term */
997 void writecmd(monitoritem* item, const char* command) {
999 item->D_sock->msglen = strlen(command);
1000 pm_strcpy(&item->D_sock->msg, command);
1001 bnet_send(item->D_sock);
1005 /* Note: Does not seem to work either on Gnome nor KDE... */
1006 void trayMessage(const char *fmt,...)
1011 va_start(arg_ptr, fmt);
1012 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1015 fprintf(stderr, buf);
1017 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1020 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1024 va_start(arg_ptr, fmt);
1025 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1028 gtk_label_set_text(GTK_LABEL(item->label), buf);
1031 void updateStatusIcon(monitoritem* item) {
1035 /* For the current status, select the two worse for blinking,
1036 but never blink a D_Sock == NULL error with idle. */
1037 stateenum state1, state2, oldstate;
1038 gboolean onenull = FALSE;
1042 for (int i = 0; i < nitems; i++) {
1043 if (items[i].D_sock == NULL) onenull = TRUE;
1044 if (items[i].state >= state1) {
1046 state1 = items[i].state;
1048 else if (items[i].state > state2) {
1049 state2 = items[i].state;
1051 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1054 if ((onenull == TRUE) && (state2 == idle)) {
1058 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1061 if ((blinkstate) && (item->D_sock != NULL)) {
1062 if (item->state > 1) { //Warning or error while running
1063 xpm = generateXPM(running, item->oldstate);
1066 xpm = generateXPM(idle, item->oldstate);
1070 xpm = generateXPM(item->state, item->oldstate);
1074 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1076 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1077 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1080 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1082 g_object_unref(G_OBJECT(pixbuf));
1085 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1086 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1087 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1090 strcpy(address, "ff0000");
1093 strcpy(address, "ffffff");
1096 strcpy(address, "00ff00");
1099 strcpy(address, "ffff00");
1103 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1106 strcpy(address, "ff0000");
1109 strcpy(address, "ffffff");
1112 strcpy(address, "00ff00");
1115 strcpy(address, "ffff00");
1119 return (const char**)xpm_generic_var;