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 */
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"), 2004, 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"
509 "\n<small>%s: %s (%s) %s %s %s</small>",
510 _("Bacula Tray Monitor"),
512 _("Written by Nicolas Boichat\n"),
514 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
516 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
520 "\n%s: %s (%s) %s %s %s",
521 _("Bacula Tray Monitor"),
523 _("Written by Nicolas Boichat\n"),
525 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
527 gtk_dialog_run(GTK_DIALOG(about));
528 gtk_widget_destroy(about);
531 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
532 for (int i = 0; i < nitems; i++) {
537 static gboolean delete_event( GtkWidget *widget,
540 gtk_widget_hide(window);
541 return TRUE; /* do not destroy the window */
544 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
545 gtk_widget_show(window);
548 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
549 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
550 gtk_widget_show_all(mTrayMenu);
553 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
557 static void IntervalChanged(GtkWidget *widget, gpointer data) {
558 g_source_remove(timerTag);
559 timerTag = g_timeout_add(
561 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
565 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
566 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
568 for (int i = 0; i < nitems; i++) {
569 if (data == &(items[i])) {
574 g_return_if_fail(fullitem != -1);
576 int oldlastupdated = lastupdated;
577 lastupdated = fullitem-1;
579 lastupdated = oldlastupdated;
583 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
584 switch (item->type) {
586 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
588 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
590 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
592 printf(_("Error, currentitem is not a Client or a Storage..\n"));
599 static gboolean blink(gpointer data) {
600 blinkstate = !blinkstate;
601 for (int i = 0; i < nitems; i++) {
602 updateStatusIcon(&items[i]);
604 updateStatusIcon(NULL);
608 static gboolean fd_read(gpointer data)
611 #if TRAY_DEBUG_MEMORY
612 printf("sm_line=%d\n", sm_line);
614 GtkTextBuffer *newbuffer;
615 GtkTextIter start, stop, nstart, nstop;
619 GString *strlast, *strcurrent;
622 if (lastupdated == nitems) {
626 if (lastupdated == fullitem) {
627 newbuffer = gtk_text_buffer_new(NULL);
629 if (items[lastupdated].type == R_DIRECTOR)
630 docmd(&items[lastupdated], "status Director\n", &list);
632 docmd(&items[lastupdated], "status\n", &list);
636 gtk_text_buffer_get_end_iter(newbuffer, &stop);
637 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
638 if (it->data) g_string_free((GString*)it->data, TRUE);
639 } while ((it = it->next) != NULL);
643 /* Keep the selection if necessary */
644 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
645 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
646 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
649 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
651 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
652 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
656 g_object_unref(buffer);
659 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
662 getstatus(&items[lastupdated], 1, &strcurrent);
663 getstatus(&items[lastupdated], 0, &strlast);
664 updateStatusIcon(&items[lastupdated]);
666 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
668 updateStatusIcon(NULL);
670 g_string_free(strcurrent, TRUE);
671 g_string_free(strlast, TRUE);
676 void append_error_string(GString* str, int joberrors) {
678 g_string_append_printf(str, _(" (%d errors)"), joberrors);
681 g_string_append_printf(str, _(" (%d error)"), joberrors);
685 void getstatus(monitoritem* item, int current, GString** str)
688 stateenum ret = error;
689 int jobid = 0, joberrors = 0;
690 char jobstatus = JS_ErrorTerminated;
694 *str = g_string_sized_new(128);
697 if (item->type == R_DIRECTOR)
698 docmd(&items[lastupdated], ".status dir current\n", &list);
700 docmd(&items[lastupdated], ".status current\n", &list);
703 if (item->type == R_DIRECTOR)
704 docmd(&items[lastupdated], ".status dir last\n", &list);
706 docmd(&items[lastupdated], ".status last\n", &list);
710 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
711 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
712 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
713 g_string_set_size(*str, (*str)->len-1);
717 else if ((it = it->next) == NULL) {
719 g_string_append(*str, _("No current job."));
722 g_string_append(*str, _("No last job."));
726 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
729 ret = (joberrors > 0) ? warn : running;
730 g_string_append_printf(*str, _("Job status: Created"));
731 append_error_string(*str, joberrors);
734 ret = (joberrors > 0) ? warn : running;
735 g_string_append_printf(*str, _("Job status: Running"));
736 append_error_string(*str, joberrors);
739 g_string_append_printf(*str, _("Job status: Blocked"));
740 append_error_string(*str, joberrors);
744 g_string_append_printf(*str, _("Job status: Terminated"));
745 append_error_string(*str, joberrors);
746 ret = (joberrors > 0) ? warn : idle;
748 case JS_ErrorTerminated:
749 g_string_append_printf(*str, _("Job status: Terminated in error"));
750 append_error_string(*str, joberrors);
754 ret = (joberrors > 0) ? warn : running;
755 g_string_append_printf(*str, _("Job status: Error"));
756 append_error_string(*str, joberrors);
759 g_string_append_printf(*str, _("Job status: Fatal error"));
760 append_error_string(*str, joberrors);
764 g_string_append_printf(*str, _("Job status: Verify differences"));
765 append_error_string(*str, joberrors);
769 g_string_append_printf(*str, _("Job status: Canceled"));
770 append_error_string(*str, joberrors);
774 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
775 append_error_string(*str, joberrors);
779 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
780 append_error_string(*str, joberrors);
784 g_string_append_printf(*str, _("Job status: Waiting for new media"));
785 append_error_string(*str, joberrors);
789 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
790 append_error_string(*str, joberrors);
793 case JS_WaitStoreRes:
794 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
795 append_error_string(*str, joberrors);
799 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
800 append_error_string(*str, joberrors);
803 case JS_WaitClientRes:
804 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
805 append_error_string(*str, joberrors);
809 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
810 append_error_string(*str, joberrors);
813 case JS_WaitStartTime:
814 g_string_append_printf(*str, _("Job status: Waiting for start time"));
815 append_error_string(*str, joberrors);
818 case JS_WaitPriority:
819 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
820 append_error_string(*str, joberrors);
824 g_warning(_("Unknown job status %c."), jobstatus);
825 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
826 append_error_string(*str, joberrors);
832 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
839 g_string_free((GString*)it->data, TRUE);
841 } while ((it = it->next) != NULL);
849 item->oldstate = ret;
853 int docmd(monitoritem* item, const char* command, GSList** list)
858 *list = g_slist_alloc();
860 //str = g_string_sized_new(64);
863 memset(&jcr, 0, sizeof(jcr));
869 switch (item->type) {
871 dird = (DIRRES*)item->resource;
872 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
873 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
874 item->D_sock = bnet_connect(NULL, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
875 jcr.dir_bsock = item->D_sock;
878 filed = (CLIENT*)item->resource;
879 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
880 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
881 item->D_sock = bnet_connect(NULL, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
882 jcr.file_bsock = item->D_sock;
885 stored = (STORE*)item->resource;
886 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
887 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
888 item->D_sock = bnet_connect(NULL, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
889 jcr.store_bsock = item->D_sock;
892 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
897 if (item->D_sock == NULL) {
898 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
899 changeStatusMessage(item, _("Cannot connect to daemon."));
901 item->oldstate = error;
905 if (!authenticate_daemon(item, &jcr)) {
906 str = g_string_sized_new(64);
907 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
908 *list = g_slist_append(*list, str);
910 item->oldstate = error;
911 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
916 switch (item->type) {
918 trayMessage(_("Opened connection with Director daemon.\n"));
919 changeStatusMessage(item, _("Opened connection with Director daemon."));
922 trayMessage(_("Opened connection with File daemon.\n"));
923 changeStatusMessage(item, _("Opened connection with File daemon."));
926 trayMessage(_("Opened connection with Storage daemon.\n"));
927 changeStatusMessage(item, _("Opened connection with Storage daemon."));
930 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
936 if (item->type == R_DIRECTOR) { /* Read connection messages... */
938 docmd(item, "", &tlist); /* Usually invalid, but no matter */
942 g_string_free((GString*)it->data, TRUE);
944 } while ((it = it->next) != NULL);
951 writecmd(item, command);
954 if ((stat = bnet_recv(item->D_sock)) >= 0) {
955 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
957 else if (stat == BNET_SIGNAL) {
958 if (item->D_sock->msglen == BNET_EOD) {
959 //fprintf(stderr, "<< EOD >>\n");
962 else if (item->D_sock->msglen == BNET_PROMPT) {
963 //fprintf(stderr, "<< PROMPT >>\n");
964 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
967 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
968 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
969 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
972 str = g_string_sized_new(64);
973 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
974 *list = g_slist_append(*list, str);
977 else { /* BNET_HARDEOF || BNET_ERROR */
978 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
981 item->oldstate = error;
982 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
983 //fprintf(stderr, _("<< ERROR >>\n"));
987 if (is_bnet_stop(item->D_sock)) {
988 g_string_append_printf(str, _("<STOP>\n"));
991 item->oldstate = error;
992 changeStatusMessage(item, _("Error : Connection closed."));
993 //fprintf(stderr, "<< STOP >>\n");
994 return 0; /* error or term */
999 void writecmd(monitoritem* item, const char* command) {
1001 item->D_sock->msglen = strlen(command);
1002 pm_strcpy(&item->D_sock->msg, command);
1003 bnet_send(item->D_sock);
1007 /* Note: Does not seem to work either on Gnome nor KDE... */
1008 void trayMessage(const char *fmt,...)
1013 va_start(arg_ptr, fmt);
1014 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1017 fprintf(stderr, buf);
1019 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1022 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1026 va_start(arg_ptr, fmt);
1027 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1030 gtk_label_set_text(GTK_LABEL(item->label), buf);
1033 void updateStatusIcon(monitoritem* item) {
1037 /* For the current status, select the two worse for blinking,
1038 but never blink a D_Sock == NULL error with idle. */
1039 stateenum state1, state2, oldstate;
1040 gboolean onenull = FALSE;
1044 for (int i = 0; i < nitems; i++) {
1045 if (items[i].D_sock == NULL) onenull = TRUE;
1046 if (items[i].state >= state1) {
1048 state1 = items[i].state;
1050 else if (items[i].state > state2) {
1051 state2 = items[i].state;
1053 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1056 if ((onenull == TRUE) && (state2 == idle)) {
1060 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1063 if ((blinkstate) && (item->D_sock != NULL)) {
1064 if (item->state > 1) { //Warning or error while running
1065 xpm = generateXPM(running, item->oldstate);
1068 xpm = generateXPM(idle, item->oldstate);
1072 xpm = generateXPM(item->state, item->oldstate);
1076 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1078 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1079 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1082 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1084 g_object_unref(G_OBJECT(pixbuf));
1087 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1088 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1089 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1092 strcpy(address, "ff0000");
1095 strcpy(address, "ffffff");
1098 strcpy(address, "00ff00");
1101 strcpy(address, "ffff00");
1105 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1108 strcpy(address, "ff0000");
1111 strcpy(address, "ffffff");
1114 strcpy(address, "00ff00");
1117 strcpy(address, "ffff00");
1121 return (const char**)xpm_generic_var;