2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2008 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);
49 extern bool parse_tmon_config(CONFIG *config, const char *configfile, int exit_code);
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];
67 static CONFIG *config;
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 GtkStatusIcon *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 " -d <nn> set debug level to <nn>\n"
118 " -dt print timestamp in debug output\n"
119 " -t test - read configuration and exit\n"
120 " -? print this message.\n"
121 "\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
124 static GtkWidget *new_image_button(const gchar *stock_id,
125 const gchar *label_text)
132 button = gtk_button_new();
134 box = gtk_hbox_new(FALSE, 0);
135 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
136 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
137 label = gtk_label_new(label_text);
139 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
140 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
142 gtk_widget_show(image);
143 gtk_widget_show(label);
145 gtk_widget_show(box);
147 gtk_container_add(GTK_CONTAINER(button), box);
154 #if TRAY_DEBUG_MEMORY
155 gpointer smt_malloc(gsize n_bytes) {
156 return sm_malloc("GLib", sm_line, n_bytes);
159 gpointer smt_realloc(gpointer mem, gsize n_bytes) {
160 return sm_realloc("GLib", sm_line, mem, n_bytes);
163 gpointer smt_calloc(gsize n_blocks,
164 gsize n_block_bytes) {
165 return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes);
168 void smt_free(gpointer mem) {
169 sm_free("Glib", sm_line, mem);
173 /*********************************************************************
175 * Main Bacula Tray Monitor -- User Interface Program
178 int main(int argc, char *argv[])
180 #if TRAY_DEBUG_MEMORY
182 smvtable.malloc = &smt_malloc;
183 smvtable.realloc = &smt_realloc;
184 smvtable.free = &smt_free;
185 smvtable.calloc = &smt_calloc;
186 smvtable.try_malloc = NULL;
187 smvtable.try_realloc = NULL;
188 g_mem_set_vtable(&smvtable);
192 bool test_config = false;
196 CONFONTRES *con_font;
198 setlocale(LC_ALL, "");
199 bindtextdomain("bacula", LOCALEDIR);
200 textdomain("bacula");
203 my_name_is(argc, argv, "tray-monitor");
204 init_msg(NULL, NULL);
205 working_directory = "/tmp";
207 struct sigaction sigignore;
208 sigignore.sa_flags = 0;
209 sigignore.sa_handler = SIG_IGN;
210 sigfillset(&sigignore.sa_mask);
211 sigaction(SIGPIPE, &sigignore, NULL);
213 gtk_init(&argc, &argv);
215 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
217 case 'c': /* configuration file */
218 if (configfile != NULL) {
221 configfile = bstrdup(optarg);
225 if (*optarg == 't') {
226 dbg_timestamp = true;
228 debug_level = atoi(optarg);
229 if (debug_level <= 0) {
254 if (configfile == NULL) {
255 configfile = bstrdup(CONFIG_FILE);
258 config = new_config_parser();
259 parse_tmon_config(config, configfile, M_ERROR_TERM);
263 foreach_res(monitor, R_MONITOR) {
268 Emsg2(M_ERROR_TERM, 0,
269 _("Error: %d Monitor resources defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
273 foreach_res(dird, R_DIRECTOR) {
274 items[nitems].type = R_DIRECTOR;
275 items[nitems].resource = dird;
276 items[nitems].D_sock = NULL;
277 items[nitems].state = warn;
278 items[nitems].oldstate = warn;
281 foreach_res(filed, R_CLIENT) {
282 items[nitems].type = R_CLIENT;
283 items[nitems].resource = filed;
284 items[nitems].D_sock = NULL;
285 items[nitems].state = warn;
286 items[nitems].oldstate = warn;
289 foreach_res(stored, R_STORAGE) {
290 items[nitems].type = R_STORAGE;
291 items[nitems].resource = stored;
292 items[nitems].D_sock = NULL;
293 items[nitems].state = warn;
294 items[nitems].oldstate = warn;
300 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage or Director resource defined in %s\n"
301 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
308 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
309 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
310 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
311 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
312 strcpy(xpm_generic_var[i], xpm_generic[i]);
315 (void)WSA_Init(); /* Initialize Windows sockets */
318 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
321 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
322 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
323 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
326 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
328 mTrayIcon = gtk_status_icon_new_from_pixbuf(pixbuf);
329 gtk_status_icon_set_tooltip(mTrayIcon, _("Bacula daemon status monitor"));
330 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
331 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
332 g_object_unref(G_OBJECT(pixbuf));
334 mTrayMenu = gtk_menu_new();
338 entry = gtk_menu_item_new_with_label(_("Open status window..."));
339 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
340 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
342 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
344 entry = gtk_menu_item_new_with_label(_("Exit"));
345 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
346 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
348 gtk_widget_show_all(mTrayMenu);
350 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
352 g_timeout_add( 1000, blink, NULL);
354 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
356 gtk_window_set_title(GTK_WINDOW(window), _("Bacula tray monitor"));
358 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
359 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
361 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
363 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
365 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
367 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
369 GtkWidget* separator = gtk_hseparator_new();
370 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
373 GSList *group = NULL;
377 for (int i = 0; i < nitems; i++) {
378 switch (items[i].type) {
380 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
381 g_string_append(str, _(" (DIR)"));
384 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
385 g_string_append(str, _(" (FD)"));
388 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
389 g_string_append(str, _(" (SD)"));
395 radio = gtk_radio_button_new_with_label(group, str->str);
396 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
397 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
399 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
400 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
402 items[i].label = gtk_label_new(_("Unknown status."));
403 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
404 gtk_container_add(GTK_CONTAINER(align), items[i].label);
406 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
407 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
408 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
409 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
410 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
411 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
413 separator = gtk_hseparator_new();
414 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
416 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
419 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
421 textview = gtk_text_view_new();
423 buffer = gtk_text_buffer_new(NULL);
425 gtk_text_buffer_set_text(buffer, "", -1);
428 * Gtk2/pango have different font names. Gnome2 comes with "Monospace 10"
432 foreach_res(con_font, R_CONSOLE_FONT) {
433 if (!con_font->fontface) {
434 Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
437 Dmsg1(100, "Now loading: %s\n",con_font->fontface);
438 font_desc = pango_font_description_from_string(con_font->fontface);
439 if (font_desc == NULL) {
440 Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
441 con_font->hdr.name, con_font->fontface);
443 Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
444 con_font->hdr.name, con_font->fontface);
451 font_desc = pango_font_description_from_string("Monospace 10");
454 font_desc = pango_font_description_from_string("monospace");
457 gtk_widget_modify_font(textview, font_desc);
458 pango_font_description_free(font_desc);
460 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
461 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
463 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
465 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
467 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
469 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
471 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
472 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
473 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
474 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
475 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
476 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
477 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
478 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
480 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
481 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
482 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
484 button = new_image_button("gtk-help", _("About"));
485 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
486 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
488 button = new_image_button("gtk-close", _("Close"));
489 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
490 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
492 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
494 gtk_container_add(GTK_CONTAINER (window), vbox);
496 gtk_widget_show_all(vbox);
500 g_source_remove(timerTag);
504 for (i = 0; i < nitems; i++) {
505 if (items[i].D_sock) {
506 switch (items[i].type) {
508 trayMessage(_("Disconnecting from Director %s:%d\n"), ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport);
511 trayMessage(_("Disconnecting from Client %s:%d\n"), ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport);
514 trayMessage(_("Disconnecting from Storage %s:%d\n"), ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport);
519 //writecmd(&items[i], "quit");
520 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
521 bnet_close(items[i].D_sock);
525 (void)WSACleanup(); /* Cleanup Windows sockets */
527 //Free xpm_generic_var
528 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
529 g_free(xpm_generic_var[i]);
531 g_free(xpm_generic_var);
533 gtk_object_destroy(GTK_OBJECT(window));
534 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
535 config->free_resources();
540 #if TRAY_DEBUG_MEMORY
547 static void MonitorAbout(GtkWidget *widget, gpointer data)
550 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
551 "<span size='x-large' weight='bold'>%s</span>\n\n"
554 "\n<small>%s: %s (%s) %s %s %s</small>",
555 _("Bacula Tray Monitor"),
557 _("Written by Nicolas Boichat\n"),
559 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
561 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
565 "\n%s: %s (%s) %s %s %s",
566 _("Bacula Tray Monitor"),
568 _("Written by Nicolas Boichat\n"),
570 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
572 gtk_dialog_run(GTK_DIALOG(about));
573 gtk_widget_destroy(about);
576 static void MonitorRefresh(GtkWidget *widget, gpointer data)
578 for (int i = 0; i < nitems; i++) {
583 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
585 gtk_widget_hide(window);
586 return TRUE; /* do not destroy the window */
590 * Come here when the user right clicks on the icon.
591 * We display the icon menu.
593 static void TrayIconActivate(GtkWidget *widget, gpointer data)
595 gtk_widget_show(window);
599 * Come here when the user left clicks on the icon.
600 * We popup the status window.
602 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button)
604 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, button,
605 gtk_get_current_event_time());
606 gtk_widget_show(mTrayMenu);
609 static void TrayIconExit(unsigned int activateTime, unsigned int button)
614 static void IntervalChanged(GtkWidget *widget, gpointer data)
616 g_source_remove(timerTag);
617 timerTag = g_timeout_add(
619 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
623 static void DaemonChanged(GtkWidget *widget, monitoritem* data)
625 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
627 for (int i = 0; i < nitems; i++) {
628 if (data == &(items[i])) {
633 g_return_if_fail(fullitem != -1);
635 int oldlastupdated = lastupdated;
636 lastupdated = fullitem-1;
638 lastupdated = oldlastupdated;
642 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
643 switch (item->type) {
645 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
647 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
649 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
651 printf(_("Error, currentitem is not a Client or a Storage..\n"));
658 static gboolean blink(gpointer data) {
659 blinkstate = !blinkstate;
660 for (int i = 0; i < nitems; i++) {
661 updateStatusIcon(&items[i]);
663 updateStatusIcon(NULL);
667 static gboolean fd_read(gpointer data)
670 #if TRAY_DEBUG_MEMORY
671 printf("sm_line=%d\n", sm_line);
673 GtkTextBuffer *newbuffer;
674 GtkTextIter start, stop, nstart, nstop;
678 GString *strlast, *strcurrent;
681 if (lastupdated == nitems) {
685 if (lastupdated == fullitem) {
686 newbuffer = gtk_text_buffer_new(NULL);
688 if (items[lastupdated].type == R_DIRECTOR)
689 docmd(&items[lastupdated], "status Director\n", &list);
691 docmd(&items[lastupdated], "status\n", &list);
695 gtk_text_buffer_get_end_iter(newbuffer, &stop);
696 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
697 if (it->data) g_string_free((GString*)it->data, TRUE);
698 } while ((it = it->next) != NULL);
702 /* Keep the selection if necessary */
703 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
704 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
705 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
708 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
710 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
711 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
715 g_object_unref(buffer);
718 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
721 getstatus(&items[lastupdated], 1, &strcurrent);
722 getstatus(&items[lastupdated], 0, &strlast);
723 updateStatusIcon(&items[lastupdated]);
725 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
727 updateStatusIcon(NULL);
729 g_string_free(strcurrent, TRUE);
730 g_string_free(strlast, TRUE);
735 void append_error_string(GString* str, int joberrors) {
737 g_string_append_printf(str, _(" (%d errors)"), joberrors);
740 g_string_append_printf(str, _(" (%d error)"), joberrors);
744 void getstatus(monitoritem* item, int current, GString** str)
747 stateenum ret = error;
748 int jobid = 0, joberrors = 0;
749 char jobstatus = JS_ErrorTerminated;
753 *str = g_string_sized_new(128);
756 if (item->type == R_DIRECTOR)
757 docmd(&items[lastupdated], ".status dir current\n", &list);
759 docmd(&items[lastupdated], ".status current\n", &list);
762 if (item->type == R_DIRECTOR)
763 docmd(&items[lastupdated], ".status dir last\n", &list);
765 docmd(&items[lastupdated], ".status last\n", &list);
769 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
770 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
771 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
772 g_string_set_size(*str, (*str)->len-1);
776 else if ((it = it->next) == NULL) {
778 g_string_append(*str, _("No current job."));
781 g_string_append(*str, _("No last job."));
785 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
788 ret = (joberrors > 0) ? warn : running;
789 g_string_append_printf(*str, _("Job status: Created"));
790 append_error_string(*str, joberrors);
793 ret = (joberrors > 0) ? warn : running;
794 g_string_append_printf(*str, _("Job status: Running"));
795 append_error_string(*str, joberrors);
798 g_string_append_printf(*str, _("Job status: Blocked"));
799 append_error_string(*str, joberrors);
803 g_string_append_printf(*str, _("Job status: Terminated"));
804 append_error_string(*str, joberrors);
805 ret = (joberrors > 0) ? warn : idle;
807 case JS_ErrorTerminated:
808 g_string_append_printf(*str, _("Job status: Terminated in error"));
809 append_error_string(*str, joberrors);
813 ret = (joberrors > 0) ? warn : running;
814 g_string_append_printf(*str, _("Job status: Error"));
815 append_error_string(*str, joberrors);
818 g_string_append_printf(*str, _("Job status: Fatal error"));
819 append_error_string(*str, joberrors);
823 g_string_append_printf(*str, _("Job status: Verify differences"));
824 append_error_string(*str, joberrors);
828 g_string_append_printf(*str, _("Job status: Canceled"));
829 append_error_string(*str, joberrors);
833 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
834 append_error_string(*str, joberrors);
838 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
839 append_error_string(*str, joberrors);
843 g_string_append_printf(*str, _("Job status: Waiting for new media"));
844 append_error_string(*str, joberrors);
848 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
849 append_error_string(*str, joberrors);
852 case JS_WaitStoreRes:
853 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
854 append_error_string(*str, joberrors);
858 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
859 append_error_string(*str, joberrors);
862 case JS_WaitClientRes:
863 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
864 append_error_string(*str, joberrors);
868 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
869 append_error_string(*str, joberrors);
872 case JS_WaitStartTime:
873 g_string_append_printf(*str, _("Job status: Waiting for start time"));
874 append_error_string(*str, joberrors);
877 case JS_WaitPriority:
878 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
879 append_error_string(*str, joberrors);
883 g_warning(_("Unknown job status %c."), jobstatus);
884 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
885 append_error_string(*str, joberrors);
891 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
898 g_string_free((GString*)it->data, TRUE);
900 } while ((it = it->next) != NULL);
908 item->oldstate = ret;
912 int docmd(monitoritem* item, const char* command, GSList** list)
917 *list = g_slist_alloc();
919 //str = g_string_sized_new(64);
922 memset(&jcr, 0, sizeof(jcr));
928 switch (item->type) {
930 dird = (DIRRES*)item->resource;
931 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
932 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
933 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
934 jcr.dir_bsock = item->D_sock;
937 filed = (CLIENT*)item->resource;
938 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
939 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
940 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
941 jcr.file_bsock = item->D_sock;
944 stored = (STORE*)item->resource;
945 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
946 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
947 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
948 jcr.store_bsock = item->D_sock;
951 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
956 if (item->D_sock == NULL) {
957 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
958 changeStatusMessage(item, _("Cannot connect to daemon."));
960 item->oldstate = error;
964 if (!authenticate_daemon(item, &jcr)) {
965 str = g_string_sized_new(64);
966 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
967 *list = g_slist_append(*list, str);
969 item->oldstate = error;
970 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
975 switch (item->type) {
977 trayMessage(_("Opened connection with Director daemon.\n"));
978 changeStatusMessage(item, _("Opened connection with Director daemon."));
981 trayMessage(_("Opened connection with File daemon.\n"));
982 changeStatusMessage(item, _("Opened connection with File daemon."));
985 trayMessage(_("Opened connection with Storage daemon.\n"));
986 changeStatusMessage(item, _("Opened connection with Storage daemon."));
989 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
995 if (item->type == R_DIRECTOR) { /* Read connection messages... */
997 docmd(item, "", &tlist); /* Usually invalid, but no matter */
1001 g_string_free((GString*)it->data, TRUE);
1003 } while ((it = it->next) != NULL);
1005 g_slist_free(tlist);
1009 if (command[0] != 0)
1010 writecmd(item, command);
1013 if ((stat = bnet_recv(item->D_sock)) >= 0) {
1014 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
1016 else if (stat == BNET_SIGNAL) {
1017 if (item->D_sock->msglen == BNET_EOD) {
1018 //fprintf(stderr, "<< EOD >>\n");
1021 else if (item->D_sock->msglen == BNET_PROMPT) {
1022 //fprintf(stderr, "<< PROMPT >>\n");
1023 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
1026 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
1027 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
1028 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
1031 str = g_string_sized_new(64);
1032 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
1033 *list = g_slist_append(*list, str);
1036 else { /* BNET_HARDEOF || BNET_ERROR */
1037 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
1038 item->D_sock = NULL;
1039 item->state = error;
1040 item->oldstate = error;
1041 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
1042 //fprintf(stderr, _("<< ERROR >>\n"));
1046 if (is_bnet_stop(item->D_sock)) {
1047 g_string_append_printf(str, _("<STOP>\n"));
1048 item->D_sock = NULL;
1049 item->state = error;
1050 item->oldstate = error;
1051 changeStatusMessage(item, _("Error : Connection closed."));
1052 //fprintf(stderr, "<< STOP >>\n");
1053 return 0; /* error or term */
1058 void writecmd(monitoritem* item, const char* command) {
1060 item->D_sock->msglen = strlen(command);
1061 pm_strcpy(&item->D_sock->msg, command);
1062 bnet_send(item->D_sock);
1066 /* Note: Does not seem to work either on Gnome nor KDE... */
1067 void trayMessage(const char *fmt,...)
1072 va_start(arg_ptr, fmt);
1073 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1076 fprintf(stderr, buf);
1078 // gtk_tray_icon_send_message(gtk_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1081 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1085 va_start(arg_ptr, fmt);
1086 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1089 gtk_label_set_text(GTK_LABEL(item->label), buf);
1092 void updateStatusIcon(monitoritem* item) {
1096 /* For the current status, select the two worse for blinking,
1097 but never blink a D_Sock == NULL error with idle. */
1098 stateenum state1, state2, oldstate;
1099 gboolean onenull = FALSE;
1103 for (int i = 0; i < nitems; i++) {
1104 if (items[i].D_sock == NULL) onenull = TRUE;
1105 if (items[i].state >= state1) {
1107 state1 = items[i].state;
1109 else if (items[i].state > state2) {
1110 state2 = items[i].state;
1112 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1115 if ((onenull == TRUE) && (state2 == idle)) {
1119 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1122 if ((blinkstate) && (item->D_sock != NULL)) {
1123 if (item->state > 1) { //Warning or error while running
1124 xpm = generateXPM(running, item->oldstate);
1127 xpm = generateXPM(idle, item->oldstate);
1131 xpm = generateXPM(item->state, item->oldstate);
1135 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1137 gtk_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1138 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1141 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1143 g_object_unref(G_OBJECT(pixbuf));
1146 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1147 static const char** generateXPM(stateenum newstate, stateenum oldstate)
1149 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1152 strcpy(address, "ff0000");
1155 strcpy(address, "ffffff");
1158 strcpy(address, "00ff00");
1161 strcpy(address, "ffff00");
1165 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1168 strcpy(address, "ff0000");
1171 strcpy(address, "ffffff");
1174 strcpy(address, "00ff00");
1177 strcpy(address, "ffff00");
1181 return (const char**)xpm_generic_var;