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);
570 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
572 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
574 printf("Error, currentitem is not a Client or a Storage..\n");
581 static gboolean blink(gpointer data) {
582 blinkstate = !blinkstate;
583 for (int i = 0; i < nitems; i++) {
584 updateStatusIcon(&items[i]);
586 updateStatusIcon(NULL);
590 static gboolean fd_read(gpointer data) {
592 #if TRAY_DEBUG_MEMORY
593 printf("sm_line=%d\n", sm_line);
595 GtkTextBuffer *newbuffer;
596 GtkTextIter start, stop, nstart, nstop;
600 GString *strlast, *strcurrent;
603 if (lastupdated == nitems) {
607 if (lastupdated == fullitem) {
608 newbuffer = gtk_text_buffer_new(NULL);
610 if (items[lastupdated].type == R_DIRECTOR)
611 docmd(&items[lastupdated], "status Director\n", &list);
613 docmd(&items[lastupdated], "status\n", &list);
617 gtk_text_buffer_get_end_iter(newbuffer, &stop);
618 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
619 if (it->data) g_string_free((GString*)it->data, TRUE);
620 } while ((it = it->next) != NULL);
624 /* Keep the selection if necessary */
625 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
626 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
627 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
630 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
632 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
633 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
637 g_object_unref(buffer);
640 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
643 getstatus(&items[lastupdated], 1, &strcurrent);
644 getstatus(&items[lastupdated], 0, &strlast);
645 updateStatusIcon(&items[lastupdated]);
647 changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
649 updateStatusIcon(NULL);
651 g_string_free(strcurrent, TRUE);
652 g_string_free(strlast, TRUE);
657 void getstatus(monitoritem* item, int current, GString** str) {
659 stateenum ret = error;
660 int jobid = 0, joberrors = 0;
661 char jobstatus = JS_ErrorTerminated;
665 *str = g_string_sized_new(128);
668 if (item->type == R_DIRECTOR)
669 docmd(&items[lastupdated], ".status dir current\n", &list);
671 docmd(&items[lastupdated], ".status current\n", &list);
674 if (item->type == R_DIRECTOR)
675 docmd(&items[lastupdated], ".status dir last\n", &list);
677 docmd(&items[lastupdated], ".status last\n", &list);
681 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
682 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
683 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
684 g_string_set_size(*str, (*str)->len-1);
688 else if ((it = it->next) == NULL) {
690 g_string_append(*str, _("No current job."));
693 g_string_append(*str, _("No last job."));
697 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
700 ret = (joberrors > 0) ? warn : running;
701 g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
704 ret = (joberrors > 0) ? warn : running;
705 g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
708 g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
712 g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
713 ret = (joberrors > 0) ? warn : idle;
715 case JS_ErrorTerminated:
716 g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
720 ret = (joberrors > 0) ? warn : running;
721 g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
724 g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
728 g_string_append_printf(*str, _("Job status: Verify differences (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
732 g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
736 g_string_append_printf(*str, _("Job status: Waiting on File daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
740 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
744 g_string_append_printf(*str, _("Job status: Waiting for new media (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
748 g_string_append_printf(*str, _("Job status: Waiting for Mount (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
751 case JS_WaitStoreRes:
752 g_string_append_printf(*str, _("Job status: Waiting for storage resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
756 g_string_append_printf(*str, _("Job status: Waiting for job resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
759 case JS_WaitClientRes:
760 g_string_append_printf(*str, _("Job status: Waiting for Client resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
764 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
767 case JS_WaitStartTime:
768 g_string_append_printf(*str, _("Job status: Waiting for start time (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
771 case JS_WaitPriority:
772 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
776 g_warning("Unknown job status %c.", jobstatus);
777 g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
783 fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
789 if (it->data) g_string_free((GString*)it->data, TRUE);
790 } while ((it = it->next) != NULL);
798 item->oldstate = ret;
802 int docmd(monitoritem* item, const char* command, GSList** list) {
806 *list = g_slist_alloc();
808 //str = g_string_sized_new(64);
811 memset(&jcr, 0, sizeof(jcr));
817 switch (item->type) {
819 dird = (DIRRES*)item->resource;
820 trayMessage("Connecting to Director %s:%d\n", dird->address, dird->DIRport);
821 changeStatusMessage(item, "Connecting to Director %s:%d", dird->address, dird->DIRport);
822 item->D_sock = bnet_connect(NULL, 0, 0, "Director daemon", dird->address, NULL, dird->DIRport, 0);
823 jcr.dir_bsock = item->D_sock;
826 filed = (CLIENT*)item->resource;
827 trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
828 changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
829 item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
830 jcr.file_bsock = item->D_sock;
833 stored = (STORE*)item->resource;
834 trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
835 changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
836 item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
837 jcr.store_bsock = item->D_sock;
840 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
845 if (item->D_sock == NULL) {
846 g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
847 changeStatusMessage(item, "Cannot connect to daemon.");
849 item->oldstate = error;
853 if (!authenticate_daemon(item, &jcr)) {
854 str = g_string_sized_new(64);
855 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
856 g_slist_append(*list, str);
858 item->oldstate = error;
859 changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
864 switch (item->type) {
866 trayMessage("Opened connection with Director daemon.\n");
867 changeStatusMessage(item, "Opened connection with Director daemon.");
870 trayMessage("Opened connection with File daemon.\n");
871 changeStatusMessage(item, "Opened connection with File daemon.");
874 trayMessage("Opened connection with Storage daemon.\n");
875 changeStatusMessage(item, "Opened connection with Storage daemon.");
878 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
884 if (item->type == R_DIRECTOR) { /* Read connection messages... */
886 docmd(item, "", &list); /* Usually invalid, but no matter */
889 if (it->data) g_string_free((GString*)it->data, TRUE);
890 } while ((it = it->next) != NULL);
897 writecmd(item, command);
900 if ((stat = bnet_recv(item->D_sock)) >= 0) {
901 g_slist_append(*list, g_string_new(item->D_sock->msg));
903 else if (stat == BNET_SIGNAL) {
904 if (item->D_sock->msglen == BNET_EOD) {
905 //fprintf(stderr, "<< EOD >>\n");
908 else if (item->D_sock->msglen == BNET_PROMPT) {
909 //fprintf(stderr, "<< PROMPT >>\n");
910 g_slist_append(*list, g_string_new("<< Error: BNET_PROMPT signal received. >>\n"));
913 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
914 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
915 g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
918 str = g_string_sized_new(64);
919 g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
920 g_slist_append(*list, str);
923 else { /* BNET_HARDEOF || BNET_ERROR */
924 g_slist_append(*list, g_string_new("<ERROR>\n"));
927 item->oldstate = error;
928 changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
929 //fprintf(stderr, "<< ERROR >>\n");
933 if (is_bnet_stop(item->D_sock)) {
934 g_string_append_printf(str, "<STOP>\n");
937 item->oldstate = error;
938 changeStatusMessage(item, "Error : Connection closed.");
939 //fprintf(stderr, "<< STOP >>\n");
940 return 0; /* error or term */
945 void writecmd(monitoritem* item, const char* command) {
947 item->D_sock->msglen = strlen(command);
948 pm_strcpy(&item->D_sock->msg, command);
949 bnet_send(item->D_sock);
953 /* Note: Does not seem to work either on Gnome nor KDE... */
954 void trayMessage(const char *fmt,...) {
958 va_start(arg_ptr, fmt);
959 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
962 fprintf(stderr, buf);
964 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
967 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
971 va_start(arg_ptr, fmt);
972 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
975 gtk_label_set_text(GTK_LABEL(item->label), buf);
978 void updateStatusIcon(monitoritem* item) {
982 /* For the current status, select the two worse for blinking,
983 but never blink a D_Sock == NULL error with idle. */
984 stateenum state1, state2, oldstate;
985 gboolean onenull = FALSE;
989 for (int i = 0; i < nitems; i++) {
990 if (items[i].D_sock == NULL) onenull = TRUE;
991 if (items[i].state >= state1) {
993 state1 = items[i].state;
995 else if (items[i].state > state2) {
996 state2 = items[i].state;
998 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1001 if ((onenull == TRUE) && (state2 == idle)) {
1005 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1008 if ((blinkstate) && (item->D_sock != NULL)) {
1009 if (item->state > 1) { //Warning or error while running
1010 xpm = generateXPM(running, item->oldstate);
1013 xpm = generateXPM(idle, item->oldstate);
1017 xpm = generateXPM(item->state, item->oldstate);
1021 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1023 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1024 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1027 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1029 g_object_unref(G_OBJECT(pixbuf));
1032 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1033 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1034 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1037 strcpy(address, "ff0000");
1040 strcpy(address, "ffffff");
1043 strcpy(address, "00ff00");
1046 strcpy(address, "ffff00");
1050 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1053 strcpy(address, "ff0000");
1056 strcpy(address, "ffffff");
1059 strcpy(address, "00ff00");
1062 strcpy(address, "ffff00");
1066 return (const char**)xpm_generic_var;