3 * Bacula Gnome Tray Monitor
5 * Nicolas Boichat, August MMIV
11 Copyright (C) 2004-2005 Kern Sibbald
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2.1 of the License, or (at your option) any later version.
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the Free
25 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
31 #include "tray-monitor.h"
33 #include "eggstatusicon.h"
35 #include "generic.xpm"
37 #define TRAY_DEBUG_MEMORY 0
39 /* Imported functions */
40 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
41 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
42 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
45 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
47 /* Forward referenced functions */
48 void writecmd(monitoritem* item, const char* command);
49 int docmd(monitoritem* item, const char* command, GSList** list);
50 void getstatus(monitoritem* item, int current, GString** str);
52 /* Static variables */
53 static char *configfile = NULL;
54 static MONITOR *monitor;
56 static int nitems = 0;
57 static int fullitem = 0; //Item to be display in detailled status window
58 static int lastupdated = -1; //Last item updated
59 static monitoritem items[32];
61 /* Data received from DIR/FD/SD */
62 static char OKqstatus[] = "%c000 OK .status\n";
63 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
65 /* UI variables and functions */
68 static gboolean fd_read(gpointer data);
69 static gboolean blink(gpointer data);
72 void trayMessage(const char *fmt,...);
73 void updateStatusIcon(monitoritem* item);
74 void changeStatusMessage(monitoritem* item, const char *fmt,...);
75 static const char** generateXPM(stateenum newstate, stateenum oldstate);
78 static void TrayIconActivate(GtkWidget *widget, gpointer data);
79 static void TrayIconExit(unsigned int activateTime, unsigned int button);
80 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
81 static void MonitorAbout(GtkWidget *widget, gpointer data);
82 static void MonitorRefresh(GtkWidget *widget, gpointer data);
83 static void IntervalChanged(GtkWidget *widget, gpointer data);
84 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
85 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
87 static guint timerTag;
88 static EggStatusIcon *mTrayIcon;
89 static GtkWidget *mTrayMenu;
90 static GtkWidget *window;
91 static GtkWidget *textview;
92 static GtkTextBuffer *buffer;
93 static GtkWidget *timeoutspinner;
94 char** xpm_generic_var;
95 static gboolean blinkstate = TRUE;
97 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
102 "Copyright (C) 2000-2004 Kern Sibbald and John Walker\n"
103 "Written by Nicolas Boichat (2004)\n"
104 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
105 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
106 " -c <file> set configuration file to file\n"
107 " -dnn set debug level to nn\n"
108 " -t test - read configuration and exit\n"
109 " -? print this message.\n"
110 "\n"), HOST_OS, DISTNAME, DISTVER);
113 static GtkWidget *new_image_button(const gchar *stock_id,
114 const gchar *label_text) {
120 button = gtk_button_new();
122 box = gtk_hbox_new(FALSE, 0);
123 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
124 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
125 label = gtk_label_new(label_text);
127 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
128 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
130 gtk_widget_show(image);
131 gtk_widget_show(label);
133 gtk_widget_show(box);
135 gtk_container_add(GTK_CONTAINER(button), box);
142 #if TRAY_DEBUG_MEMORY
143 gpointer smt_malloc(gsize n_bytes) {
144 return sm_malloc("GLib", sm_line, n_bytes);
147 gpointer smt_realloc(gpointer mem, gsize n_bytes) {
148 return sm_realloc("GLib", sm_line, mem, n_bytes);
151 gpointer smt_calloc(gsize n_blocks,
152 gsize n_block_bytes) {
153 return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes);
156 void smt_free(gpointer mem) {
157 sm_free("Glib", sm_line, mem);
161 /*********************************************************************
163 * Main Bacula Tray Monitor -- User Interface Program
166 int main(int argc, char *argv[])
168 #if TRAY_DEBUG_MEMORY
170 smvtable.malloc = &smt_malloc;
171 smvtable.realloc = &smt_realloc;
172 smvtable.free = &smt_free;
173 smvtable.calloc = &smt_calloc;
174 smvtable.try_malloc = NULL;
175 smvtable.try_realloc = NULL;
176 g_mem_set_vtable(&smvtable);
180 bool test_config = false;
186 my_name_is(argc, argv, "tray-monitor");
187 textdomain("bacula");
188 init_msg(NULL, NULL);
189 working_directory = "/tmp";
191 struct sigaction sigignore;
192 sigignore.sa_flags = 0;
193 sigignore.sa_handler = SIG_IGN;
194 sigfillset(&sigignore.sa_mask);
195 sigaction(SIGPIPE, &sigignore, NULL);
197 gtk_init (&argc, &argv);
199 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
201 case 'c': /* configuration file */
202 if (configfile != NULL) {
205 configfile = bstrdup(optarg);
209 debug_level = atoi(optarg);
210 if (debug_level <= 0) {
234 if (configfile == NULL) {
235 configfile = bstrdup(CONFIG_FILE);
238 parse_config(configfile);
242 foreach_res(monitor, R_MONITOR) {
247 Emsg2(M_ERROR_TERM, 0,
248 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
252 foreach_res(dird, R_DIRECTOR) {
253 items[nitems].type = R_DIRECTOR;
254 items[nitems].resource = dird;
255 items[nitems].D_sock = NULL;
256 items[nitems].state = warn;
257 items[nitems].oldstate = warn;
260 foreach_res(filed, R_CLIENT) {
261 items[nitems].type = R_CLIENT;
262 items[nitems].resource = filed;
263 items[nitems].D_sock = NULL;
264 items[nitems].state = warn;
265 items[nitems].oldstate = warn;
268 foreach_res(stored, R_STORAGE) {
269 items[nitems].type = R_STORAGE;
270 items[nitems].resource = stored;
271 items[nitems].D_sock = NULL;
272 items[nitems].state = warn;
273 items[nitems].oldstate = warn;
279 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n"
280 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
287 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
288 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
289 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
290 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
291 strcpy(xpm_generic_var[i], xpm_generic[i]);
294 (void)WSA_Init(); /* Initialize Windows sockets */
297 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
300 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
301 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
302 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
305 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
306 // This should be ideally replaced by a completely libpr0n-based icon rendering.
307 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
308 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
309 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
310 g_object_unref(G_OBJECT(pixbuf));
312 mTrayMenu = gtk_menu_new();
316 entry = gtk_menu_item_new_with_label("Open status window...");
317 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
318 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
320 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
322 entry = gtk_menu_item_new_with_label("Exit");
323 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
324 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
326 gtk_widget_show_all(mTrayMenu);
328 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
330 g_timeout_add( 1000, blink, NULL );
332 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
334 gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
336 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
337 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
339 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
341 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
343 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
345 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
347 GtkWidget* separator = gtk_hseparator_new();
348 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
351 GSList *group = NULL;
355 for (int i = 0; i < nitems; i++) {
356 switch (items[i].type) {
358 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
359 g_string_append(str, _(" (DIR)"));
362 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
363 g_string_append(str, _(" (FD)"));
366 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
367 g_string_append(str, _(" (SD)"));
373 radio = gtk_radio_button_new_with_label(group, str->str);
374 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
375 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
377 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
378 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
380 items[i].label = gtk_label_new(_("Unknown status."));
381 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
382 gtk_container_add(GTK_CONTAINER(align), items[i].label);
384 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
385 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
386 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
387 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
388 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
389 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
391 separator = gtk_hseparator_new();
392 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
394 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
397 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
399 textview = gtk_text_view_new();
401 buffer = gtk_text_buffer_new(NULL);
403 gtk_text_buffer_set_text(buffer, "", -1);
405 PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
406 gtk_widget_modify_font(textview, font_desc);
407 pango_font_description_free(font_desc);
409 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
410 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
412 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
414 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
416 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
418 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
420 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
421 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
422 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
423 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
424 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
425 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
426 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
427 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
429 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
430 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
431 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
433 button = new_image_button("gtk-help", _("About"));
434 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
435 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
437 button = new_image_button("gtk-close", _("Close"));
438 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
439 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
441 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
443 gtk_container_add(GTK_CONTAINER (window), vbox);
445 gtk_widget_show_all(vbox);
449 g_source_remove(timerTag);
453 for (i = 0; i < nitems; i++) {
454 if (items[i].D_sock) {
455 switch (items[i].type) {
457 trayMessage("Disconnecting from Director %s:%d\n", ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport);
460 trayMessage("Disconnecting from Client %s:%d\n", ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport);
463 trayMessage("Disconnecting from Storage %s:%d\n", ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport);
468 //writecmd(&items[i], "quit");
469 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
470 bnet_close(items[i].D_sock);
474 (void)WSACleanup(); /* Cleanup Windows sockets */
476 //Free xpm_generic_var
477 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
478 g_free(xpm_generic_var[i]);
480 g_free(xpm_generic_var);
482 gtk_object_destroy(GTK_OBJECT(window));
483 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
486 #if TRAY_DEBUG_MEMORY
493 static void MonitorAbout(GtkWidget *widget, gpointer data) {
495 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
496 "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
497 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
498 "Written by Nicolas Boichat\n"
499 "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
500 ), HOST_OS, DISTNAME, DISTVER);
502 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
503 "Bacula Tray Monitor\n\n"
504 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
505 "Written by Nicolas Boichat\n"
506 "\nVersion: " VERSION " (" BDATE ") %s %s %s"
507 ), HOST_OS, DISTNAME, DISTVER);
509 gtk_dialog_run(GTK_DIALOG(about));
510 gtk_widget_destroy(about);
513 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
514 for (int i = 0; i < nitems; i++) {
519 static gboolean delete_event( GtkWidget *widget,
522 gtk_widget_hide(window);
523 return TRUE; /* do not destroy the window */
526 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
527 gtk_widget_show(window);
530 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
531 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
532 gtk_widget_show_all(mTrayMenu);
535 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
539 static void IntervalChanged(GtkWidget *widget, gpointer data) {
540 g_source_remove(timerTag);
541 timerTag = g_timeout_add(
543 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
547 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
548 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
550 for (int i = 0; i < nitems; i++) {
551 if (data == &(items[i])) {
556 g_return_if_fail(fullitem != -1);
558 int oldlastupdated = lastupdated;
559 lastupdated = fullitem-1;
561 lastupdated = oldlastupdated;
565 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
566 switch (item->type) {
568 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
571 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
574 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
577 printf("Error, currentitem is not a Client or a Storage..\n");
583 static gboolean blink(gpointer data) {
584 blinkstate = !blinkstate;
585 for (int i = 0; i < nitems; i++) {
586 updateStatusIcon(&items[i]);
588 updateStatusIcon(NULL);
592 static gboolean fd_read(gpointer data) {
594 #if TRAY_DEBUG_MEMORY
595 printf("sm_line=%d\n", sm_line);
597 GtkTextBuffer *newbuffer;
598 GtkTextIter start, stop, nstart, nstop;
602 GString *strlast, *strcurrent;
605 if (lastupdated == nitems) {
609 if (lastupdated == fullitem) {
610 newbuffer = gtk_text_buffer_new(NULL);
612 if (items[lastupdated].type == R_DIRECTOR)
613 docmd(&items[lastupdated], "status Director\n", &list);
615 docmd(&items[lastupdated], "status\n", &list);
619 gtk_text_buffer_get_end_iter(newbuffer, &stop);
620 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
621 if (it->data) g_string_free((GString*)it->data, TRUE);
622 } while ((it = it->next) != NULL);
626 /* Keep the selection if necessary */
627 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
628 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
629 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
632 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
634 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
635 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
639 g_object_unref(buffer);
642 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
645 getstatus(&items[lastupdated], 1, &strcurrent);
646 getstatus(&items[lastupdated], 0, &strlast);
647 updateStatusIcon(&items[lastupdated]);
649 changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
651 updateStatusIcon(NULL);
653 g_string_free(strcurrent, TRUE);
654 g_string_free(strlast, TRUE);
659 void getstatus(monitoritem* item, int current, GString** str) {
661 stateenum ret = error;
662 int jobid = 0, joberrors = 0;
663 char jobstatus = JS_ErrorTerminated;
667 *str = g_string_sized_new(128);
670 if (item->type == R_DIRECTOR)
671 docmd(&items[lastupdated], ".status dir current\n", &list);
673 docmd(&items[lastupdated], ".status current\n", &list);
676 if (item->type == R_DIRECTOR)
677 docmd(&items[lastupdated], ".status dir last\n", &list);
679 docmd(&items[lastupdated], ".status last\n", &list);
683 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
684 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
685 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
686 g_string_set_size(*str, (*str)->len-1);
690 else if ((it = it->next) == NULL) {
692 g_string_append(*str, _("No current job."));
695 g_string_append(*str, _("No last job."));
699 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
702 ret = (joberrors > 0) ? warn : running;
703 g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
706 ret = (joberrors > 0) ? warn : running;
707 g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
710 g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
714 g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
715 ret = (joberrors > 0) ? warn : idle;
717 case JS_ErrorTerminated:
718 g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
722 ret = (joberrors > 0) ? warn : running;
723 g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
726 g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
730 g_string_append_printf(*str, _("Job status: Verify differences (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
734 g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
738 g_string_append_printf(*str, _("Job status: Waiting on File daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
742 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
746 g_string_append_printf(*str, _("Job status: Waiting for new media (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
750 g_string_append_printf(*str, _("Job status: Waiting for Mount (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
753 case JS_WaitStoreRes:
754 g_string_append_printf(*str, _("Job status: Waiting for storage resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
758 g_string_append_printf(*str, _("Job status: Waiting for job resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
761 case JS_WaitClientRes:
762 g_string_append_printf(*str, _("Job status: Waiting for Client resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
766 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
769 case JS_WaitStartTime:
770 g_string_append_printf(*str, _("Job status: Waiting for start time (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
773 case JS_WaitPriority:
774 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
778 g_warning("Unknown job status %c.", jobstatus);
779 g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
785 fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
791 if (it->data) g_string_free((GString*)it->data, TRUE);
792 } while ((it = it->next) != NULL);
800 item->oldstate = ret;
804 int docmd(monitoritem* item, const char* command, GSList** list) {
808 *list = g_slist_alloc();
810 //str = g_string_sized_new(64);
813 memset(&jcr, 0, sizeof(jcr));
819 switch (item->type) {
821 dird = (DIRRES*)item->resource;
822 trayMessage("Connecting to Director %s:%d\n", dird->address, dird->DIRport);
823 changeStatusMessage(item, "Connecting to Director %s:%d", dird->address, dird->DIRport);
824 item->D_sock = bnet_connect(NULL, 0, 0, "Director daemon", dird->address, NULL, dird->DIRport, 0);
825 jcr.dir_bsock = item->D_sock;
828 filed = (CLIENT*)item->resource;
829 trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
830 changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
831 item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
832 jcr.file_bsock = item->D_sock;
835 stored = (STORE*)item->resource;
836 trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
837 changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
838 item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
839 jcr.store_bsock = item->D_sock;
842 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
847 if (item->D_sock == NULL) {
848 g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
849 changeStatusMessage(item, "Cannot connect to daemon.");
851 item->oldstate = error;
855 if (!authenticate_daemon(item, &jcr)) {
856 str = g_string_sized_new(64);
857 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
858 g_slist_append(*list, str);
860 item->oldstate = error;
861 changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
866 switch (item->type) {
868 trayMessage("Opened connection with Director daemon.\n");
869 changeStatusMessage(item, "Opened connection with Director daemon.");
872 trayMessage("Opened connection with File daemon.\n");
873 changeStatusMessage(item, "Opened connection with File daemon.");
876 trayMessage("Opened connection with Storage daemon.\n");
877 changeStatusMessage(item, "Opened connection with Storage daemon.");
880 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
886 if (item->type == R_DIRECTOR) { /* Read connection messages... */
888 docmd(item, "", &list); /* Usually invalid, but no matter */
891 if (it->data) g_string_free((GString*)it->data, TRUE);
892 } while ((it = it->next) != NULL);
899 writecmd(item, command);
902 if ((stat = bnet_recv(item->D_sock)) >= 0) {
903 g_slist_append(*list, g_string_new(item->D_sock->msg));
905 else if (stat == BNET_SIGNAL) {
906 if (item->D_sock->msglen == BNET_EOD) {
907 //fprintf(stderr, "<< EOD >>\n");
910 else if (item->D_sock->msglen == BNET_PROMPT) {
911 //fprintf(stderr, "<< PROMPT >>\n");
912 g_slist_append(*list, g_string_new("<< Error: BNET_PROMPT signal received. >>\n"));
915 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
916 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
917 g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
920 str = g_string_sized_new(64);
921 g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
922 g_slist_append(*list, str);
925 else { /* BNET_HARDEOF || BNET_ERROR */
926 g_slist_append(*list, g_string_new("<ERROR>\n"));
929 item->oldstate = error;
930 changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
931 //fprintf(stderr, "<< ERROR >>\n");
935 if (is_bnet_stop(item->D_sock)) {
936 g_string_append_printf(str, "<STOP>\n");
939 item->oldstate = error;
940 changeStatusMessage(item, "Error : Connection closed.");
941 //fprintf(stderr, "<< STOP >>\n");
942 return 0; /* error or term */
947 void writecmd(monitoritem* item, const char* command) {
949 item->D_sock->msglen = strlen(command);
950 pm_strcpy(&item->D_sock->msg, command);
951 bnet_send(item->D_sock);
955 /* Note: Does not seem to work either on Gnome nor KDE... */
956 void trayMessage(const char *fmt,...) {
960 va_start(arg_ptr, fmt);
961 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
964 fprintf(stderr, buf);
966 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
969 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
973 va_start(arg_ptr, fmt);
974 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
977 gtk_label_set_text(GTK_LABEL(item->label), buf);
980 void updateStatusIcon(monitoritem* item) {
984 /* For the current status, select the two worse for blinking,
985 but never blink a D_Sock == NULL error with idle. */
986 stateenum state1, state2, oldstate;
987 gboolean onenull = FALSE;
991 for (int i = 0; i < nitems; i++) {
992 if (items[i].D_sock == NULL) onenull = TRUE;
993 if (items[i].state >= state1) {
995 state1 = items[i].state;
997 else if (items[i].state > state2) {
998 state2 = items[i].state;
1000 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1003 if ((onenull == TRUE) && (state2 == idle)) {
1007 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1010 if ((blinkstate) && (item->D_sock != NULL)) {
1011 if (item->state > 1) { //Warning or error while running
1012 xpm = generateXPM(running, item->oldstate);
1015 xpm = generateXPM(idle, item->oldstate);
1019 xpm = generateXPM(item->state, item->oldstate);
1023 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1025 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1026 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1029 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1031 g_object_unref(G_OBJECT(pixbuf));
1034 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1035 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1036 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1039 strcpy(address, "ff0000");
1042 strcpy(address, "ffffff");
1045 strcpy(address, "00ff00");
1048 strcpy(address, "ffff00");
1052 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1055 strcpy(address, "ff0000");
1058 strcpy(address, "ffffff");
1061 strcpy(address, "00ff00");
1064 strcpy(address, "ffff00");
1068 return (const char**)xpm_generic_var;