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 " -d <nn> set debug level to <nn>\n"
116 " -dt print timestamp in debug output\n"
117 " -t test - read configuration and exit\n"
118 " -? print this message.\n"
119 "\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
122 static GtkWidget *new_image_button(const gchar *stock_id,
123 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 if (*optarg == 't') {
224 dbg_timestamp = true;
226 debug_level = atoi(optarg);
227 if (debug_level <= 0) {
252 if (configfile == NULL) {
253 configfile = bstrdup(CONFIG_FILE);
256 parse_config(configfile);
260 foreach_res(monitor, R_MONITOR) {
265 Emsg2(M_ERROR_TERM, 0,
266 _("Error: %d Monitor resources defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
270 foreach_res(dird, R_DIRECTOR) {
271 items[nitems].type = R_DIRECTOR;
272 items[nitems].resource = dird;
273 items[nitems].D_sock = NULL;
274 items[nitems].state = warn;
275 items[nitems].oldstate = warn;
278 foreach_res(filed, R_CLIENT) {
279 items[nitems].type = R_CLIENT;
280 items[nitems].resource = filed;
281 items[nitems].D_sock = NULL;
282 items[nitems].state = warn;
283 items[nitems].oldstate = warn;
286 foreach_res(stored, R_STORAGE) {
287 items[nitems].type = R_STORAGE;
288 items[nitems].resource = stored;
289 items[nitems].D_sock = NULL;
290 items[nitems].state = warn;
291 items[nitems].oldstate = warn;
297 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage or Director resource defined in %s\n"
298 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
305 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
306 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
307 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
308 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
309 strcpy(xpm_generic_var[i], xpm_generic[i]);
312 (void)WSA_Init(); /* Initialize Windows sockets */
315 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
318 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
319 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
320 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
323 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
325 mTrayIcon = gtk_status_icon_new_from_pixbuf(pixbuf);
326 gtk_status_icon_set_tooltip(mTrayIcon, _("Bacula daemon status monitor"));
327 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
328 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
329 g_object_unref(G_OBJECT(pixbuf));
331 mTrayMenu = gtk_menu_new();
335 entry = gtk_menu_item_new_with_label(_("Open status window..."));
336 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
337 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
339 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
341 entry = gtk_menu_item_new_with_label(_("Exit"));
342 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
343 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
345 gtk_widget_show_all(mTrayMenu);
347 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
349 g_timeout_add( 1000, blink, NULL);
351 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
353 gtk_window_set_title(GTK_WINDOW(window), _("Bacula tray monitor"));
355 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
356 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
358 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
360 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
362 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
364 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
366 GtkWidget* separator = gtk_hseparator_new();
367 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
370 GSList *group = NULL;
374 for (int i = 0; i < nitems; i++) {
375 switch (items[i].type) {
377 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
378 g_string_append(str, _(" (DIR)"));
381 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
382 g_string_append(str, _(" (FD)"));
385 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
386 g_string_append(str, _(" (SD)"));
392 radio = gtk_radio_button_new_with_label(group, str->str);
393 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
394 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
396 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
397 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
399 items[i].label = gtk_label_new(_("Unknown status."));
400 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
401 gtk_container_add(GTK_CONTAINER(align), items[i].label);
403 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
404 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
405 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
406 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
407 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
408 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
410 separator = gtk_hseparator_new();
411 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
413 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
416 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
418 textview = gtk_text_view_new();
420 buffer = gtk_text_buffer_new(NULL);
422 gtk_text_buffer_set_text(buffer, "", -1);
425 * Gtk2/pango have different font names. Gnome2 comes with "Monospace 10"
429 foreach_res(con_font, R_CONSOLE_FONT) {
430 if (!con_font->fontface) {
431 Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
434 Dmsg1(100, "Now loading: %s\n",con_font->fontface);
435 font_desc = pango_font_description_from_string(con_font->fontface);
436 if (font_desc == NULL) {
437 Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
438 con_font->hdr.name, con_font->fontface);
440 Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
441 con_font->hdr.name, con_font->fontface);
448 font_desc = pango_font_description_from_string("Monospace 10");
451 font_desc = pango_font_description_from_string("monospace");
454 gtk_widget_modify_font(textview, font_desc);
455 pango_font_description_free(font_desc);
457 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
458 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
460 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
462 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
464 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
466 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
468 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
469 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
470 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
471 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
472 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
473 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
474 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
475 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
477 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
478 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
479 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
481 button = new_image_button("gtk-help", _("About"));
482 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
483 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
485 button = new_image_button("gtk-close", _("Close"));
486 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
487 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
489 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
491 gtk_container_add(GTK_CONTAINER (window), vbox);
493 gtk_widget_show_all(vbox);
497 g_source_remove(timerTag);
501 for (i = 0; i < nitems; i++) {
502 if (items[i].D_sock) {
503 switch (items[i].type) {
505 trayMessage(_("Disconnecting from Director %s:%d\n"), ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport);
508 trayMessage(_("Disconnecting from Client %s:%d\n"), ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport);
511 trayMessage(_("Disconnecting from Storage %s:%d\n"), ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport);
516 //writecmd(&items[i], "quit");
517 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
518 bnet_close(items[i].D_sock);
522 (void)WSACleanup(); /* Cleanup Windows sockets */
524 //Free xpm_generic_var
525 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
526 g_free(xpm_generic_var[i]);
528 g_free(xpm_generic_var);
530 gtk_object_destroy(GTK_OBJECT(window));
531 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
534 #if TRAY_DEBUG_MEMORY
541 static void MonitorAbout(GtkWidget *widget, gpointer data)
544 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
545 "<span size='x-large' weight='bold'>%s</span>\n\n"
548 "\n<small>%s: %s (%s) %s %s %s</small>",
549 _("Bacula Tray Monitor"),
551 _("Written by Nicolas Boichat\n"),
553 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
555 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
559 "\n%s: %s (%s) %s %s %s",
560 _("Bacula Tray Monitor"),
562 _("Written by Nicolas Boichat\n"),
564 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
566 gtk_dialog_run(GTK_DIALOG(about));
567 gtk_widget_destroy(about);
570 static void MonitorRefresh(GtkWidget *widget, gpointer data)
572 for (int i = 0; i < nitems; i++) {
577 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
579 gtk_widget_hide(window);
580 return TRUE; /* do not destroy the window */
584 * Come here when the user right clicks on the icon.
585 * We display the icon menu.
587 static void TrayIconActivate(GtkWidget *widget, gpointer data)
589 gtk_widget_show(window);
593 * Come here when the user left clicks on the icon.
594 * We popup the status window.
596 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button)
598 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, button,
599 gtk_get_current_event_time());
600 gtk_widget_show(mTrayMenu);
603 static void TrayIconExit(unsigned int activateTime, unsigned int button)
608 static void IntervalChanged(GtkWidget *widget, gpointer data)
610 g_source_remove(timerTag);
611 timerTag = g_timeout_add(
613 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
617 static void DaemonChanged(GtkWidget *widget, monitoritem* data)
619 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
621 for (int i = 0; i < nitems; i++) {
622 if (data == &(items[i])) {
627 g_return_if_fail(fullitem != -1);
629 int oldlastupdated = lastupdated;
630 lastupdated = fullitem-1;
632 lastupdated = oldlastupdated;
636 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
637 switch (item->type) {
639 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
641 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
643 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
645 printf(_("Error, currentitem is not a Client or a Storage..\n"));
652 static gboolean blink(gpointer data) {
653 blinkstate = !blinkstate;
654 for (int i = 0; i < nitems; i++) {
655 updateStatusIcon(&items[i]);
657 updateStatusIcon(NULL);
661 static gboolean fd_read(gpointer data)
664 #if TRAY_DEBUG_MEMORY
665 printf("sm_line=%d\n", sm_line);
667 GtkTextBuffer *newbuffer;
668 GtkTextIter start, stop, nstart, nstop;
672 GString *strlast, *strcurrent;
675 if (lastupdated == nitems) {
679 if (lastupdated == fullitem) {
680 newbuffer = gtk_text_buffer_new(NULL);
682 if (items[lastupdated].type == R_DIRECTOR)
683 docmd(&items[lastupdated], "status Director\n", &list);
685 docmd(&items[lastupdated], "status\n", &list);
689 gtk_text_buffer_get_end_iter(newbuffer, &stop);
690 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
691 if (it->data) g_string_free((GString*)it->data, TRUE);
692 } while ((it = it->next) != NULL);
696 /* Keep the selection if necessary */
697 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
698 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
699 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
702 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
704 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
705 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
709 g_object_unref(buffer);
712 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
715 getstatus(&items[lastupdated], 1, &strcurrent);
716 getstatus(&items[lastupdated], 0, &strlast);
717 updateStatusIcon(&items[lastupdated]);
719 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
721 updateStatusIcon(NULL);
723 g_string_free(strcurrent, TRUE);
724 g_string_free(strlast, TRUE);
729 void append_error_string(GString* str, int joberrors) {
731 g_string_append_printf(str, _(" (%d errors)"), joberrors);
734 g_string_append_printf(str, _(" (%d error)"), joberrors);
738 void getstatus(monitoritem* item, int current, GString** str)
741 stateenum ret = error;
742 int jobid = 0, joberrors = 0;
743 char jobstatus = JS_ErrorTerminated;
747 *str = g_string_sized_new(128);
750 if (item->type == R_DIRECTOR)
751 docmd(&items[lastupdated], ".status dir current\n", &list);
753 docmd(&items[lastupdated], ".status current\n", &list);
756 if (item->type == R_DIRECTOR)
757 docmd(&items[lastupdated], ".status dir last\n", &list);
759 docmd(&items[lastupdated], ".status last\n", &list);
763 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
764 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
765 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
766 g_string_set_size(*str, (*str)->len-1);
770 else if ((it = it->next) == NULL) {
772 g_string_append(*str, _("No current job."));
775 g_string_append(*str, _("No last job."));
779 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
782 ret = (joberrors > 0) ? warn : running;
783 g_string_append_printf(*str, _("Job status: Created"));
784 append_error_string(*str, joberrors);
787 ret = (joberrors > 0) ? warn : running;
788 g_string_append_printf(*str, _("Job status: Running"));
789 append_error_string(*str, joberrors);
792 g_string_append_printf(*str, _("Job status: Blocked"));
793 append_error_string(*str, joberrors);
797 g_string_append_printf(*str, _("Job status: Terminated"));
798 append_error_string(*str, joberrors);
799 ret = (joberrors > 0) ? warn : idle;
801 case JS_ErrorTerminated:
802 g_string_append_printf(*str, _("Job status: Terminated in error"));
803 append_error_string(*str, joberrors);
807 ret = (joberrors > 0) ? warn : running;
808 g_string_append_printf(*str, _("Job status: Error"));
809 append_error_string(*str, joberrors);
812 g_string_append_printf(*str, _("Job status: Fatal error"));
813 append_error_string(*str, joberrors);
817 g_string_append_printf(*str, _("Job status: Verify differences"));
818 append_error_string(*str, joberrors);
822 g_string_append_printf(*str, _("Job status: Canceled"));
823 append_error_string(*str, joberrors);
827 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
828 append_error_string(*str, joberrors);
832 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
833 append_error_string(*str, joberrors);
837 g_string_append_printf(*str, _("Job status: Waiting for new media"));
838 append_error_string(*str, joberrors);
842 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
843 append_error_string(*str, joberrors);
846 case JS_WaitStoreRes:
847 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
848 append_error_string(*str, joberrors);
852 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
853 append_error_string(*str, joberrors);
856 case JS_WaitClientRes:
857 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
858 append_error_string(*str, joberrors);
862 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
863 append_error_string(*str, joberrors);
866 case JS_WaitStartTime:
867 g_string_append_printf(*str, _("Job status: Waiting for start time"));
868 append_error_string(*str, joberrors);
871 case JS_WaitPriority:
872 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
873 append_error_string(*str, joberrors);
877 g_warning(_("Unknown job status %c."), jobstatus);
878 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
879 append_error_string(*str, joberrors);
885 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
892 g_string_free((GString*)it->data, TRUE);
894 } while ((it = it->next) != NULL);
902 item->oldstate = ret;
906 int docmd(monitoritem* item, const char* command, GSList** list)
911 *list = g_slist_alloc();
913 //str = g_string_sized_new(64);
916 memset(&jcr, 0, sizeof(jcr));
922 switch (item->type) {
924 dird = (DIRRES*)item->resource;
925 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
926 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
927 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
928 jcr.dir_bsock = item->D_sock;
931 filed = (CLIENT*)item->resource;
932 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
933 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
934 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
935 jcr.file_bsock = item->D_sock;
938 stored = (STORE*)item->resource;
939 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
940 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
941 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
942 jcr.store_bsock = item->D_sock;
945 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
950 if (item->D_sock == NULL) {
951 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
952 changeStatusMessage(item, _("Cannot connect to daemon."));
954 item->oldstate = error;
958 if (!authenticate_daemon(item, &jcr)) {
959 str = g_string_sized_new(64);
960 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
961 *list = g_slist_append(*list, str);
963 item->oldstate = error;
964 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
969 switch (item->type) {
971 trayMessage(_("Opened connection with Director daemon.\n"));
972 changeStatusMessage(item, _("Opened connection with Director daemon."));
975 trayMessage(_("Opened connection with File daemon.\n"));
976 changeStatusMessage(item, _("Opened connection with File daemon."));
979 trayMessage(_("Opened connection with Storage daemon.\n"));
980 changeStatusMessage(item, _("Opened connection with Storage daemon."));
983 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
989 if (item->type == R_DIRECTOR) { /* Read connection messages... */
991 docmd(item, "", &tlist); /* Usually invalid, but no matter */
995 g_string_free((GString*)it->data, TRUE);
997 } while ((it = it->next) != NULL);
1003 if (command[0] != 0)
1004 writecmd(item, command);
1007 if ((stat = bnet_recv(item->D_sock)) >= 0) {
1008 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
1010 else if (stat == BNET_SIGNAL) {
1011 if (item->D_sock->msglen == BNET_EOD) {
1012 //fprintf(stderr, "<< EOD >>\n");
1015 else if (item->D_sock->msglen == BNET_PROMPT) {
1016 //fprintf(stderr, "<< PROMPT >>\n");
1017 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
1020 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
1021 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
1022 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
1025 str = g_string_sized_new(64);
1026 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
1027 *list = g_slist_append(*list, str);
1030 else { /* BNET_HARDEOF || BNET_ERROR */
1031 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
1032 item->D_sock = NULL;
1033 item->state = error;
1034 item->oldstate = error;
1035 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
1036 //fprintf(stderr, _("<< ERROR >>\n"));
1040 if (is_bnet_stop(item->D_sock)) {
1041 g_string_append_printf(str, _("<STOP>\n"));
1042 item->D_sock = NULL;
1043 item->state = error;
1044 item->oldstate = error;
1045 changeStatusMessage(item, _("Error : Connection closed."));
1046 //fprintf(stderr, "<< STOP >>\n");
1047 return 0; /* error or term */
1052 void writecmd(monitoritem* item, const char* command) {
1054 item->D_sock->msglen = strlen(command);
1055 pm_strcpy(&item->D_sock->msg, command);
1056 bnet_send(item->D_sock);
1060 /* Note: Does not seem to work either on Gnome nor KDE... */
1061 void trayMessage(const char *fmt,...)
1066 va_start(arg_ptr, fmt);
1067 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1070 fprintf(stderr, buf);
1072 // gtk_tray_icon_send_message(gtk_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1075 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1079 va_start(arg_ptr, fmt);
1080 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1083 gtk_label_set_text(GTK_LABEL(item->label), buf);
1086 void updateStatusIcon(monitoritem* item) {
1090 /* For the current status, select the two worse for blinking,
1091 but never blink a D_Sock == NULL error with idle. */
1092 stateenum state1, state2, oldstate;
1093 gboolean onenull = FALSE;
1097 for (int i = 0; i < nitems; i++) {
1098 if (items[i].D_sock == NULL) onenull = TRUE;
1099 if (items[i].state >= state1) {
1101 state1 = items[i].state;
1103 else if (items[i].state > state2) {
1104 state2 = items[i].state;
1106 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1109 if ((onenull == TRUE) && (state2 == idle)) {
1113 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1116 if ((blinkstate) && (item->D_sock != NULL)) {
1117 if (item->state > 1) { //Warning or error while running
1118 xpm = generateXPM(running, item->oldstate);
1121 xpm = generateXPM(idle, item->oldstate);
1125 xpm = generateXPM(item->state, item->oldstate);
1129 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1131 gtk_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1132 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1135 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1137 g_object_unref(G_OBJECT(pixbuf));
1140 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1141 static const char** generateXPM(stateenum newstate, stateenum oldstate)
1143 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1146 strcpy(address, "ff0000");
1149 strcpy(address, "ffffff");
1152 strcpy(address, "00ff00");
1155 strcpy(address, "ffff00");
1159 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1162 strcpy(address, "ff0000");
1165 strcpy(address, "ffffff");
1168 strcpy(address, "00ff00");
1171 strcpy(address, "ffff00");
1175 return (const char**)xpm_generic_var;