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 and included
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 "generic.xpm"
43 #define TRAY_DEBUG_MEMORY 0
45 /* Imported functions */
46 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
47 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
48 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
51 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
53 /* Forward referenced functions */
54 void writecmd(monitoritem* item, const char* command);
55 int docmd(monitoritem* item, const char* command, GSList** list);
56 void getstatus(monitoritem* item, int current, GString** str);
58 /* Static variables */
59 static char *configfile = NULL;
60 static MONITOR *monitor;
62 static int nitems = 0;
63 static int fullitem = 0; //Item to be display in detailled status window
64 static int lastupdated = -1; //Last item updated
65 static monitoritem items[32];
67 /* Data received from DIR/FD/SD */
68 static char OKqstatus[] = "%c000 OK .status\n";
69 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
71 /* UI variables and functions */
74 static gboolean fd_read(gpointer data);
75 static gboolean blink(gpointer data);
78 void trayMessage(const char *fmt,...);
79 void updateStatusIcon(monitoritem* item);
80 void changeStatusMessage(monitoritem* item, const char *fmt,...);
81 static const char** generateXPM(stateenum newstate, stateenum oldstate);
84 static void TrayIconActivate(GtkWidget *widget, gpointer data);
85 static void TrayIconExit(unsigned int activateTime, unsigned int button);
86 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
87 static void MonitorAbout(GtkWidget *widget, gpointer data);
88 static void MonitorRefresh(GtkWidget *widget, gpointer data);
89 static void IntervalChanged(GtkWidget *widget, gpointer data);
90 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
91 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
93 static guint timerTag;
94 static GtkStatusIcon *mTrayIcon;
95 static GtkWidget *mTrayMenu;
96 static GtkWidget *window;
97 static GtkWidget *textview;
98 static GtkTextBuffer *buffer;
99 static GtkWidget *timeoutspinner;
100 char** xpm_generic_var;
101 static gboolean blinkstate = TRUE;
103 PangoFontDescription *font_desc = NULL;
105 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
111 "Written by Nicolas Boichat (2004)\n"
112 "\nVersion: %s (%s) %s %s %s\n\n"
113 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
114 " -c <file> set configuration file to file\n"
115 " -dnn set debug level to nn\n"
116 " -t test - read configuration and exit\n"
117 " -? print this message.\n"
118 "\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
121 static GtkWidget *new_image_button(const gchar *stock_id,
122 const gchar *label_text)
129 button = gtk_button_new();
131 box = gtk_hbox_new(FALSE, 0);
132 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
133 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
134 label = gtk_label_new(label_text);
136 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
137 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
139 gtk_widget_show(image);
140 gtk_widget_show(label);
142 gtk_widget_show(box);
144 gtk_container_add(GTK_CONTAINER(button), box);
151 #if TRAY_DEBUG_MEMORY
152 gpointer smt_malloc(gsize n_bytes) {
153 return sm_malloc("GLib", sm_line, n_bytes);
156 gpointer smt_realloc(gpointer mem, gsize n_bytes) {
157 return sm_realloc("GLib", sm_line, mem, n_bytes);
160 gpointer smt_calloc(gsize n_blocks,
161 gsize n_block_bytes) {
162 return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes);
165 void smt_free(gpointer mem) {
166 sm_free("Glib", sm_line, mem);
170 /*********************************************************************
172 * Main Bacula Tray Monitor -- User Interface Program
175 int main(int argc, char *argv[])
177 #if TRAY_DEBUG_MEMORY
179 smvtable.malloc = &smt_malloc;
180 smvtable.realloc = &smt_realloc;
181 smvtable.free = &smt_free;
182 smvtable.calloc = &smt_calloc;
183 smvtable.try_malloc = NULL;
184 smvtable.try_realloc = NULL;
185 g_mem_set_vtable(&smvtable);
189 bool test_config = false;
193 CONFONTRES *con_font;
195 setlocale(LC_ALL, "");
196 bindtextdomain("bacula", LOCALEDIR);
197 textdomain("bacula");
200 my_name_is(argc, argv, "tray-monitor");
201 init_msg(NULL, NULL);
202 working_directory = "/tmp";
204 struct sigaction sigignore;
205 sigignore.sa_flags = 0;
206 sigignore.sa_handler = SIG_IGN;
207 sigfillset(&sigignore.sa_mask);
208 sigaction(SIGPIPE, &sigignore, NULL);
210 gtk_init(&argc, &argv);
212 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
214 case 'c': /* configuration file */
215 if (configfile != NULL) {
218 configfile = bstrdup(optarg);
222 debug_level = atoi(optarg);
223 if (debug_level <= 0) {
247 if (configfile == NULL) {
248 configfile = bstrdup(CONFIG_FILE);
251 parse_config(configfile);
255 foreach_res(monitor, R_MONITOR) {
260 Emsg2(M_ERROR_TERM, 0,
261 _("Error: %d Monitor resources defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
265 foreach_res(dird, R_DIRECTOR) {
266 items[nitems].type = R_DIRECTOR;
267 items[nitems].resource = dird;
268 items[nitems].D_sock = NULL;
269 items[nitems].state = warn;
270 items[nitems].oldstate = warn;
273 foreach_res(filed, R_CLIENT) {
274 items[nitems].type = R_CLIENT;
275 items[nitems].resource = filed;
276 items[nitems].D_sock = NULL;
277 items[nitems].state = warn;
278 items[nitems].oldstate = warn;
281 foreach_res(stored, R_STORAGE) {
282 items[nitems].type = R_STORAGE;
283 items[nitems].resource = stored;
284 items[nitems].D_sock = NULL;
285 items[nitems].state = warn;
286 items[nitems].oldstate = warn;
292 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage or Director resource defined in %s\n"
293 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
300 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
301 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
302 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
303 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
304 strcpy(xpm_generic_var[i], xpm_generic[i]);
307 (void)WSA_Init(); /* Initialize Windows sockets */
310 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
313 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
314 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
315 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
318 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
320 mTrayIcon = gtk_status_icon_new_from_pixbuf(pixbuf);
321 gtk_status_icon_set_tooltip(mTrayIcon, _("Bacula daemon status monitor"));
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)
539 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
540 "<span size='x-large' weight='bold'>%s</span>\n\n"
543 "\n<small>%s: %s (%s) %s %s %s</small>",
544 _("Bacula Tray Monitor"),
546 _("Written by Nicolas Boichat\n"),
548 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
550 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
554 "\n%s: %s (%s) %s %s %s",
555 _("Bacula Tray Monitor"),
557 _("Written by Nicolas Boichat\n"),
559 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
561 gtk_dialog_run(GTK_DIALOG(about));
562 gtk_widget_destroy(about);
565 static void MonitorRefresh(GtkWidget *widget, gpointer data)
567 for (int i = 0; i < nitems; i++) {
572 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
574 gtk_widget_hide(window);
575 return TRUE; /* do not destroy the window */
579 * Come here when the user right clicks on the icon.
580 * We display the icon menu.
582 static void TrayIconActivate(GtkWidget *widget, gpointer data)
584 gtk_widget_show(window);
588 * Come here when the user left clicks on the icon.
589 * We popup the status window.
591 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button)
593 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, button,
594 gtk_get_current_event_time());
595 gtk_widget_show(mTrayMenu);
598 static void TrayIconExit(unsigned int activateTime, unsigned int button)
603 static void IntervalChanged(GtkWidget *widget, gpointer data)
605 g_source_remove(timerTag);
606 timerTag = g_timeout_add(
608 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
612 static void DaemonChanged(GtkWidget *widget, monitoritem* data)
614 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
616 for (int i = 0; i < nitems; i++) {
617 if (data == &(items[i])) {
622 g_return_if_fail(fullitem != -1);
624 int oldlastupdated = lastupdated;
625 lastupdated = fullitem-1;
627 lastupdated = oldlastupdated;
631 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
632 switch (item->type) {
634 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
636 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
638 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
640 printf(_("Error, currentitem is not a Client or a Storage..\n"));
647 static gboolean blink(gpointer data) {
648 blinkstate = !blinkstate;
649 for (int i = 0; i < nitems; i++) {
650 updateStatusIcon(&items[i]);
652 updateStatusIcon(NULL);
656 static gboolean fd_read(gpointer data)
659 #if TRAY_DEBUG_MEMORY
660 printf("sm_line=%d\n", sm_line);
662 GtkTextBuffer *newbuffer;
663 GtkTextIter start, stop, nstart, nstop;
667 GString *strlast, *strcurrent;
670 if (lastupdated == nitems) {
674 if (lastupdated == fullitem) {
675 newbuffer = gtk_text_buffer_new(NULL);
677 if (items[lastupdated].type == R_DIRECTOR)
678 docmd(&items[lastupdated], "status Director\n", &list);
680 docmd(&items[lastupdated], "status\n", &list);
684 gtk_text_buffer_get_end_iter(newbuffer, &stop);
685 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
686 if (it->data) g_string_free((GString*)it->data, TRUE);
687 } while ((it = it->next) != NULL);
691 /* Keep the selection if necessary */
692 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
693 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
694 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
697 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
699 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
700 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
704 g_object_unref(buffer);
707 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
710 getstatus(&items[lastupdated], 1, &strcurrent);
711 getstatus(&items[lastupdated], 0, &strlast);
712 updateStatusIcon(&items[lastupdated]);
714 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
716 updateStatusIcon(NULL);
718 g_string_free(strcurrent, TRUE);
719 g_string_free(strlast, TRUE);
724 void append_error_string(GString* str, int joberrors) {
726 g_string_append_printf(str, _(" (%d errors)"), joberrors);
729 g_string_append_printf(str, _(" (%d error)"), joberrors);
733 void getstatus(monitoritem* item, int current, GString** str)
736 stateenum ret = error;
737 int jobid = 0, joberrors = 0;
738 char jobstatus = JS_ErrorTerminated;
742 *str = g_string_sized_new(128);
745 if (item->type == R_DIRECTOR)
746 docmd(&items[lastupdated], ".status dir current\n", &list);
748 docmd(&items[lastupdated], ".status current\n", &list);
751 if (item->type == R_DIRECTOR)
752 docmd(&items[lastupdated], ".status dir last\n", &list);
754 docmd(&items[lastupdated], ".status last\n", &list);
758 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
759 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
760 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
761 g_string_set_size(*str, (*str)->len-1);
765 else if ((it = it->next) == NULL) {
767 g_string_append(*str, _("No current job."));
770 g_string_append(*str, _("No last job."));
774 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
777 ret = (joberrors > 0) ? warn : running;
778 g_string_append_printf(*str, _("Job status: Created"));
779 append_error_string(*str, joberrors);
782 ret = (joberrors > 0) ? warn : running;
783 g_string_append_printf(*str, _("Job status: Running"));
784 append_error_string(*str, joberrors);
787 g_string_append_printf(*str, _("Job status: Blocked"));
788 append_error_string(*str, joberrors);
792 g_string_append_printf(*str, _("Job status: Terminated"));
793 append_error_string(*str, joberrors);
794 ret = (joberrors > 0) ? warn : idle;
796 case JS_ErrorTerminated:
797 g_string_append_printf(*str, _("Job status: Terminated in error"));
798 append_error_string(*str, joberrors);
802 ret = (joberrors > 0) ? warn : running;
803 g_string_append_printf(*str, _("Job status: Error"));
804 append_error_string(*str, joberrors);
807 g_string_append_printf(*str, _("Job status: Fatal error"));
808 append_error_string(*str, joberrors);
812 g_string_append_printf(*str, _("Job status: Verify differences"));
813 append_error_string(*str, joberrors);
817 g_string_append_printf(*str, _("Job status: Canceled"));
818 append_error_string(*str, joberrors);
822 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
823 append_error_string(*str, joberrors);
827 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
828 append_error_string(*str, joberrors);
832 g_string_append_printf(*str, _("Job status: Waiting for new media"));
833 append_error_string(*str, joberrors);
837 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
838 append_error_string(*str, joberrors);
841 case JS_WaitStoreRes:
842 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
843 append_error_string(*str, joberrors);
847 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
848 append_error_string(*str, joberrors);
851 case JS_WaitClientRes:
852 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
853 append_error_string(*str, joberrors);
857 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
858 append_error_string(*str, joberrors);
861 case JS_WaitStartTime:
862 g_string_append_printf(*str, _("Job status: Waiting for start time"));
863 append_error_string(*str, joberrors);
866 case JS_WaitPriority:
867 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
868 append_error_string(*str, joberrors);
872 g_warning(_("Unknown job status %c."), jobstatus);
873 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
874 append_error_string(*str, joberrors);
880 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
887 g_string_free((GString*)it->data, TRUE);
889 } while ((it = it->next) != NULL);
897 item->oldstate = ret;
901 int docmd(monitoritem* item, const char* command, GSList** list)
906 *list = g_slist_alloc();
908 //str = g_string_sized_new(64);
911 memset(&jcr, 0, sizeof(jcr));
917 switch (item->type) {
919 dird = (DIRRES*)item->resource;
920 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
921 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
922 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
923 jcr.dir_bsock = item->D_sock;
926 filed = (CLIENT*)item->resource;
927 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
928 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
929 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
930 jcr.file_bsock = item->D_sock;
933 stored = (STORE*)item->resource;
934 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
935 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
936 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
937 jcr.store_bsock = item->D_sock;
940 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
945 if (item->D_sock == NULL) {
946 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
947 changeStatusMessage(item, _("Cannot connect to daemon."));
949 item->oldstate = error;
953 if (!authenticate_daemon(item, &jcr)) {
954 str = g_string_sized_new(64);
955 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
956 *list = g_slist_append(*list, str);
958 item->oldstate = error;
959 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
964 switch (item->type) {
966 trayMessage(_("Opened connection with Director daemon.\n"));
967 changeStatusMessage(item, _("Opened connection with Director daemon."));
970 trayMessage(_("Opened connection with File daemon.\n"));
971 changeStatusMessage(item, _("Opened connection with File daemon."));
974 trayMessage(_("Opened connection with Storage daemon.\n"));
975 changeStatusMessage(item, _("Opened connection with Storage daemon."));
978 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
984 if (item->type == R_DIRECTOR) { /* Read connection messages... */
986 docmd(item, "", &tlist); /* Usually invalid, but no matter */
990 g_string_free((GString*)it->data, TRUE);
992 } while ((it = it->next) != NULL);
999 writecmd(item, command);
1002 if ((stat = bnet_recv(item->D_sock)) >= 0) {
1003 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
1005 else if (stat == BNET_SIGNAL) {
1006 if (item->D_sock->msglen == BNET_EOD) {
1007 //fprintf(stderr, "<< EOD >>\n");
1010 else if (item->D_sock->msglen == BNET_PROMPT) {
1011 //fprintf(stderr, "<< PROMPT >>\n");
1012 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
1015 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
1016 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
1017 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
1020 str = g_string_sized_new(64);
1021 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
1022 *list = g_slist_append(*list, str);
1025 else { /* BNET_HARDEOF || BNET_ERROR */
1026 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
1027 item->D_sock = NULL;
1028 item->state = error;
1029 item->oldstate = error;
1030 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
1031 //fprintf(stderr, _("<< ERROR >>\n"));
1035 if (is_bnet_stop(item->D_sock)) {
1036 g_string_append_printf(str, _("<STOP>\n"));
1037 item->D_sock = NULL;
1038 item->state = error;
1039 item->oldstate = error;
1040 changeStatusMessage(item, _("Error : Connection closed."));
1041 //fprintf(stderr, "<< STOP >>\n");
1042 return 0; /* error or term */
1047 void writecmd(monitoritem* item, const char* command) {
1049 item->D_sock->msglen = strlen(command);
1050 pm_strcpy(&item->D_sock->msg, command);
1051 bnet_send(item->D_sock);
1055 /* Note: Does not seem to work either on Gnome nor KDE... */
1056 void trayMessage(const char *fmt,...)
1061 va_start(arg_ptr, fmt);
1062 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1065 fprintf(stderr, buf);
1067 // gtk_tray_icon_send_message(gtk_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1070 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1074 va_start(arg_ptr, fmt);
1075 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1078 gtk_label_set_text(GTK_LABEL(item->label), buf);
1081 void updateStatusIcon(monitoritem* item) {
1085 /* For the current status, select the two worse for blinking,
1086 but never blink a D_Sock == NULL error with idle. */
1087 stateenum state1, state2, oldstate;
1088 gboolean onenull = FALSE;
1092 for (int i = 0; i < nitems; i++) {
1093 if (items[i].D_sock == NULL) onenull = TRUE;
1094 if (items[i].state >= state1) {
1096 state1 = items[i].state;
1098 else if (items[i].state > state2) {
1099 state2 = items[i].state;
1101 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1104 if ((onenull == TRUE) && (state2 == idle)) {
1108 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1111 if ((blinkstate) && (item->D_sock != NULL)) {
1112 if (item->state > 1) { //Warning or error while running
1113 xpm = generateXPM(running, item->oldstate);
1116 xpm = generateXPM(idle, item->oldstate);
1120 xpm = generateXPM(item->state, item->oldstate);
1124 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1126 gtk_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1127 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1130 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1132 g_object_unref(G_OBJECT(pixbuf));
1135 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1136 static const char** generateXPM(stateenum newstate, stateenum oldstate)
1138 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1141 strcpy(address, "ff0000");
1144 strcpy(address, "ffffff");
1147 strcpy(address, "00ff00");
1150 strcpy(address, "ffff00");
1154 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1157 strcpy(address, "ff0000");
1160 strcpy(address, "ffffff");
1163 strcpy(address, "00ff00");
1166 strcpy(address, "ffff00");
1170 return (const char**)xpm_generic_var;