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 writecmd(&items[i], "quit");
456 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
457 bnet_close(items[i].D_sock);
461 (void)WSACleanup(); /* Cleanup Windows sockets */
463 //Free xpm_generic_var
464 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
465 g_free(xpm_generic_var[i]);
467 g_free(xpm_generic_var);
469 gtk_object_destroy(GTK_OBJECT(window));
470 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
473 #if TRAY_DEBUG_MEMORY
480 static void MonitorAbout(GtkWidget *widget, gpointer data) {
482 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
483 "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
484 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
485 "Written by Nicolas Boichat\n"
486 "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
487 ), HOST_OS, DISTNAME, DISTVER);
489 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
490 "Bacula Tray Monitor\n\n"
491 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
492 "Written by Nicolas Boichat\n"
493 "\nVersion: " VERSION " (" BDATE ") %s %s %s"
494 ), HOST_OS, DISTNAME, DISTVER);
496 gtk_dialog_run(GTK_DIALOG(about));
497 gtk_widget_destroy(about);
500 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
501 for (int i = 0; i < nitems; i++) {
506 static gboolean delete_event( GtkWidget *widget,
509 gtk_widget_hide(window);
510 return TRUE; /* do not destroy the window */
513 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
514 gtk_widget_show(window);
517 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
518 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
519 gtk_widget_show_all(mTrayMenu);
522 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
526 static void IntervalChanged(GtkWidget *widget, gpointer data) {
527 g_source_remove(timerTag);
528 timerTag = g_timeout_add(
530 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
534 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
535 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
537 for (int i = 0; i < nitems; i++) {
538 if (data == &(items[i])) {
543 g_return_if_fail(fullitem != -1);
545 int oldlastupdated = lastupdated;
546 lastupdated = fullitem-1;
548 lastupdated = oldlastupdated;
552 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
553 switch (item->type) {
555 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
558 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
561 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
564 printf("Error, currentitem is not a Client or a Storage..\n");
570 static gboolean blink(gpointer data) {
571 blinkstate = !blinkstate;
572 for (int i = 0; i < nitems; i++) {
573 updateStatusIcon(&items[i]);
575 updateStatusIcon(NULL);
579 static gboolean fd_read(gpointer data) {
581 #if TRAY_DEBUG_MEMORY
582 printf("sm_line=%d\n", sm_line);
584 GtkTextBuffer *newbuffer;
585 GtkTextIter start, stop, nstart, nstop;
589 GString *strlast, *strcurrent;
592 if (lastupdated == nitems) {
596 if (lastupdated == fullitem) {
597 newbuffer = gtk_text_buffer_new(NULL);
599 if (items[lastupdated].type == R_DIRECTOR)
600 docmd(&items[lastupdated], "status Director\n", &list);
602 docmd(&items[lastupdated], "status\n", &list);
606 gtk_text_buffer_get_end_iter(newbuffer, &stop);
607 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
608 if (it->data) g_string_free((GString*)it->data, TRUE);
609 } while ((it = it->next) != NULL);
613 /* Keep the selection if necessary */
614 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
615 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
616 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
619 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
621 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
622 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
626 g_object_unref(buffer);
629 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
632 getstatus(&items[lastupdated], 1, &strcurrent);
633 getstatus(&items[lastupdated], 0, &strlast);
634 updateStatusIcon(&items[lastupdated]);
636 changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
638 updateStatusIcon(NULL);
640 g_string_free(strcurrent, TRUE);
641 g_string_free(strlast, TRUE);
646 void getstatus(monitoritem* item, int current, GString** str) {
648 stateenum ret = error;
649 int jobid = 0, joberrors = 0;
650 char jobstatus = JS_ErrorTerminated;
654 *str = g_string_sized_new(128);
657 if (item->type == R_DIRECTOR)
658 docmd(&items[lastupdated], ".status dir current\n", &list);
660 docmd(&items[lastupdated], ".status current\n", &list);
663 if (item->type == R_DIRECTOR)
664 docmd(&items[lastupdated], ".status dir last\n", &list);
666 docmd(&items[lastupdated], ".status last\n", &list);
670 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
671 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
672 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
673 g_string_set_size(*str, (*str)->len-1);
677 else if ((it = it->next) == NULL) {
679 g_string_append(*str, _("No current job."));
682 g_string_append(*str, _("No last job."));
686 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
689 ret = (joberrors > 0) ? warn : running;
690 g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
693 ret = (joberrors > 0) ? warn : running;
694 g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
697 g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
701 g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
702 ret = (joberrors > 0) ? warn : idle;
704 case JS_ErrorTerminated:
705 g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
709 ret = (joberrors > 0) ? warn : running;
710 g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
713 g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
717 g_string_append_printf(*str, _("Job status: Verify differences (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
721 g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
725 g_string_append_printf(*str, _("Job status: Waiting on File daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
729 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
733 g_string_append_printf(*str, _("Job status: Waiting for new media (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
737 g_string_append_printf(*str, _("Job status: Waiting for Mount (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
740 case JS_WaitStoreRes:
741 g_string_append_printf(*str, _("Job status: Waiting for storage resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
745 g_string_append_printf(*str, _("Job status: Waiting for job resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
748 case JS_WaitClientRes:
749 g_string_append_printf(*str, _("Job status: Waiting for Client resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
753 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
756 case JS_WaitStartTime:
757 g_string_append_printf(*str, _("Job status: Waiting for start time (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
760 case JS_WaitPriority:
761 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
765 g_warning("Unknown job status %c.", jobstatus);
766 g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
772 fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
778 if (it->data) g_string_free((GString*)it->data, TRUE);
779 } while ((it = it->next) != NULL);
787 item->oldstate = ret;
791 int docmd(monitoritem* item, const char* command, GSList** list) {
795 *list = g_slist_alloc();
797 //str = g_string_sized_new(64);
800 memset(&jcr, 0, sizeof(jcr));
806 switch (item->type) {
808 dird = (DIRRES*)item->resource;
809 trayMessage("Connecting to Director %s:%d\n", dird->address, dird->DIRport);
810 changeStatusMessage(item, "Connecting to Director %s:%d", dird->address, dird->DIRport);
811 item->D_sock = bnet_connect(NULL, 0, 0, "Director daemon", dird->address, NULL, dird->DIRport, 0);
812 jcr.dir_bsock = item->D_sock;
815 filed = (CLIENT*)item->resource;
816 trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
817 changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
818 item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
819 jcr.file_bsock = item->D_sock;
822 stored = (STORE*)item->resource;
823 trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
824 changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
825 item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
826 jcr.store_bsock = item->D_sock;
829 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
834 if (item->D_sock == NULL) {
835 g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
836 changeStatusMessage(item, "Cannot connect to daemon.");
838 item->oldstate = error;
842 if (!authenticate_daemon(item, &jcr)) {
843 str = g_string_sized_new(64);
844 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
845 g_slist_append(*list, str);
847 item->oldstate = error;
848 changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
853 switch (item->type) {
855 trayMessage("Opened connection with Director daemon.\n");
856 changeStatusMessage(item, "Opened connection with Director daemon.");
859 trayMessage("Opened connection with File daemon.\n");
860 changeStatusMessage(item, "Opened connection with File daemon.");
863 trayMessage("Opened connection with Storage daemon.\n");
864 changeStatusMessage(item, "Opened connection with Storage daemon.");
867 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
873 if (item->type == R_DIRECTOR) { /* Read connection messages... */
875 docmd(item, "", &list); /* Usually invalid, but no matter */
878 if (it->data) g_string_free((GString*)it->data, TRUE);
879 } while ((it = it->next) != NULL);
886 writecmd(item, command);
889 if ((stat = bnet_recv(item->D_sock)) >= 0) {
890 g_slist_append(*list, g_string_new(item->D_sock->msg));
892 else if (stat == BNET_SIGNAL) {
893 if (item->D_sock->msglen == BNET_EOD) {
894 //fprintf(stderr, "<< EOD >>\n");
897 else if (item->D_sock->msglen == BNET_PROMPT) {
898 //fprintf(stderr, "<< PROMPT >>\n");
899 g_slist_append(*list, g_string_new("<< Error: BNET_PROMPT signal received. >>\n"));
902 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
903 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
904 g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
907 str = g_string_sized_new(64);
908 g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
909 g_slist_append(*list, str);
912 else { /* BNET_HARDEOF || BNET_ERROR */
913 g_slist_append(*list, g_string_new("<ERROR>\n"));
916 item->oldstate = error;
917 changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
918 //fprintf(stderr, "<< ERROR >>\n");
922 if (is_bnet_stop(item->D_sock)) {
923 g_string_append_printf(str, "<STOP>\n");
926 item->oldstate = error;
927 changeStatusMessage(item, "Error : Connection closed.");
928 //fprintf(stderr, "<< STOP >>\n");
929 return 0; /* error or term */
934 void writecmd(monitoritem* item, const char* command) {
936 item->D_sock->msglen = strlen(command);
937 pm_strcpy(&item->D_sock->msg, command);
938 bnet_send(item->D_sock);
942 /* Note: Does not seem to work either on Gnome nor KDE... */
943 void trayMessage(const char *fmt,...) {
947 va_start(arg_ptr, fmt);
948 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
951 fprintf(stderr, buf);
953 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
956 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
960 va_start(arg_ptr, fmt);
961 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
964 gtk_label_set_text(GTK_LABEL(item->label), buf);
967 void updateStatusIcon(monitoritem* item) {
971 /* For the current status, select the two worse for blinking,
972 but never blink a D_Sock == NULL error with idle. */
973 stateenum state1, state2, oldstate;
974 gboolean onenull = FALSE;
978 for (int i = 0; i < nitems; i++) {
979 if (items[i].D_sock == NULL) onenull = TRUE;
980 if (items[i].state >= state1) {
982 state1 = items[i].state;
984 else if (items[i].state > state2) {
985 state2 = items[i].state;
987 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
990 if ((onenull == TRUE) && (state2 == idle)) {
994 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
997 if ((blinkstate) && (item->D_sock != NULL)) {
998 if (item->state > 1) { //Warning or error while running
999 xpm = generateXPM(running, item->oldstate);
1002 xpm = generateXPM(idle, item->oldstate);
1006 xpm = generateXPM(item->state, item->oldstate);
1010 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1012 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1013 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1016 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1018 g_object_unref(G_OBJECT(pixbuf));
1021 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1022 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1023 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1026 strcpy(address, "ff0000");
1029 strcpy(address, "ffffff");
1032 strcpy(address, "00ff00");
1035 strcpy(address, "ffff00");
1039 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1042 strcpy(address, "ff0000");
1045 strcpy(address, "ffffff");
1048 strcpy(address, "00ff00");
1051 strcpy(address, "ffff00");
1055 return (const char**)xpm_generic_var;