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 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
155 case 'c': /* configuration file */
156 if (configfile != NULL) {
159 configfile = bstrdup(optarg);
163 debug_level = atoi(optarg);
164 if (debug_level <= 0) {
188 if (configfile == NULL) {
189 configfile = bstrdup(CONFIG_FILE);
192 parse_config(configfile);
196 foreach_res(monitor, R_MONITOR) {
201 Emsg2(M_ERROR_TERM, 0,
202 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
206 foreach_res(dird, R_DIRECTOR) {
207 items[nitems].type = R_DIRECTOR;
208 items[nitems].resource = dird;
209 items[nitems].D_sock = NULL;
210 items[nitems].state = warn;
211 items[nitems].oldstate = warn;
214 foreach_res(filed, R_CLIENT) {
215 items[nitems].type = R_CLIENT;
216 items[nitems].resource = filed;
217 items[nitems].D_sock = NULL;
218 items[nitems].state = warn;
219 items[nitems].oldstate = warn;
222 foreach_res(stored, R_STORAGE) {
223 items[nitems].type = R_STORAGE;
224 items[nitems].resource = stored;
225 items[nitems].D_sock = NULL;
226 items[nitems].state = warn;
227 items[nitems].oldstate = warn;
233 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n\
234 Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
241 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
242 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
243 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
244 g_assert((xpm_generic_var[i] = (char*)g_malloc(strlen(xpm_generic[i])*sizeof(char))));
245 strcpy(xpm_generic_var[i], xpm_generic[i]);
248 (void)WSA_Init(); /* Initialize Windows sockets */
251 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
254 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
255 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n\
256 This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
259 gtk_init (&argc, &argv);
261 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
262 // This should be ideally replaced by a completely libpr0n-based icon rendering.
263 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
264 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
265 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
266 g_object_unref(G_OBJECT(pixbuf));
268 mTrayMenu = gtk_menu_new();
272 entry = gtk_menu_item_new_with_label("Open status window...");
273 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
274 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
276 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
278 entry = gtk_menu_item_new_with_label("Exit");
279 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
280 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
282 gtk_widget_show_all(mTrayMenu);
284 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
286 g_timeout_add( 1000, blink, NULL );
288 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
290 gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
292 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
293 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
295 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
297 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
299 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
301 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
303 GtkWidget* separator = gtk_hseparator_new();
304 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
307 GSList *group = NULL;
311 for (int i = 0; i < nitems; i++) {
312 switch (items[i].type) {
314 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
315 g_string_append(str, _(" (DIR)"));
318 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
319 g_string_append(str, _(" (FD)"));
322 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
323 g_string_append(str, _(" (SD)"));
329 radio = gtk_radio_button_new_with_label(group, str->str);
330 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
331 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
333 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
334 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
336 items[i].label = gtk_label_new(_("Unknown status."));
337 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
338 gtk_container_add(GTK_CONTAINER(align), items[i].label);
340 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
341 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
342 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
343 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
344 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
345 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
347 separator = gtk_hseparator_new();
348 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
350 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
353 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
355 textview = gtk_text_view_new();
357 buffer = gtk_text_buffer_new(NULL);
359 gtk_text_buffer_set_text(buffer, "", -1);
361 PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
362 gtk_widget_modify_font(textview, font_desc);
363 pango_font_description_free(font_desc);
365 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
366 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
368 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
370 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
372 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
374 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
376 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
377 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
378 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
379 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
380 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
381 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
382 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
383 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
385 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
386 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
387 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
389 button = new_image_button("gtk-help", _("About"));
390 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
391 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
393 button = new_image_button("gtk-close", _("Close"));
394 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
395 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
397 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
399 gtk_container_add(GTK_CONTAINER (window), vbox);
401 gtk_widget_show_all(vbox);
405 for (i = 0; i < nitems; i++) {
406 if (items[i].D_sock) {
407 writecmd(&items[i], "quit");
408 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
409 bnet_close(items[i].D_sock);
413 free_pool_memory(args);
414 (void)WSACleanup(); /* Cleanup Windows sockets */
416 //Free xpm_generic_var
417 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
418 g_free(xpm_generic_var[i]);
420 g_free(xpm_generic_var);
425 static void MonitorAbout(GtkWidget *widget, gpointer data) {
427 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
428 "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
429 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
430 "Written by Nicolas Boichat\n"
431 "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
432 ), HOST_OS, DISTNAME, DISTVER);
434 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
435 "Bacula Tray Monitor\n\n"
436 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
437 "Written by Nicolas Boichat\n"
438 "\nVersion: " VERSION " (" BDATE ") %s %s %s"
439 ), HOST_OS, DISTNAME, DISTVER);
441 gtk_dialog_run(GTK_DIALOG(about));
442 gtk_widget_destroy(about);
445 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
446 for (int i = 0; i < nitems; i++) {
451 static gboolean delete_event( GtkWidget *widget,
454 gtk_widget_hide(window);
455 return TRUE; /* do not destroy the window */
458 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
459 gtk_widget_show(window);
462 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
463 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
464 gtk_widget_show_all(mTrayMenu);
467 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
471 static void IntervalChanged(GtkWidget *widget, gpointer data) {
472 g_source_remove(timerTag);
473 timerTag = g_timeout_add(
475 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
479 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
480 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
482 for (int i = 0; i < nitems; i++) {
483 if (data == &(items[i])) {
488 g_return_if_fail(fullitem != -1);
490 int oldlastupdated = lastupdated;
491 lastupdated = fullitem-1;
493 lastupdated = oldlastupdated;
497 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
498 switch (item->type) {
500 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
503 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
506 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
509 printf("Error, currentitem is not a Client or a Storage..\n");
515 static gboolean blink(gpointer data) {
516 blinkstate = !blinkstate;
517 for (int i = 0; i < nitems; i++) {
518 updateStatusIcon(&items[i]);
520 updateStatusIcon(NULL);
524 static gboolean fd_read(gpointer data) {
525 GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
526 GtkTextIter start, stop, nstart, nstop;
530 GString *strlast, *strcurrent;
533 if (lastupdated == nitems) {
537 if (lastupdated == fullitem) {
538 if (items[lastupdated].type == R_DIRECTOR)
539 docmd(&items[lastupdated], "status Director\n", &list);
541 docmd(&items[lastupdated], "status\n", &list);
545 gtk_text_buffer_get_end_iter(newbuffer, &stop);
546 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
547 if (it->data) g_string_free((GString*)it->data, TRUE);
548 } while ((it = it->next) != NULL);
550 /* Keep the selection if necessary */
551 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
552 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
553 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
556 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
558 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
559 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
563 g_object_unref(buffer);
566 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
569 getstatus(&items[lastupdated], 1, &strcurrent);
570 getstatus(&items[lastupdated], 0, &strlast);
571 updateStatusIcon(&items[lastupdated]);
573 changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
575 updateStatusIcon(NULL);
577 g_string_free(strcurrent, TRUE);
578 g_string_free(strlast, TRUE);
583 void getstatus(monitoritem* item, int current, GString** str) {
585 stateenum ret = error;
586 int jobid = 0, joberrors = 0;
587 char jobstatus = JS_ErrorTerminated;
591 *str = g_string_sized_new(128);
594 if (item->type == R_DIRECTOR)
595 docmd(&items[lastupdated], ".status dir current\n", &list);
597 docmd(&items[lastupdated], ".status current\n", &list);
600 if (item->type == R_DIRECTOR)
601 docmd(&items[lastupdated], ".status dir last\n", &list);
603 docmd(&items[lastupdated], ".status last\n", &list);
607 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
608 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
609 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
610 g_string_set_size(*str, (*str)->len-1);
614 else if ((it = it->next) == NULL) {
616 g_string_append(*str, _("No current job."));
619 g_string_append(*str, _("No last job."));
623 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
626 ret = (joberrors > 0) ? warn : running;
627 g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
630 ret = (joberrors > 0) ? warn : running;
631 g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
634 g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
638 g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
639 ret = (joberrors > 0) ? warn : idle;
641 case JS_ErrorTerminated:
642 g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
646 ret = (joberrors > 0) ? warn : running;
647 g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
650 g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
654 g_string_append_printf(*str, _("Job status: Verify differences (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
658 g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
662 g_string_append_printf(*str, _("Job status: Waiting on File daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
666 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
670 g_string_append_printf(*str, _("Job status: Waiting for new media (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
674 g_string_append_printf(*str, _("Job status: Waiting for Mount (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
677 case JS_WaitStoreRes:
678 g_string_append_printf(*str, _("Job status: Waiting for storage resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
682 g_string_append_printf(*str, _("Job status: Waiting for job resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
685 case JS_WaitClientRes:
686 g_string_append_printf(*str, _("Job status: Waiting for Client resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
690 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
693 case JS_WaitStartTime:
694 g_string_append_printf(*str, _("Job status: Waiting for start time (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
697 case JS_WaitPriority:
698 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
702 g_warning("Unknown job status %c.", jobstatus);
703 g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
709 fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
715 if (it->data) g_string_free((GString*)it->data, TRUE);
716 } while ((it = it->next) != NULL);
724 item->oldstate = ret;
728 int docmd(monitoritem* item, const char* command, GSList** list) {
732 *list = g_slist_alloc();
734 //str = g_string_sized_new(64);
737 memset(&jcr, 0, sizeof(jcr));
743 switch (item->type) {
745 dird = (DIRRES*)item->resource;
746 trayMessage("Connecting to Director %s:%d\n", dird->address, dird->DIRport);
747 changeStatusMessage(item, "Connecting to Director %s:%d", dird->address, dird->DIRport);
748 item->D_sock = bnet_connect(NULL, 0, 0, "Director daemon", dird->address, NULL, dird->DIRport, 0);
749 jcr.dir_bsock = item->D_sock;
752 filed = (CLIENT*)item->resource;
753 trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
754 changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
755 item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
756 jcr.file_bsock = item->D_sock;
759 stored = (STORE*)item->resource;
760 trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
761 changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
762 item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
763 jcr.store_bsock = item->D_sock;
766 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
771 if (item->D_sock == NULL) {
772 g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
773 changeStatusMessage(item, "Cannot connect to daemon.");
775 item->oldstate = error;
779 if (!authenticate_daemon(item, &jcr)) {
780 str = g_string_sized_new(64);
781 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
782 g_slist_append(*list, str);
784 item->oldstate = error;
785 changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
790 switch (item->type) {
792 trayMessage("Opened connection with Director daemon.\n");
793 changeStatusMessage(item, "Opened connection with Director daemon.");
796 trayMessage("Opened connection with File daemon.\n");
797 changeStatusMessage(item, "Opened connection with File daemon.");
800 trayMessage("Opened connection with Storage daemon.\n");
801 changeStatusMessage(item, "Opened connection with Storage daemon.");
804 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
810 if (item->type == R_DIRECTOR) { /* Read connection messages... */
812 docmd(item, "", &list); /* Usually invalid, but no matter */
815 if (it->data) g_string_free((GString*)it->data, TRUE);
816 } while ((it = it->next) != NULL);
823 writecmd(item, command);
826 if ((stat = bnet_recv(item->D_sock)) >= 0) {
827 g_slist_append(*list, g_string_new(item->D_sock->msg));
829 else if (stat == BNET_SIGNAL) {
830 if (item->D_sock->msglen == BNET_EOD) {
831 //fprintf(stderr, "<< EOD >>\n");
834 else if (item->D_sock->msglen == BNET_PROMPT) {
835 //fprintf(stderr, "<< PROMPT >>\n");
836 g_slist_append(*list, g_string_new("<< Error: BNET_PROMPT signal received. >>\n"));
839 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
840 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
841 g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
844 str = g_string_sized_new(64);
845 g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
846 g_slist_append(*list, str);
849 else { /* BNET_HARDEOF || BNET_ERROR */
850 g_slist_append(*list, g_string_new("<ERROR>\n"));
853 item->oldstate = error;
854 changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
855 //fprintf(stderr, "<< ERROR >>\n");
859 if (is_bnet_stop(item->D_sock)) {
860 g_string_append_printf(str, "<STOP>\n");
863 item->oldstate = error;
864 changeStatusMessage(item, "Error : Connection closed.");
865 //fprintf(stderr, "<< STOP >>\n");
866 return 0; /* error or term */
871 void writecmd(monitoritem* item, const char* command) {
873 item->D_sock->msglen = strlen(command);
874 pm_strcpy(&item->D_sock->msg, command);
875 bnet_send(item->D_sock);
879 /* Note: Does not seem to work either on Gnome nor KDE... */
880 void trayMessage(const char *fmt,...) {
884 va_start(arg_ptr, fmt);
885 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
888 fprintf(stderr, buf);
890 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
893 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
897 va_start(arg_ptr, fmt);
898 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
901 gtk_label_set_text(GTK_LABEL(item->label), buf);
904 void updateStatusIcon(monitoritem* item) {
908 /* For the current status, select the two worse for blinking,
909 but never blink a D_Sock == NULL error with idle. */
910 stateenum state1, state2, oldstate;
911 gboolean onenull = FALSE;
915 for (int i = 0; i < nitems; i++) {
916 if (items[i].D_sock == NULL) onenull = TRUE;
917 if (items[i].state >= state1) {
919 state1 = items[i].state;
921 else if (items[i].state > state2) {
922 state2 = items[i].state;
924 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
927 if ((onenull == TRUE) && (state2 == idle)) {
931 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
934 if ((blinkstate) && (item->D_sock != NULL)) {
935 if (item->state > 1) { //Warning or error while running
936 xpm = generateXPM(running, item->oldstate);
939 xpm = generateXPM(idle, item->oldstate);
943 xpm = generateXPM(item->state, item->oldstate);
947 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
949 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
950 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
953 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
957 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
958 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
959 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
962 strcpy(address, "ff0000");
965 strcpy(address, "ffffff");
968 strcpy(address, "00ff00");
971 strcpy(address, "ffff00");
975 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
978 strcpy(address, "ff0000");
981 strcpy(address, "ffffff");
984 strcpy(address, "00ff00");
987 strcpy(address, "ffff00");
991 return (const char**)xpm_generic_var;