3 * Bacula Gnome Tray Monitor
5 * Nicolas Boichat, August MMIV
11 Copyright (C) 2004 Kern Sibbald and John Walker
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 /* Imported functions */
38 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
39 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
40 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
42 /* Forward referenced functions */
43 void writecmd(monitoritem* item, const char* command);
44 int docmd(monitoritem* item, const char* command, GSList** list);
45 void getstatus(monitoritem* item, int current, GString** str);
47 /* Static variables */
48 static char *configfile = NULL;
49 static MONITOR *monitor;
52 static int nitems = 0;
53 static int fullitem = 0; //Item to be display in detailled status window
54 static int lastupdated = -1; //Last item updated
55 static monitoritem items[32];
57 /* Data received from DIR/FD/SD */
58 static char OKqstatus[] = "%c000 OK .status\n";
59 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
61 /* UI variables and functions */
63 static gboolean fd_read(gpointer data);
64 static gboolean blink(gpointer data);
65 void trayMessage(const char *fmt,...);
66 void updateStatusIcon(monitoritem* item);
67 void changeStatusMessage(monitoritem* item, const char *fmt,...);
68 static const char** generateXPM(stateenum newstate, stateenum oldstate);
71 static void TrayIconActivate(GtkWidget *widget, gpointer data);
72 static void TrayIconExit(unsigned int activateTime, unsigned int button);
73 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
74 static void MonitorAbout(GtkWidget *widget, gpointer data);
75 static void MonitorRefresh(GtkWidget *widget, gpointer data);
76 static void IntervalChanged(GtkWidget *widget, gpointer data);
77 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
78 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
80 static guint timerTag;
81 static EggStatusIcon *mTrayIcon;
82 static GtkWidget *mTrayMenu;
83 static GtkWidget *window;
84 static GtkWidget *textview;
85 static GtkTextBuffer *buffer;
86 static GtkWidget *timeoutspinner;
87 char** xpm_generic_var;
88 static gboolean blinkstate = TRUE;
90 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
95 "Copyright (C) 2000-2004 Kern Sibbald and John Walker\n"
96 "Written by Nicolas Boichat (2004)\n"
97 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
98 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
99 " -c <file> set configuration file to file\n"
100 " -dnn set debug level to nn\n"
101 " -t test - read configuration and exit\n"
102 " -? print this message.\n"
103 "\n"), HOST_OS, DISTNAME, DISTVER);
106 static GtkWidget *new_image_button(const gchar *stock_id,
107 const gchar *label_text) {
113 button = gtk_button_new();
115 box = gtk_hbox_new(FALSE, 0);
116 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
117 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
118 label = gtk_label_new(label_text);
120 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
121 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
123 gtk_widget_show(image);
124 gtk_widget_show(label);
126 gtk_widget_show(box);
128 gtk_container_add(GTK_CONTAINER(button), box);
133 /*********************************************************************
135 * Main Bacula Tray Monitor -- User Interface Program
138 int main(int argc, char *argv[])
141 bool test_config = false;
147 my_name_is(argc, argv, "tray-monitor");
148 textdomain("bacula");
149 init_msg(NULL, NULL);
150 working_directory = "/tmp";
151 args = get_pool_memory(PM_FNAME);
153 struct sigaction sigignore;
154 sigignore.sa_flags = 0;
155 sigignore.sa_handler = SIG_IGN;
156 sigfillset(&sigignore.sa_mask);
157 sigaction(SIGPIPE, &sigignore, NULL);
159 gtk_init (&argc, &argv);
161 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
163 case 'c': /* configuration file */
164 if (configfile != NULL) {
167 configfile = bstrdup(optarg);
171 debug_level = atoi(optarg);
172 if (debug_level <= 0) {
196 if (configfile == NULL) {
197 configfile = bstrdup(CONFIG_FILE);
200 parse_config(configfile);
204 foreach_res(monitor, R_MONITOR) {
209 Emsg2(M_ERROR_TERM, 0,
210 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
214 foreach_res(dird, R_DIRECTOR) {
215 items[nitems].type = R_DIRECTOR;
216 items[nitems].resource = dird;
217 items[nitems].D_sock = NULL;
218 items[nitems].state = warn;
219 items[nitems].oldstate = warn;
222 foreach_res(filed, R_CLIENT) {
223 items[nitems].type = R_CLIENT;
224 items[nitems].resource = filed;
225 items[nitems].D_sock = NULL;
226 items[nitems].state = warn;
227 items[nitems].oldstate = warn;
230 foreach_res(stored, R_STORAGE) {
231 items[nitems].type = R_STORAGE;
232 items[nitems].resource = stored;
233 items[nitems].D_sock = NULL;
234 items[nitems].state = warn;
235 items[nitems].oldstate = warn;
241 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n\
242 Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
249 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
250 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
251 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
252 g_assert((xpm_generic_var[i] = (char*)g_malloc(strlen(xpm_generic[i])*sizeof(char))));
253 strcpy(xpm_generic_var[i], xpm_generic[i]);
256 (void)WSA_Init(); /* Initialize Windows sockets */
259 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
262 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
263 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n\
264 This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
267 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
268 // This should be ideally replaced by a completely libpr0n-based icon rendering.
269 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
270 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
271 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
272 g_object_unref(G_OBJECT(pixbuf));
274 mTrayMenu = gtk_menu_new();
278 entry = gtk_menu_item_new_with_label("Open status window...");
279 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
280 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
282 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
284 entry = gtk_menu_item_new_with_label("Exit");
285 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
286 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
288 gtk_widget_show_all(mTrayMenu);
290 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
292 g_timeout_add( 1000, blink, NULL );
294 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
296 gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
298 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
299 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
301 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
303 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
305 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
307 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
309 GtkWidget* separator = gtk_hseparator_new();
310 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
313 GSList *group = NULL;
317 for (int i = 0; i < nitems; i++) {
318 switch (items[i].type) {
320 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
321 g_string_append(str, _(" (DIR)"));
324 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
325 g_string_append(str, _(" (FD)"));
328 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
329 g_string_append(str, _(" (SD)"));
335 radio = gtk_radio_button_new_with_label(group, str->str);
336 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
337 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
339 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
340 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
342 items[i].label = gtk_label_new(_("Unknown status."));
343 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
344 gtk_container_add(GTK_CONTAINER(align), items[i].label);
346 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
347 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
348 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
349 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
350 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
351 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
353 separator = gtk_hseparator_new();
354 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
356 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
359 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
361 textview = gtk_text_view_new();
363 buffer = gtk_text_buffer_new(NULL);
365 gtk_text_buffer_set_text(buffer, "", -1);
367 PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
368 gtk_widget_modify_font(textview, font_desc);
369 pango_font_description_free(font_desc);
371 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
372 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
374 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
376 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
378 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
380 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
382 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
383 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
384 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
385 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
386 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
387 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
388 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
389 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
391 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
392 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
393 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
395 button = new_image_button("gtk-help", _("About"));
396 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
397 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
399 button = new_image_button("gtk-close", _("Close"));
400 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
401 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
403 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
405 gtk_container_add(GTK_CONTAINER (window), vbox);
407 gtk_widget_show_all(vbox);
411 for (i = 0; i < nitems; i++) {
412 if (items[i].D_sock) {
413 writecmd(&items[i], "quit");
414 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
415 bnet_close(items[i].D_sock);
419 free_pool_memory(args);
420 (void)WSACleanup(); /* Cleanup Windows sockets */
422 //Free xpm_generic_var
423 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
424 g_free(xpm_generic_var[i]);
426 g_free(xpm_generic_var);
431 static void MonitorAbout(GtkWidget *widget, gpointer data) {
433 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
434 "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
435 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
436 "Written by Nicolas Boichat\n"
437 "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
438 ), HOST_OS, DISTNAME, DISTVER);
440 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
441 "Bacula Tray Monitor\n\n"
442 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
443 "Written by Nicolas Boichat\n"
444 "\nVersion: " VERSION " (" BDATE ") %s %s %s"
445 ), HOST_OS, DISTNAME, DISTVER);
447 gtk_dialog_run(GTK_DIALOG(about));
448 gtk_widget_destroy(about);
451 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
452 for (int i = 0; i < nitems; i++) {
457 static gboolean delete_event( GtkWidget *widget,
460 gtk_widget_hide(window);
461 return TRUE; /* do not destroy the window */
464 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
465 gtk_widget_show(window);
468 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
469 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
470 gtk_widget_show_all(mTrayMenu);
473 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
477 static void IntervalChanged(GtkWidget *widget, gpointer data) {
478 g_source_remove(timerTag);
479 timerTag = g_timeout_add(
481 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
485 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
486 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
488 for (int i = 0; i < nitems; i++) {
489 if (data == &(items[i])) {
494 g_return_if_fail(fullitem != -1);
496 int oldlastupdated = lastupdated;
497 lastupdated = fullitem-1;
499 lastupdated = oldlastupdated;
503 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
504 switch (item->type) {
506 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
509 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
512 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
515 printf("Error, currentitem is not a Client or a Storage..\n");
521 static gboolean blink(gpointer data) {
522 blinkstate = !blinkstate;
523 for (int i = 0; i < nitems; i++) {
524 updateStatusIcon(&items[i]);
526 updateStatusIcon(NULL);
530 static gboolean fd_read(gpointer data) {
531 GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
532 GtkTextIter start, stop, nstart, nstop;
536 GString *strlast, *strcurrent;
539 if (lastupdated == nitems) {
543 if (lastupdated == fullitem) {
544 if (items[lastupdated].type == R_DIRECTOR)
545 docmd(&items[lastupdated], "status Director\n", &list);
547 docmd(&items[lastupdated], "status\n", &list);
551 gtk_text_buffer_get_end_iter(newbuffer, &stop);
552 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
553 if (it->data) g_string_free((GString*)it->data, TRUE);
554 } while ((it = it->next) != NULL);
556 /* Keep the selection if necessary */
557 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
558 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
559 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
562 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
564 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
565 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
569 g_object_unref(buffer);
572 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
575 getstatus(&items[lastupdated], 1, &strcurrent);
576 getstatus(&items[lastupdated], 0, &strlast);
577 updateStatusIcon(&items[lastupdated]);
579 changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
581 updateStatusIcon(NULL);
583 g_string_free(strcurrent, TRUE);
584 g_string_free(strlast, TRUE);
589 void getstatus(monitoritem* item, int current, GString** str) {
591 stateenum ret = error;
592 int jobid = 0, joberrors = 0;
593 char jobstatus = JS_ErrorTerminated;
597 *str = g_string_sized_new(128);
600 if (item->type == R_DIRECTOR)
601 docmd(&items[lastupdated], ".status dir current\n", &list);
603 docmd(&items[lastupdated], ".status current\n", &list);
606 if (item->type == R_DIRECTOR)
607 docmd(&items[lastupdated], ".status dir last\n", &list);
609 docmd(&items[lastupdated], ".status last\n", &list);
613 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
614 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
615 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
616 g_string_set_size(*str, (*str)->len-1);
620 else if ((it = it->next) == NULL) {
622 g_string_append(*str, _("No current job."));
625 g_string_append(*str, _("No last job."));
629 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
632 ret = (joberrors > 0) ? warn : running;
633 g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
636 ret = (joberrors > 0) ? warn : running;
637 g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
640 g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
644 g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
645 ret = (joberrors > 0) ? warn : idle;
647 case JS_ErrorTerminated:
648 g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
652 ret = (joberrors > 0) ? warn : running;
653 g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
656 g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
660 g_string_append_printf(*str, _("Job status: Verify differences (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
664 g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
668 g_string_append_printf(*str, _("Job status: Waiting on File daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
672 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
676 g_string_append_printf(*str, _("Job status: Waiting for new media (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
680 g_string_append_printf(*str, _("Job status: Waiting for Mount (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
683 case JS_WaitStoreRes:
684 g_string_append_printf(*str, _("Job status: Waiting for storage resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
688 g_string_append_printf(*str, _("Job status: Waiting for job resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
691 case JS_WaitClientRes:
692 g_string_append_printf(*str, _("Job status: Waiting for Client resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
696 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
699 case JS_WaitStartTime:
700 g_string_append_printf(*str, _("Job status: Waiting for start time (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
703 case JS_WaitPriority:
704 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
708 g_warning("Unknown job status %c.", jobstatus);
709 g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
715 fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
721 if (it->data) g_string_free((GString*)it->data, TRUE);
722 } while ((it = it->next) != NULL);
730 item->oldstate = ret;
734 int docmd(monitoritem* item, const char* command, GSList** list) {
738 *list = g_slist_alloc();
740 //str = g_string_sized_new(64);
743 memset(&jcr, 0, sizeof(jcr));
749 switch (item->type) {
751 dird = (DIRRES*)item->resource;
752 trayMessage("Connecting to Director %s:%d\n", dird->address, dird->DIRport);
753 changeStatusMessage(item, "Connecting to Director %s:%d", dird->address, dird->DIRport);
754 item->D_sock = bnet_connect(NULL, 0, 0, "Director daemon", dird->address, NULL, dird->DIRport, 0);
755 jcr.dir_bsock = item->D_sock;
758 filed = (CLIENT*)item->resource;
759 trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
760 changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
761 item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
762 jcr.file_bsock = item->D_sock;
765 stored = (STORE*)item->resource;
766 trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
767 changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
768 item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
769 jcr.store_bsock = item->D_sock;
772 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
777 if (item->D_sock == NULL) {
778 g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
779 changeStatusMessage(item, "Cannot connect to daemon.");
781 item->oldstate = error;
785 if (!authenticate_daemon(item, &jcr)) {
786 str = g_string_sized_new(64);
787 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
788 g_slist_append(*list, str);
790 item->oldstate = error;
791 changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
796 switch (item->type) {
798 trayMessage("Opened connection with Director daemon.\n");
799 changeStatusMessage(item, "Opened connection with Director daemon.");
802 trayMessage("Opened connection with File daemon.\n");
803 changeStatusMessage(item, "Opened connection with File daemon.");
806 trayMessage("Opened connection with Storage daemon.\n");
807 changeStatusMessage(item, "Opened connection with Storage daemon.");
810 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
816 if (item->type == R_DIRECTOR) { /* Read connection messages... */
818 docmd(item, "", &list); /* Usually invalid, but no matter */
821 if (it->data) g_string_free((GString*)it->data, TRUE);
822 } while ((it = it->next) != NULL);
829 writecmd(item, command);
832 if ((stat = bnet_recv(item->D_sock)) >= 0) {
833 g_slist_append(*list, g_string_new(item->D_sock->msg));
835 else if (stat == BNET_SIGNAL) {
836 if (item->D_sock->msglen == BNET_EOD) {
837 //fprintf(stderr, "<< EOD >>\n");
840 else if (item->D_sock->msglen == BNET_PROMPT) {
841 //fprintf(stderr, "<< PROMPT >>\n");
842 g_slist_append(*list, g_string_new("<< Error: BNET_PROMPT signal received. >>\n"));
845 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
846 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
847 g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
850 str = g_string_sized_new(64);
851 g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
852 g_slist_append(*list, str);
855 else { /* BNET_HARDEOF || BNET_ERROR */
856 g_slist_append(*list, g_string_new("<ERROR>\n"));
859 item->oldstate = error;
860 changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
861 //fprintf(stderr, "<< ERROR >>\n");
865 if (is_bnet_stop(item->D_sock)) {
866 g_string_append_printf(str, "<STOP>\n");
869 item->oldstate = error;
870 changeStatusMessage(item, "Error : Connection closed.");
871 //fprintf(stderr, "<< STOP >>\n");
872 return 0; /* error or term */
877 void writecmd(monitoritem* item, const char* command) {
879 item->D_sock->msglen = strlen(command);
880 pm_strcpy(&item->D_sock->msg, command);
881 bnet_send(item->D_sock);
885 /* Note: Does not seem to work either on Gnome nor KDE... */
886 void trayMessage(const char *fmt,...) {
890 va_start(arg_ptr, fmt);
891 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
894 fprintf(stderr, buf);
896 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
899 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
903 va_start(arg_ptr, fmt);
904 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
907 gtk_label_set_text(GTK_LABEL(item->label), buf);
910 void updateStatusIcon(monitoritem* item) {
914 /* For the current status, select the two worse for blinking,
915 but never blink a D_Sock == NULL error with idle. */
916 stateenum state1, state2, oldstate;
917 gboolean onenull = FALSE;
921 for (int i = 0; i < nitems; i++) {
922 if (items[i].D_sock == NULL) onenull = TRUE;
923 if (items[i].state >= state1) {
925 state1 = items[i].state;
927 else if (items[i].state > state2) {
928 state2 = items[i].state;
930 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
933 if ((onenull == TRUE) && (state2 == idle)) {
937 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
940 if ((blinkstate) && (item->D_sock != NULL)) {
941 if (item->state > 1) { //Warning or error while running
942 xpm = generateXPM(running, item->oldstate);
945 xpm = generateXPM(idle, item->oldstate);
949 xpm = generateXPM(item->state, item->oldstate);
953 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
955 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
956 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
959 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
963 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
964 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
965 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
968 strcpy(address, "ff0000");
971 strcpy(address, "ffffff");
974 strcpy(address, "00ff00");
977 strcpy(address, "ffff00");
981 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
984 strcpy(address, "ff0000");
987 strcpy(address, "ffffff");
990 strcpy(address, "00ff00");
993 strcpy(address, "ffff00");
997 return (const char**)xpm_generic_var;