2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2009 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 Kern Sibbald.
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 static GtkWidget *scrolledWindow;
103 char** xpm_generic_var;
104 static gboolean blinkstate = TRUE;
106 PangoFontDescription *font_desc = NULL;
108 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
114 "Written by Nicolas Boichat (2004)\n"
115 "\nVersion: %s (%s) %s %s %s\n\n"
116 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
117 " -c <file> set configuration file to file\n"
118 " -d <nn> set debug level to <nn>\n"
119 " -dt print timestamp in debug output\n"
120 " -t test - read configuration and exit\n"
121 " -? print this message.\n"
122 "\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
125 static GtkWidget *new_image_button(const gchar *stock_id,
126 const gchar *label_text)
133 button = gtk_button_new();
135 box = gtk_hbox_new(FALSE, 0);
136 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
137 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
138 label = gtk_label_new(label_text);
140 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
141 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
143 gtk_widget_show(image);
144 gtk_widget_show(label);
146 gtk_widget_show(box);
148 gtk_container_add(GTK_CONTAINER(button), box);
155 #if TRAY_DEBUG_MEMORY
156 gpointer smt_malloc(gsize n_bytes) {
157 return sm_malloc("GLib", sm_line, n_bytes);
160 gpointer smt_realloc(gpointer mem, gsize n_bytes) {
161 return sm_realloc("GLib", sm_line, mem, n_bytes);
164 gpointer smt_calloc(gsize n_blocks,
165 gsize n_block_bytes) {
166 return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes);
169 void smt_free(gpointer mem) {
170 sm_free("Glib", sm_line, mem);
174 /*********************************************************************
176 * Main Bacula Tray Monitor -- User Interface Program
179 int main(int argc, char *argv[])
181 #if TRAY_DEBUG_MEMORY
183 smvtable.malloc = &smt_malloc;
184 smvtable.realloc = &smt_realloc;
185 smvtable.free = &smt_free;
186 smvtable.calloc = &smt_calloc;
187 smvtable.try_malloc = NULL;
188 smvtable.try_realloc = NULL;
189 g_mem_set_vtable(&smvtable);
193 bool test_config = false;
197 CONFONTRES *con_font;
199 setlocale(LC_ALL, "");
200 bindtextdomain("bacula", LOCALEDIR);
201 textdomain("bacula");
204 my_name_is(argc, argv, "tray-monitor");
205 init_msg(NULL, NULL);
206 working_directory = "/tmp";
208 struct sigaction sigignore;
209 sigignore.sa_flags = 0;
210 sigignore.sa_handler = SIG_IGN;
211 sigfillset(&sigignore.sa_mask);
212 sigaction(SIGPIPE, &sigignore, NULL);
214 gtk_init(&argc, &argv);
216 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
218 case 'c': /* configuration file */
219 if (configfile != NULL) {
222 configfile = bstrdup(optarg);
226 if (*optarg == 't') {
227 dbg_timestamp = true;
229 debug_level = atoi(optarg);
230 if (debug_level <= 0) {
255 if (configfile == NULL) {
256 configfile = bstrdup(CONFIG_FILE);
259 config = new_config_parser();
260 parse_tmon_config(config, configfile, M_ERROR_TERM);
264 foreach_res(monitor, R_MONITOR) {
269 Emsg2(M_ERROR_TERM, 0,
270 _("Error: %d Monitor resources defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
274 foreach_res(dird, R_DIRECTOR) {
275 items[nitems].type = R_DIRECTOR;
276 items[nitems].resource = dird;
277 items[nitems].D_sock = NULL;
278 items[nitems].state = warn;
279 items[nitems].oldstate = warn;
282 foreach_res(filed, R_CLIENT) {
283 items[nitems].type = R_CLIENT;
284 items[nitems].resource = filed;
285 items[nitems].D_sock = NULL;
286 items[nitems].state = warn;
287 items[nitems].oldstate = warn;
290 foreach_res(stored, R_STORAGE) {
291 items[nitems].type = R_STORAGE;
292 items[nitems].resource = stored;
293 items[nitems].D_sock = NULL;
294 items[nitems].state = warn;
295 items[nitems].oldstate = warn;
301 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage or Director resource defined in %s\n"
302 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
309 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
310 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
311 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
312 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
313 strcpy(xpm_generic_var[i], xpm_generic[i]);
316 (void)WSA_Init(); /* Initialize Windows sockets */
319 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
322 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
323 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
324 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
327 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
329 mTrayIcon = gtk_status_icon_new_from_pixbuf(pixbuf);
330 gtk_status_icon_set_tooltip(mTrayIcon, _("Bacula daemon status monitor"));
331 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
332 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
333 g_object_unref(G_OBJECT(pixbuf));
335 mTrayMenu = gtk_menu_new();
339 entry = gtk_menu_item_new_with_label(_("Open status window..."));
340 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
341 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
343 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
345 entry = gtk_menu_item_new_with_label(_("Exit"));
346 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
347 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
349 gtk_widget_show_all(mTrayMenu);
351 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
353 g_timeout_add( 1000, blink, NULL);
355 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
357 gtk_window_set_title(GTK_WINDOW(window), _("Bacula tray monitor"));
359 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
360 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
362 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
364 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
366 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
368 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
370 GtkWidget* separator = gtk_hseparator_new();
371 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
374 GSList *group = NULL;
378 for (int i = 0; i < nitems; i++) {
379 switch (items[i].type) {
381 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
382 g_string_append(str, _(" (DIR)"));
385 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
386 g_string_append(str, _(" (FD)"));
389 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
390 g_string_append(str, _(" (SD)"));
396 radio = gtk_radio_button_new_with_label(group, str->str);
397 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
398 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
400 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
401 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
403 items[i].label = gtk_label_new(_("Unknown status."));
404 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
405 gtk_container_add(GTK_CONTAINER(align), items[i].label);
407 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
408 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
409 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
410 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
411 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
412 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
414 separator = gtk_hseparator_new();
415 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
417 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
420 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
422 textview = gtk_text_view_new();
424 scrolledWindow = gtk_scrolled_window_new(NULL, NULL);
425 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledWindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
426 gtk_container_add(GTK_CONTAINER (scrolledWindow), textview);
428 buffer = gtk_text_buffer_new(NULL);
430 gtk_text_buffer_set_text(buffer, "", -1);
433 * Gtk2/pango have different font names. Gnome2 comes with "Monospace 10"
437 foreach_res(con_font, R_CONSOLE_FONT) {
438 if (!con_font->fontface) {
439 Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
442 Dmsg1(100, "Now loading: %s\n",con_font->fontface);
443 font_desc = pango_font_description_from_string(con_font->fontface);
444 if (font_desc == NULL) {
445 Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
446 con_font->hdr.name, con_font->fontface);
448 Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
449 con_font->hdr.name, con_font->fontface);
456 font_desc = pango_font_description_from_string("Monospace 10");
459 font_desc = pango_font_description_from_string("monospace");
462 gtk_widget_modify_font(textview, font_desc);
463 pango_font_description_free(font_desc);
465 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
466 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
468 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
470 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
472 gtk_box_pack_start(GTK_BOX(vbox), scrolledWindow, TRUE, TRUE, 0);
474 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
476 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
477 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
478 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
479 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
480 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
481 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
482 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
483 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
485 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
486 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
487 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
489 button = new_image_button("gtk-help", _("About"));
490 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
491 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
493 button = new_image_button("gtk-close", _("Close"));
494 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
495 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
497 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
499 gtk_container_add(GTK_CONTAINER (window), vbox);
501 gtk_widget_show_all(vbox);
505 g_source_remove(timerTag);
509 for (i = 0; i < nitems; i++) {
510 if (items[i].D_sock) {
511 switch (items[i].type) {
513 trayMessage(_("Disconnecting from Director %s:%d\n"), ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport);
516 trayMessage(_("Disconnecting from Client %s:%d\n"), ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport);
519 trayMessage(_("Disconnecting from Storage %s:%d\n"), ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport);
524 //writecmd(&items[i], "quit");
525 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
526 bnet_close(items[i].D_sock);
530 (void)WSACleanup(); /* Cleanup Windows sockets */
532 //Free xpm_generic_var
533 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
534 g_free(xpm_generic_var[i]);
536 g_free(xpm_generic_var);
538 gtk_object_destroy(GTK_OBJECT(window));
539 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
540 config->free_resources();
545 #if TRAY_DEBUG_MEMORY
552 static void MonitorAbout(GtkWidget *widget, gpointer data)
555 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
556 "<span size='x-large' weight='bold'>%s</span>\n\n"
559 "\n<small>%s: %s (%s) %s %s %s</small>",
560 _("Bacula Tray Monitor"),
562 _("Written by Nicolas Boichat\n"),
564 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
566 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
570 "\n%s: %s (%s) %s %s %s",
571 _("Bacula Tray Monitor"),
573 _("Written by Nicolas Boichat\n"),
575 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
577 gtk_dialog_run(GTK_DIALOG(about));
578 gtk_widget_destroy(about);
581 static void MonitorRefresh(GtkWidget *widget, gpointer data)
583 for (int i = 0; i < nitems; i++) {
588 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
590 gtk_widget_hide(window);
591 return TRUE; /* do not destroy the window */
595 * Come here when the user right clicks on the icon.
596 * We display the icon menu.
598 static void TrayIconActivate(GtkWidget *widget, gpointer data)
600 gtk_widget_show(window);
604 * Come here when the user left clicks on the icon.
605 * We popup the status window.
607 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button)
609 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, button,
610 gtk_get_current_event_time());
611 gtk_widget_show(mTrayMenu);
614 static void TrayIconExit(unsigned int activateTime, unsigned int button)
619 static void IntervalChanged(GtkWidget *widget, gpointer data)
621 g_source_remove(timerTag);
622 timerTag = g_timeout_add(
624 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
628 static void DaemonChanged(GtkWidget *widget, monitoritem* data)
630 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
632 for (int i = 0; i < nitems; i++) {
633 if (data == &(items[i])) {
638 g_return_if_fail(fullitem != -1);
640 int oldlastupdated = lastupdated;
641 lastupdated = fullitem-1;
643 lastupdated = oldlastupdated;
647 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
648 switch (item->type) {
650 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
652 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
654 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
656 printf(_("Error, currentitem is not a Client or a Storage..\n"));
663 static gboolean blink(gpointer data) {
664 blinkstate = !blinkstate;
665 for (int i = 0; i < nitems; i++) {
666 updateStatusIcon(&items[i]);
668 updateStatusIcon(NULL);
672 static gboolean fd_read(gpointer data)
675 #if TRAY_DEBUG_MEMORY
676 printf("sm_line=%d\n", sm_line);
678 GtkTextBuffer *newbuffer;
679 GtkTextIter start, stop, nstart, nstop;
683 GString *strlast, *strcurrent;
686 if (lastupdated == nitems) {
690 if (lastupdated == fullitem) {
691 newbuffer = gtk_text_buffer_new(NULL);
693 if (items[lastupdated].type == R_DIRECTOR)
694 docmd(&items[lastupdated], "status Director\n", &list);
696 docmd(&items[lastupdated], "status\n", &list);
700 gtk_text_buffer_get_end_iter(newbuffer, &stop);
701 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
702 if (it->data) g_string_free((GString*)it->data, TRUE);
703 } while ((it = it->next) != NULL);
707 /* Keep the selection if necessary */
708 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
709 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
710 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
713 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
715 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
716 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
720 g_object_unref(buffer);
723 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
726 getstatus(&items[lastupdated], 1, &strcurrent);
727 getstatus(&items[lastupdated], 0, &strlast);
728 updateStatusIcon(&items[lastupdated]);
730 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
732 updateStatusIcon(NULL);
734 g_string_free(strcurrent, TRUE);
735 g_string_free(strlast, TRUE);
740 void append_error_string(GString* str, int joberrors) {
742 g_string_append_printf(str, _(" (%d errors)"), joberrors);
745 g_string_append_printf(str, _(" (%d error)"), joberrors);
749 void getstatus(monitoritem* item, int current, GString** str)
752 stateenum ret = error;
753 int jobid = 0, joberrors = 0;
754 char jobstatus = JS_ErrorTerminated;
758 *str = g_string_sized_new(128);
761 if (item->type == R_DIRECTOR)
762 docmd(&items[lastupdated], ".status dir current\n", &list);
764 docmd(&items[lastupdated], ".status current\n", &list);
767 if (item->type == R_DIRECTOR)
768 docmd(&items[lastupdated], ".status dir last\n", &list);
770 docmd(&items[lastupdated], ".status last\n", &list);
774 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
775 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
776 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
777 g_string_set_size(*str, (*str)->len-1);
781 else if ((it = it->next) == NULL) {
783 g_string_append(*str, _("No current job."));
786 g_string_append(*str, _("No last job."));
790 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
793 ret = (joberrors > 0) ? warn : running;
794 g_string_append_printf(*str, _("Job status: Created"));
795 append_error_string(*str, joberrors);
798 ret = (joberrors > 0) ? warn : running;
799 g_string_append_printf(*str, _("Job status: Running"));
800 append_error_string(*str, joberrors);
803 g_string_append_printf(*str, _("Job status: Blocked"));
804 append_error_string(*str, joberrors);
808 g_string_append_printf(*str, _("Job status: Terminated"));
809 append_error_string(*str, joberrors);
810 ret = (joberrors > 0) ? warn : idle;
812 case JS_ErrorTerminated:
813 g_string_append_printf(*str, _("Job status: Terminated in error"));
814 append_error_string(*str, joberrors);
818 ret = (joberrors > 0) ? warn : running;
819 g_string_append_printf(*str, _("Job status: Error"));
820 append_error_string(*str, joberrors);
823 g_string_append_printf(*str, _("Job status: Fatal error"));
824 append_error_string(*str, joberrors);
828 g_string_append_printf(*str, _("Job status: Verify differences"));
829 append_error_string(*str, joberrors);
833 g_string_append_printf(*str, _("Job status: Canceled"));
834 append_error_string(*str, joberrors);
838 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
839 append_error_string(*str, joberrors);
843 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
844 append_error_string(*str, joberrors);
848 g_string_append_printf(*str, _("Job status: Waiting for new media"));
849 append_error_string(*str, joberrors);
853 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
854 append_error_string(*str, joberrors);
857 case JS_WaitStoreRes:
858 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
859 append_error_string(*str, joberrors);
863 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
864 append_error_string(*str, joberrors);
867 case JS_WaitClientRes:
868 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
869 append_error_string(*str, joberrors);
873 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
874 append_error_string(*str, joberrors);
877 case JS_WaitStartTime:
878 g_string_append_printf(*str, _("Job status: Waiting for start time"));
879 append_error_string(*str, joberrors);
882 case JS_WaitPriority:
883 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
884 append_error_string(*str, joberrors);
888 g_warning(_("Unknown job status %c."), jobstatus);
889 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
890 append_error_string(*str, joberrors);
896 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
903 g_string_free((GString*)it->data, TRUE);
905 } while ((it = it->next) != NULL);
913 item->oldstate = ret;
917 int docmd(monitoritem* item, const char* command, GSList** list)
922 *list = g_slist_alloc();
924 //str = g_string_sized_new(64);
927 memset(&jcr, 0, sizeof(jcr));
933 switch (item->type) {
935 dird = (DIRRES*)item->resource;
936 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
937 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
938 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
939 jcr.dir_bsock = item->D_sock;
942 filed = (CLIENT*)item->resource;
943 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
944 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
945 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
946 jcr.file_bsock = item->D_sock;
949 stored = (STORE*)item->resource;
950 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
951 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
952 item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
953 jcr.store_bsock = item->D_sock;
956 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
961 if (item->D_sock == NULL) {
962 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
963 changeStatusMessage(item, _("Cannot connect to daemon."));
965 item->oldstate = error;
969 if (!authenticate_daemon(item, &jcr)) {
970 str = g_string_sized_new(64);
971 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
972 *list = g_slist_append(*list, str);
974 item->oldstate = error;
975 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
980 switch (item->type) {
982 trayMessage(_("Opened connection with Director daemon.\n"));
983 changeStatusMessage(item, _("Opened connection with Director daemon."));
986 trayMessage(_("Opened connection with File daemon.\n"));
987 changeStatusMessage(item, _("Opened connection with File daemon."));
990 trayMessage(_("Opened connection with Storage daemon.\n"));
991 changeStatusMessage(item, _("Opened connection with Storage daemon."));
994 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
1000 if (item->type == R_DIRECTOR) { /* Read connection messages... */
1002 docmd(item, "", &tlist); /* Usually invalid, but no matter */
1006 g_string_free((GString*)it->data, TRUE);
1008 } while ((it = it->next) != NULL);
1010 g_slist_free(tlist);
1014 if (command[0] != 0)
1015 writecmd(item, command);
1018 if ((stat = bnet_recv(item->D_sock)) >= 0) {
1019 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
1021 else if (stat == BNET_SIGNAL) {
1022 if (item->D_sock->msglen == BNET_EOD) {
1023 //fprintf(stderr, "<< EOD >>\n");
1026 else if (item->D_sock->msglen == BNET_PROMPT) {
1027 //fprintf(stderr, "<< PROMPT >>\n");
1028 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
1031 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
1032 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
1033 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
1036 str = g_string_sized_new(64);
1037 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
1038 *list = g_slist_append(*list, str);
1041 else { /* BNET_HARDEOF || BNET_ERROR */
1042 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
1043 item->D_sock = NULL;
1044 item->state = error;
1045 item->oldstate = error;
1046 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
1047 //fprintf(stderr, _("<< ERROR >>\n"));
1051 if (is_bnet_stop(item->D_sock)) {
1052 g_string_append_printf(str, _("<STOP>\n"));
1053 item->D_sock = NULL;
1054 item->state = error;
1055 item->oldstate = error;
1056 changeStatusMessage(item, _("Error : Connection closed."));
1057 //fprintf(stderr, "<< STOP >>\n");
1058 return 0; /* error or term */
1063 void writecmd(monitoritem* item, const char* command) {
1065 item->D_sock->msglen = strlen(command);
1066 pm_strcpy(&item->D_sock->msg, command);
1067 bnet_send(item->D_sock);
1071 /* Note: Does not seem to work either on Gnome nor KDE... */
1072 void trayMessage(const char *fmt,...)
1077 va_start(arg_ptr, fmt);
1078 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1081 fprintf(stderr, buf);
1083 // gtk_tray_icon_send_message(gtk_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1086 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1090 va_start(arg_ptr, fmt);
1091 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1094 gtk_label_set_text(GTK_LABEL(item->label), buf);
1097 void updateStatusIcon(monitoritem* item) {
1101 /* For the current status, select the two worse for blinking,
1102 but never blink a D_Sock == NULL error with idle. */
1103 stateenum state1, state2, oldstate;
1104 gboolean onenull = FALSE;
1108 for (int i = 0; i < nitems; i++) {
1109 if (items[i].D_sock == NULL) onenull = TRUE;
1110 if (items[i].state >= state1) {
1112 state1 = items[i].state;
1114 else if (items[i].state > state2) {
1115 state2 = items[i].state;
1117 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1120 if ((onenull == TRUE) && (state2 == idle)) {
1124 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1127 if ((blinkstate) && (item->D_sock != NULL)) {
1128 if (item->state > 1) { //Warning or error while running
1129 xpm = generateXPM(running, item->oldstate);
1132 xpm = generateXPM(idle, item->oldstate);
1136 xpm = generateXPM(item->state, item->oldstate);
1140 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1142 gtk_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1143 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1146 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1148 g_object_unref(G_OBJECT(pixbuf));
1151 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1152 static const char** generateXPM(stateenum newstate, stateenum oldstate)
1154 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1157 strcpy(address, "ff0000");
1160 strcpy(address, "ffffff");
1163 strcpy(address, "00ff00");
1166 strcpy(address, "ffff00");
1170 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1173 strcpy(address, "ff0000");
1176 strcpy(address, "ffffff");
1179 strcpy(address, "00ff00");
1182 strcpy(address, "ffff00");
1186 return (const char**)xpm_generic_var;