2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Gnome Tray Monitor
32 * Nicolas Boichat, August MMIV
39 #include "tray-monitor.h"
41 #include "eggstatusicon.h"
43 #include "generic.xpm"
45 #define TRAY_DEBUG_MEMORY 0
47 /* Imported functions */
48 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
49 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
50 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
53 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
55 /* Forward referenced functions */
56 void writecmd(monitoritem* item, const char* command);
57 int docmd(monitoritem* item, const char* command, GSList** list);
58 void getstatus(monitoritem* item, int current, GString** str);
60 /* Static variables */
61 static char *configfile = NULL;
62 static MONITOR *monitor;
64 static int nitems = 0;
65 static int fullitem = 0; //Item to be display in detailled status window
66 static int lastupdated = -1; //Last item updated
67 static monitoritem items[32];
69 /* Data received from DIR/FD/SD */
70 static char OKqstatus[] = "%c000 OK .status\n";
71 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
73 /* UI variables and functions */
76 static gboolean fd_read(gpointer data);
77 static gboolean blink(gpointer data);
80 void trayMessage(const char *fmt,...);
81 void updateStatusIcon(monitoritem* item);
82 void changeStatusMessage(monitoritem* item, const char *fmt,...);
83 static const char** generateXPM(stateenum newstate, stateenum oldstate);
86 static void TrayIconActivate(GtkWidget *widget, gpointer data);
87 static void TrayIconExit(unsigned int activateTime, unsigned int button);
88 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
89 static void MonitorAbout(GtkWidget *widget, gpointer data);
90 static void MonitorRefresh(GtkWidget *widget, gpointer data);
91 static void IntervalChanged(GtkWidget *widget, gpointer data);
92 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
93 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
95 static guint timerTag;
96 static EggStatusIcon *mTrayIcon;
97 static GtkWidget *mTrayMenu;
98 static GtkWidget *window;
99 static GtkWidget *textview;
100 static GtkTextBuffer *buffer;
101 static GtkWidget *timeoutspinner;
102 char** xpm_generic_var;
103 static gboolean blinkstate = TRUE;
105 PangoFontDescription *font_desc = NULL;
107 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
113 "Written by Nicolas Boichat (2004)\n"
114 "\nVersion: %s (%s) %s %s %s\n\n"
115 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
116 " -c <file> set configuration file to file\n"
117 " -dnn set debug level to nn\n"
118 " -t test - read configuration and exit\n"
119 " -? print this message.\n"
120 "\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
123 static GtkWidget *new_image_button(const gchar *stock_id,
124 const gchar *label_text) {
130 button = gtk_button_new();
132 box = gtk_hbox_new(FALSE, 0);
133 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
134 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
135 label = gtk_label_new(label_text);
137 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
138 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
140 gtk_widget_show(image);
141 gtk_widget_show(label);
143 gtk_widget_show(box);
145 gtk_container_add(GTK_CONTAINER(button), box);
152 #if TRAY_DEBUG_MEMORY
153 gpointer smt_malloc(gsize n_bytes) {
154 return sm_malloc("GLib", sm_line, n_bytes);
157 gpointer smt_realloc(gpointer mem, gsize n_bytes) {
158 return sm_realloc("GLib", sm_line, mem, n_bytes);
161 gpointer smt_calloc(gsize n_blocks,
162 gsize n_block_bytes) {
163 return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes);
166 void smt_free(gpointer mem) {
167 sm_free("Glib", sm_line, mem);
171 /*********************************************************************
173 * Main Bacula Tray Monitor -- User Interface Program
176 int main(int argc, char *argv[])
178 #if TRAY_DEBUG_MEMORY
180 smvtable.malloc = &smt_malloc;
181 smvtable.realloc = &smt_realloc;
182 smvtable.free = &smt_free;
183 smvtable.calloc = &smt_calloc;
184 smvtable.try_malloc = NULL;
185 smvtable.try_realloc = NULL;
186 g_mem_set_vtable(&smvtable);
190 bool test_config = false;
194 CONFONTRES *con_font;
196 setlocale(LC_ALL, "");
197 bindtextdomain("bacula", LOCALEDIR);
198 textdomain("bacula");
201 my_name_is(argc, argv, "tray-monitor");
202 init_msg(NULL, NULL);
203 working_directory = "/tmp";
205 struct sigaction sigignore;
206 sigignore.sa_flags = 0;
207 sigignore.sa_handler = SIG_IGN;
208 sigfillset(&sigignore.sa_mask);
209 sigaction(SIGPIPE, &sigignore, NULL);
211 gtk_init(&argc, &argv);
213 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
215 case 'c': /* configuration file */
216 if (configfile != NULL) {
219 configfile = bstrdup(optarg);
223 debug_level = atoi(optarg);
224 if (debug_level <= 0) {
248 if (configfile == NULL) {
249 configfile = bstrdup(CONFIG_FILE);
252 parse_config(configfile);
256 foreach_res(monitor, R_MONITOR) {
261 Emsg2(M_ERROR_TERM, 0,
262 _("Error: %d Monitor resources defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
266 foreach_res(dird, R_DIRECTOR) {
267 items[nitems].type = R_DIRECTOR;
268 items[nitems].resource = dird;
269 items[nitems].D_sock = NULL;
270 items[nitems].state = warn;
271 items[nitems].oldstate = warn;
274 foreach_res(filed, R_CLIENT) {
275 items[nitems].type = R_CLIENT;
276 items[nitems].resource = filed;
277 items[nitems].D_sock = NULL;
278 items[nitems].state = warn;
279 items[nitems].oldstate = warn;
282 foreach_res(stored, R_STORAGE) {
283 items[nitems].type = R_STORAGE;
284 items[nitems].resource = stored;
285 items[nitems].D_sock = NULL;
286 items[nitems].state = warn;
287 items[nitems].oldstate = warn;
293 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage or Director resource defined in %s\n"
294 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
301 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
302 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
303 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
304 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
305 strcpy(xpm_generic_var[i], xpm_generic[i]);
308 (void)WSA_Init(); /* Initialize Windows sockets */
311 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
314 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
315 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
316 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
319 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
320 // This should be ideally replaced by a completely libpr0n-based icon rendering.
321 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
322 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
323 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
324 g_object_unref(G_OBJECT(pixbuf));
326 mTrayMenu = gtk_menu_new();
330 entry = gtk_menu_item_new_with_label(_("Open status window..."));
331 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
332 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
334 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
336 entry = gtk_menu_item_new_with_label(_("Exit"));
337 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
338 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
340 gtk_widget_show_all(mTrayMenu);
342 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
344 g_timeout_add( 1000, blink, NULL );
346 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
348 gtk_window_set_title(GTK_WINDOW(window), _("Bacula tray monitor"));
350 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
351 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
353 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
355 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
357 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
359 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
361 GtkWidget* separator = gtk_hseparator_new();
362 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
365 GSList *group = NULL;
369 for (int i = 0; i < nitems; i++) {
370 switch (items[i].type) {
372 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
373 g_string_append(str, _(" (DIR)"));
376 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
377 g_string_append(str, _(" (FD)"));
380 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
381 g_string_append(str, _(" (SD)"));
387 radio = gtk_radio_button_new_with_label(group, str->str);
388 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
389 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
391 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
392 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
394 items[i].label = gtk_label_new(_("Unknown status."));
395 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
396 gtk_container_add(GTK_CONTAINER(align), items[i].label);
398 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
399 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
400 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
401 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
402 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
403 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
405 separator = gtk_hseparator_new();
406 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
408 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
411 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
413 textview = gtk_text_view_new();
415 buffer = gtk_text_buffer_new(NULL);
417 gtk_text_buffer_set_text(buffer, "", -1);
420 * Gtk2/pango have different font names. Gnome2 comes with "Monospace 10"
424 foreach_res(con_font, R_CONSOLE_FONT) {
425 if (!con_font->fontface) {
426 Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
429 Dmsg1(100, "Now loading: %s\n",con_font->fontface);
430 font_desc = pango_font_description_from_string(con_font->fontface);
431 if (font_desc == NULL) {
432 Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
433 con_font->hdr.name, con_font->fontface);
435 Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
436 con_font->hdr.name, con_font->fontface);
443 font_desc = pango_font_description_from_string("Monospace 10");
446 font_desc = pango_font_description_from_string("monospace");
449 gtk_widget_modify_font(textview, font_desc);
450 pango_font_description_free(font_desc);
452 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
453 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
455 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
457 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
459 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
461 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
463 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
464 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
465 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
466 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
467 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
468 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
469 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
470 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
472 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
473 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
474 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
476 button = new_image_button("gtk-help", _("About"));
477 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
478 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
480 button = new_image_button("gtk-close", _("Close"));
481 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
482 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
484 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
486 gtk_container_add(GTK_CONTAINER (window), vbox);
488 gtk_widget_show_all(vbox);
492 g_source_remove(timerTag);
496 for (i = 0; i < nitems; i++) {
497 if (items[i].D_sock) {
498 switch (items[i].type) {
500 trayMessage(_("Disconnecting from Director %s:%d\n"), ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport);
503 trayMessage(_("Disconnecting from Client %s:%d\n"), ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport);
506 trayMessage(_("Disconnecting from Storage %s:%d\n"), ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport);
511 //writecmd(&items[i], "quit");
512 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
513 bnet_close(items[i].D_sock);
517 (void)WSACleanup(); /* Cleanup Windows sockets */
519 //Free xpm_generic_var
520 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
521 g_free(xpm_generic_var[i]);
523 g_free(xpm_generic_var);
525 gtk_object_destroy(GTK_OBJECT(window));
526 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
529 #if TRAY_DEBUG_MEMORY
536 static void MonitorAbout(GtkWidget *widget, gpointer data) {
538 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
539 "<span size='x-large' weight='bold'>%s</span>\n\n"
542 "\n<small>%s: %s (%s) %s %s %s</small>",
543 _("Bacula Tray Monitor"),
545 _("Written by Nicolas Boichat\n"),
547 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
549 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
553 "\n%s: %s (%s) %s %s %s",
554 _("Bacula Tray Monitor"),
556 _("Written by Nicolas Boichat\n"),
558 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
560 gtk_dialog_run(GTK_DIALOG(about));
561 gtk_widget_destroy(about);
564 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
565 for (int i = 0; i < nitems; i++) {
570 static gboolean delete_event( GtkWidget *widget,
573 gtk_widget_hide(window);
574 return TRUE; /* do not destroy the window */
577 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
578 gtk_widget_show(window);
581 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
582 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
583 gtk_widget_show_all(mTrayMenu);
586 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
590 static void IntervalChanged(GtkWidget *widget, gpointer data) {
591 g_source_remove(timerTag);
592 timerTag = g_timeout_add(
594 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
598 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
599 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
601 for (int i = 0; i < nitems; i++) {
602 if (data == &(items[i])) {
607 g_return_if_fail(fullitem != -1);
609 int oldlastupdated = lastupdated;
610 lastupdated = fullitem-1;
612 lastupdated = oldlastupdated;
616 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
617 switch (item->type) {
619 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
621 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
623 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
625 printf(_("Error, currentitem is not a Client or a Storage..\n"));
632 static gboolean blink(gpointer data) {
633 blinkstate = !blinkstate;
634 for (int i = 0; i < nitems; i++) {
635 updateStatusIcon(&items[i]);
637 updateStatusIcon(NULL);
641 static gboolean fd_read(gpointer data)
644 #if TRAY_DEBUG_MEMORY
645 printf("sm_line=%d\n", sm_line);
647 GtkTextBuffer *newbuffer;
648 GtkTextIter start, stop, nstart, nstop;
652 GString *strlast, *strcurrent;
655 if (lastupdated == nitems) {
659 if (lastupdated == fullitem) {
660 newbuffer = gtk_text_buffer_new(NULL);
662 if (items[lastupdated].type == R_DIRECTOR)
663 docmd(&items[lastupdated], "status Director\n", &list);
665 docmd(&items[lastupdated], "status\n", &list);
669 gtk_text_buffer_get_end_iter(newbuffer, &stop);
670 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
671 if (it->data) g_string_free((GString*)it->data, TRUE);
672 } while ((it = it->next) != NULL);
676 /* Keep the selection if necessary */
677 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
678 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
679 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
682 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
684 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
685 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
689 g_object_unref(buffer);
692 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
695 getstatus(&items[lastupdated], 1, &strcurrent);
696 getstatus(&items[lastupdated], 0, &strlast);
697 updateStatusIcon(&items[lastupdated]);
699 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
701 updateStatusIcon(NULL);
703 g_string_free(strcurrent, TRUE);
704 g_string_free(strlast, TRUE);
709 void append_error_string(GString* str, int joberrors) {
711 g_string_append_printf(str, _(" (%d errors)"), joberrors);
714 g_string_append_printf(str, _(" (%d error)"), joberrors);
718 void getstatus(monitoritem* item, int current, GString** str)
721 stateenum ret = error;
722 int jobid = 0, joberrors = 0;
723 char jobstatus = JS_ErrorTerminated;
727 *str = g_string_sized_new(128);
730 if (item->type == R_DIRECTOR)
731 docmd(&items[lastupdated], ".status dir current\n", &list);
733 docmd(&items[lastupdated], ".status current\n", &list);
736 if (item->type == R_DIRECTOR)
737 docmd(&items[lastupdated], ".status dir last\n", &list);
739 docmd(&items[lastupdated], ".status last\n", &list);
743 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
744 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
745 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
746 g_string_set_size(*str, (*str)->len-1);
750 else if ((it = it->next) == NULL) {
752 g_string_append(*str, _("No current job."));
755 g_string_append(*str, _("No last job."));
759 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
762 ret = (joberrors > 0) ? warn : running;
763 g_string_append_printf(*str, _("Job status: Created"));
764 append_error_string(*str, joberrors);
767 ret = (joberrors > 0) ? warn : running;
768 g_string_append_printf(*str, _("Job status: Running"));
769 append_error_string(*str, joberrors);
772 g_string_append_printf(*str, _("Job status: Blocked"));
773 append_error_string(*str, joberrors);
777 g_string_append_printf(*str, _("Job status: Terminated"));
778 append_error_string(*str, joberrors);
779 ret = (joberrors > 0) ? warn : idle;
781 case JS_ErrorTerminated:
782 g_string_append_printf(*str, _("Job status: Terminated in error"));
783 append_error_string(*str, joberrors);
787 ret = (joberrors > 0) ? warn : running;
788 g_string_append_printf(*str, _("Job status: Error"));
789 append_error_string(*str, joberrors);
792 g_string_append_printf(*str, _("Job status: Fatal error"));
793 append_error_string(*str, joberrors);
797 g_string_append_printf(*str, _("Job status: Verify differences"));
798 append_error_string(*str, joberrors);
802 g_string_append_printf(*str, _("Job status: Canceled"));
803 append_error_string(*str, joberrors);
807 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
808 append_error_string(*str, joberrors);
812 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
813 append_error_string(*str, joberrors);
817 g_string_append_printf(*str, _("Job status: Waiting for new media"));
818 append_error_string(*str, joberrors);
822 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
823 append_error_string(*str, joberrors);
826 case JS_WaitStoreRes:
827 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
828 append_error_string(*str, joberrors);
832 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
833 append_error_string(*str, joberrors);
836 case JS_WaitClientRes:
837 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
838 append_error_string(*str, joberrors);
842 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
843 append_error_string(*str, joberrors);
846 case JS_WaitStartTime:
847 g_string_append_printf(*str, _("Job status: Waiting for start time"));
848 append_error_string(*str, joberrors);
851 case JS_WaitPriority:
852 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
853 append_error_string(*str, joberrors);
857 g_warning(_("Unknown job status %c."), jobstatus);
858 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
859 append_error_string(*str, joberrors);
865 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
872 g_string_free((GString*)it->data, TRUE);
874 } while ((it = it->next) != NULL);
882 item->oldstate = ret;
886 int docmd(monitoritem* item, const char* command, GSList** list)
891 *list = g_slist_alloc();
893 //str = g_string_sized_new(64);
896 memset(&jcr, 0, sizeof(jcr));
902 switch (item->type) {
904 dird = (DIRRES*)item->resource;
905 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
906 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
907 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
908 jcr.dir_bsock = item->D_sock;
911 filed = (CLIENT*)item->resource;
912 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
913 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
914 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
915 jcr.file_bsock = item->D_sock;
918 stored = (STORE*)item->resource;
919 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
920 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
921 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
922 jcr.store_bsock = item->D_sock;
925 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
930 if (item->D_sock == NULL) {
931 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
932 changeStatusMessage(item, _("Cannot connect to daemon."));
934 item->oldstate = error;
938 if (!authenticate_daemon(item, &jcr)) {
939 str = g_string_sized_new(64);
940 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
941 *list = g_slist_append(*list, str);
943 item->oldstate = error;
944 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
949 switch (item->type) {
951 trayMessage(_("Opened connection with Director daemon.\n"));
952 changeStatusMessage(item, _("Opened connection with Director daemon."));
955 trayMessage(_("Opened connection with File daemon.\n"));
956 changeStatusMessage(item, _("Opened connection with File daemon."));
959 trayMessage(_("Opened connection with Storage daemon.\n"));
960 changeStatusMessage(item, _("Opened connection with Storage daemon."));
963 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
969 if (item->type == R_DIRECTOR) { /* Read connection messages... */
971 docmd(item, "", &tlist); /* Usually invalid, but no matter */
975 g_string_free((GString*)it->data, TRUE);
977 } while ((it = it->next) != NULL);
984 writecmd(item, command);
987 if ((stat = bnet_recv(item->D_sock)) >= 0) {
988 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
990 else if (stat == BNET_SIGNAL) {
991 if (item->D_sock->msglen == BNET_EOD) {
992 //fprintf(stderr, "<< EOD >>\n");
995 else if (item->D_sock->msglen == BNET_PROMPT) {
996 //fprintf(stderr, "<< PROMPT >>\n");
997 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
1000 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
1001 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
1002 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
1005 str = g_string_sized_new(64);
1006 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
1007 *list = g_slist_append(*list, str);
1010 else { /* BNET_HARDEOF || BNET_ERROR */
1011 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
1012 item->D_sock = NULL;
1013 item->state = error;
1014 item->oldstate = error;
1015 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
1016 //fprintf(stderr, _("<< ERROR >>\n"));
1020 if (is_bnet_stop(item->D_sock)) {
1021 g_string_append_printf(str, _("<STOP>\n"));
1022 item->D_sock = NULL;
1023 item->state = error;
1024 item->oldstate = error;
1025 changeStatusMessage(item, _("Error : Connection closed."));
1026 //fprintf(stderr, "<< STOP >>\n");
1027 return 0; /* error or term */
1032 void writecmd(monitoritem* item, const char* command) {
1034 item->D_sock->msglen = strlen(command);
1035 pm_strcpy(&item->D_sock->msg, command);
1036 bnet_send(item->D_sock);
1040 /* Note: Does not seem to work either on Gnome nor KDE... */
1041 void trayMessage(const char *fmt,...)
1046 va_start(arg_ptr, fmt);
1047 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1050 fprintf(stderr, buf);
1052 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1055 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1059 va_start(arg_ptr, fmt);
1060 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1063 gtk_label_set_text(GTK_LABEL(item->label), buf);
1066 void updateStatusIcon(monitoritem* item) {
1070 /* For the current status, select the two worse for blinking,
1071 but never blink a D_Sock == NULL error with idle. */
1072 stateenum state1, state2, oldstate;
1073 gboolean onenull = FALSE;
1077 for (int i = 0; i < nitems; i++) {
1078 if (items[i].D_sock == NULL) onenull = TRUE;
1079 if (items[i].state >= state1) {
1081 state1 = items[i].state;
1083 else if (items[i].state > state2) {
1084 state2 = items[i].state;
1086 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1089 if ((onenull == TRUE) && (state2 == idle)) {
1093 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1096 if ((blinkstate) && (item->D_sock != NULL)) {
1097 if (item->state > 1) { //Warning or error while running
1098 xpm = generateXPM(running, item->oldstate);
1101 xpm = generateXPM(idle, item->oldstate);
1105 xpm = generateXPM(item->state, item->oldstate);
1109 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1111 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1112 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1115 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1117 g_object_unref(G_OBJECT(pixbuf));
1120 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1121 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1122 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1125 strcpy(address, "ff0000");
1128 strcpy(address, "ffffff");
1131 strcpy(address, "00ff00");
1134 strcpy(address, "ffff00");
1138 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1141 strcpy(address, "ff0000");
1144 strcpy(address, "ffffff");
1147 strcpy(address, "00ff00");
1150 strcpy(address, "ffff00");
1154 return (const char**)xpm_generic_var;