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 */
64 static gboolean fd_read(gpointer data);
65 static gboolean blink(gpointer data);
68 void trayMessage(const char *fmt,...);
69 void updateStatusIcon(monitoritem* item);
70 void changeStatusMessage(monitoritem* item, const char *fmt,...);
71 static const char** generateXPM(stateenum newstate, stateenum oldstate);
74 static void TrayIconActivate(GtkWidget *widget, gpointer data);
75 static void TrayIconExit(unsigned int activateTime, unsigned int button);
76 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
77 static void MonitorAbout(GtkWidget *widget, gpointer data);
78 static void MonitorRefresh(GtkWidget *widget, gpointer data);
79 static void IntervalChanged(GtkWidget *widget, gpointer data);
80 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
81 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
83 static guint timerTag;
84 static EggStatusIcon *mTrayIcon;
85 static GtkWidget *mTrayMenu;
86 static GtkWidget *window;
87 static GtkWidget *textview;
88 static GtkTextBuffer *buffer;
89 static GtkWidget *timeoutspinner;
90 char** xpm_generic_var;
91 static gboolean blinkstate = TRUE;
93 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
98 "Copyright (C) 2000-2004 Kern Sibbald and John Walker\n"
99 "Written by Nicolas Boichat (2004)\n"
100 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
101 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
102 " -c <file> set configuration file to file\n"
103 " -dnn set debug level to nn\n"
104 " -t test - read configuration and exit\n"
105 " -? print this message.\n"
106 "\n"), HOST_OS, DISTNAME, DISTVER);
109 static GtkWidget *new_image_button(const gchar *stock_id,
110 const gchar *label_text) {
116 button = gtk_button_new();
118 box = gtk_hbox_new(FALSE, 0);
119 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
120 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
121 label = gtk_label_new(label_text);
123 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
124 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
126 gtk_widget_show(image);
127 gtk_widget_show(label);
129 gtk_widget_show(box);
131 gtk_container_add(GTK_CONTAINER(button), box);
136 /*********************************************************************
138 * Main Bacula Tray Monitor -- User Interface Program
141 int main(int argc, char *argv[])
144 bool test_config = false;
150 my_name_is(argc, argv, "tray-monitor");
151 textdomain("bacula");
152 init_msg(NULL, NULL);
153 working_directory = "/tmp";
154 args = get_pool_memory(PM_FNAME);
156 struct sigaction sigignore;
157 sigignore.sa_flags = 0;
158 sigignore.sa_handler = SIG_IGN;
159 sigfillset(&sigignore.sa_mask);
160 sigaction(SIGPIPE, &sigignore, NULL);
162 gtk_init (&argc, &argv);
164 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
166 case 'c': /* configuration file */
167 if (configfile != NULL) {
170 configfile = bstrdup(optarg);
174 debug_level = atoi(optarg);
175 if (debug_level <= 0) {
199 if (configfile == NULL) {
200 configfile = bstrdup(CONFIG_FILE);
203 parse_config(configfile);
207 foreach_res(monitor, R_MONITOR) {
212 Emsg2(M_ERROR_TERM, 0,
213 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
217 foreach_res(dird, R_DIRECTOR) {
218 items[nitems].type = R_DIRECTOR;
219 items[nitems].resource = dird;
220 items[nitems].D_sock = NULL;
221 items[nitems].state = warn;
222 items[nitems].oldstate = warn;
225 foreach_res(filed, R_CLIENT) {
226 items[nitems].type = R_CLIENT;
227 items[nitems].resource = filed;
228 items[nitems].D_sock = NULL;
229 items[nitems].state = warn;
230 items[nitems].oldstate = warn;
233 foreach_res(stored, R_STORAGE) {
234 items[nitems].type = R_STORAGE;
235 items[nitems].resource = stored;
236 items[nitems].D_sock = NULL;
237 items[nitems].state = warn;
238 items[nitems].oldstate = warn;
244 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n"
245 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
252 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
253 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
254 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
255 g_assert((xpm_generic_var[i] = (char*)g_malloc(strlen(xpm_generic[i])*sizeof(char))));
256 strcpy(xpm_generic_var[i], xpm_generic[i]);
259 (void)WSA_Init(); /* Initialize Windows sockets */
262 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
265 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
266 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
267 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
270 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
271 // This should be ideally replaced by a completely libpr0n-based icon rendering.
272 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
273 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
274 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
275 g_object_unref(G_OBJECT(pixbuf));
277 mTrayMenu = gtk_menu_new();
281 entry = gtk_menu_item_new_with_label("Open status window...");
282 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
283 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
285 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
287 entry = gtk_menu_item_new_with_label("Exit");
288 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
289 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
291 gtk_widget_show_all(mTrayMenu);
293 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
295 g_timeout_add( 1000, blink, NULL );
297 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
299 gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
301 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
302 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
304 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
306 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
308 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
310 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
312 GtkWidget* separator = gtk_hseparator_new();
313 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
316 GSList *group = NULL;
320 for (int i = 0; i < nitems; i++) {
321 switch (items[i].type) {
323 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
324 g_string_append(str, _(" (DIR)"));
327 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
328 g_string_append(str, _(" (FD)"));
331 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
332 g_string_append(str, _(" (SD)"));
338 radio = gtk_radio_button_new_with_label(group, str->str);
339 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
340 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
342 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
343 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
345 items[i].label = gtk_label_new(_("Unknown status."));
346 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
347 gtk_container_add(GTK_CONTAINER(align), items[i].label);
349 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
350 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
351 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
352 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
353 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
354 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
356 separator = gtk_hseparator_new();
357 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
359 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
362 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
364 textview = gtk_text_view_new();
366 buffer = gtk_text_buffer_new(NULL);
368 gtk_text_buffer_set_text(buffer, "", -1);
370 PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
371 gtk_widget_modify_font(textview, font_desc);
372 pango_font_description_free(font_desc);
374 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
375 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
377 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
379 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
381 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
383 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
385 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
386 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
387 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
388 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
389 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
390 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
391 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
392 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
394 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
395 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
396 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
398 button = new_image_button("gtk-help", _("About"));
399 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
400 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
402 button = new_image_button("gtk-close", _("Close"));
403 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
404 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
406 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
408 gtk_container_add(GTK_CONTAINER (window), vbox);
410 gtk_widget_show_all(vbox);
414 for (i = 0; i < nitems; i++) {
415 if (items[i].D_sock) {
416 writecmd(&items[i], "quit");
417 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
418 bnet_close(items[i].D_sock);
422 free_pool_memory(args);
423 (void)WSACleanup(); /* Cleanup Windows sockets */
425 //Free xpm_generic_var
426 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
427 g_free(xpm_generic_var[i]);
429 g_free(xpm_generic_var);
434 static void MonitorAbout(GtkWidget *widget, gpointer data) {
436 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
437 "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
438 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
439 "Written by Nicolas Boichat\n"
440 "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
441 ), HOST_OS, DISTNAME, DISTVER);
443 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
444 "Bacula Tray Monitor\n\n"
445 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
446 "Written by Nicolas Boichat\n"
447 "\nVersion: " VERSION " (" BDATE ") %s %s %s"
448 ), HOST_OS, DISTNAME, DISTVER);
450 gtk_dialog_run(GTK_DIALOG(about));
451 gtk_widget_destroy(about);
454 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
455 for (int i = 0; i < nitems; i++) {
460 static gboolean delete_event( GtkWidget *widget,
463 gtk_widget_hide(window);
464 return TRUE; /* do not destroy the window */
467 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
468 gtk_widget_show(window);
471 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
472 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
473 gtk_widget_show_all(mTrayMenu);
476 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
480 static void IntervalChanged(GtkWidget *widget, gpointer data) {
481 g_source_remove(timerTag);
482 timerTag = g_timeout_add(
484 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
488 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
489 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
491 for (int i = 0; i < nitems; i++) {
492 if (data == &(items[i])) {
497 g_return_if_fail(fullitem != -1);
499 int oldlastupdated = lastupdated;
500 lastupdated = fullitem-1;
502 lastupdated = oldlastupdated;
506 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
507 switch (item->type) {
509 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
512 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
515 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
518 printf("Error, currentitem is not a Client or a Storage..\n");
524 static gboolean blink(gpointer data) {
525 blinkstate = !blinkstate;
526 for (int i = 0; i < nitems; i++) {
527 updateStatusIcon(&items[i]);
529 updateStatusIcon(NULL);
533 static gboolean fd_read(gpointer data) {
534 GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
535 GtkTextIter start, stop, nstart, nstop;
539 GString *strlast, *strcurrent;
542 if (lastupdated == nitems) {
546 if (lastupdated == fullitem) {
547 if (items[lastupdated].type == R_DIRECTOR)
548 docmd(&items[lastupdated], "status Director\n", &list);
550 docmd(&items[lastupdated], "status\n", &list);
554 gtk_text_buffer_get_end_iter(newbuffer, &stop);
555 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
556 if (it->data) g_string_free((GString*)it->data, TRUE);
557 } while ((it = it->next) != NULL);
559 /* Keep the selection if necessary */
560 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
561 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
562 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
565 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
567 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
568 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
572 g_object_unref(buffer);
575 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
578 getstatus(&items[lastupdated], 1, &strcurrent);
579 getstatus(&items[lastupdated], 0, &strlast);
580 updateStatusIcon(&items[lastupdated]);
582 changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
584 updateStatusIcon(NULL);
586 g_string_free(strcurrent, TRUE);
587 g_string_free(strlast, TRUE);
592 void getstatus(monitoritem* item, int current, GString** str) {
594 stateenum ret = error;
595 int jobid = 0, joberrors = 0;
596 char jobstatus = JS_ErrorTerminated;
600 *str = g_string_sized_new(128);
603 if (item->type == R_DIRECTOR)
604 docmd(&items[lastupdated], ".status dir current\n", &list);
606 docmd(&items[lastupdated], ".status current\n", &list);
609 if (item->type == R_DIRECTOR)
610 docmd(&items[lastupdated], ".status dir last\n", &list);
612 docmd(&items[lastupdated], ".status last\n", &list);
616 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
617 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
618 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
619 g_string_set_size(*str, (*str)->len-1);
623 else if ((it = it->next) == NULL) {
625 g_string_append(*str, _("No current job."));
628 g_string_append(*str, _("No last job."));
632 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
635 ret = (joberrors > 0) ? warn : running;
636 g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
639 ret = (joberrors > 0) ? warn : running;
640 g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
643 g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
647 g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
648 ret = (joberrors > 0) ? warn : idle;
650 case JS_ErrorTerminated:
651 g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
655 ret = (joberrors > 0) ? warn : running;
656 g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
659 g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
663 g_string_append_printf(*str, _("Job status: Verify differences (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
667 g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
671 g_string_append_printf(*str, _("Job status: Waiting on File daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
675 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
679 g_string_append_printf(*str, _("Job status: Waiting for new media (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
683 g_string_append_printf(*str, _("Job status: Waiting for Mount (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
686 case JS_WaitStoreRes:
687 g_string_append_printf(*str, _("Job status: Waiting for storage resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
691 g_string_append_printf(*str, _("Job status: Waiting for job resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
694 case JS_WaitClientRes:
695 g_string_append_printf(*str, _("Job status: Waiting for Client resource (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
699 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
702 case JS_WaitStartTime:
703 g_string_append_printf(*str, _("Job status: Waiting for start time (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
706 case JS_WaitPriority:
707 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
711 g_warning("Unknown job status %c.", jobstatus);
712 g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
718 fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
724 if (it->data) g_string_free((GString*)it->data, TRUE);
725 } while ((it = it->next) != NULL);
733 item->oldstate = ret;
737 int docmd(monitoritem* item, const char* command, GSList** list) {
741 *list = g_slist_alloc();
743 //str = g_string_sized_new(64);
746 memset(&jcr, 0, sizeof(jcr));
752 switch (item->type) {
754 dird = (DIRRES*)item->resource;
755 trayMessage("Connecting to Director %s:%d\n", dird->address, dird->DIRport);
756 changeStatusMessage(item, "Connecting to Director %s:%d", dird->address, dird->DIRport);
757 item->D_sock = bnet_connect(NULL, 0, 0, "Director daemon", dird->address, NULL, dird->DIRport, 0);
758 jcr.dir_bsock = item->D_sock;
761 filed = (CLIENT*)item->resource;
762 trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
763 changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
764 item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
765 jcr.file_bsock = item->D_sock;
768 stored = (STORE*)item->resource;
769 trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
770 changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
771 item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
772 jcr.store_bsock = item->D_sock;
775 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
780 if (item->D_sock == NULL) {
781 g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
782 changeStatusMessage(item, "Cannot connect to daemon.");
784 item->oldstate = error;
788 if (!authenticate_daemon(item, &jcr)) {
789 str = g_string_sized_new(64);
790 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
791 g_slist_append(*list, str);
793 item->oldstate = error;
794 changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
799 switch (item->type) {
801 trayMessage("Opened connection with Director daemon.\n");
802 changeStatusMessage(item, "Opened connection with Director daemon.");
805 trayMessage("Opened connection with File daemon.\n");
806 changeStatusMessage(item, "Opened connection with File daemon.");
809 trayMessage("Opened connection with Storage daemon.\n");
810 changeStatusMessage(item, "Opened connection with Storage daemon.");
813 printf("Error, currentitem is not a Client, a Storage or a Director..\n");
819 if (item->type == R_DIRECTOR) { /* Read connection messages... */
821 docmd(item, "", &list); /* Usually invalid, but no matter */
824 if (it->data) g_string_free((GString*)it->data, TRUE);
825 } while ((it = it->next) != NULL);
832 writecmd(item, command);
835 if ((stat = bnet_recv(item->D_sock)) >= 0) {
836 g_slist_append(*list, g_string_new(item->D_sock->msg));
838 else if (stat == BNET_SIGNAL) {
839 if (item->D_sock->msglen == BNET_EOD) {
840 //fprintf(stderr, "<< EOD >>\n");
843 else if (item->D_sock->msglen == BNET_PROMPT) {
844 //fprintf(stderr, "<< PROMPT >>\n");
845 g_slist_append(*list, g_string_new("<< Error: BNET_PROMPT signal received. >>\n"));
848 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
849 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
850 g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
853 str = g_string_sized_new(64);
854 g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
855 g_slist_append(*list, str);
858 else { /* BNET_HARDEOF || BNET_ERROR */
859 g_slist_append(*list, g_string_new("<ERROR>\n"));
862 item->oldstate = error;
863 changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
864 //fprintf(stderr, "<< ERROR >>\n");
868 if (is_bnet_stop(item->D_sock)) {
869 g_string_append_printf(str, "<STOP>\n");
872 item->oldstate = error;
873 changeStatusMessage(item, "Error : Connection closed.");
874 //fprintf(stderr, "<< STOP >>\n");
875 return 0; /* error or term */
880 void writecmd(monitoritem* item, const char* command) {
882 item->D_sock->msglen = strlen(command);
883 pm_strcpy(&item->D_sock->msg, command);
884 bnet_send(item->D_sock);
888 /* Note: Does not seem to work either on Gnome nor KDE... */
889 void trayMessage(const char *fmt,...) {
893 va_start(arg_ptr, fmt);
894 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
897 fprintf(stderr, buf);
899 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
902 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
906 va_start(arg_ptr, fmt);
907 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
910 gtk_label_set_text(GTK_LABEL(item->label), buf);
913 void updateStatusIcon(monitoritem* item) {
917 /* For the current status, select the two worse for blinking,
918 but never blink a D_Sock == NULL error with idle. */
919 stateenum state1, state2, oldstate;
920 gboolean onenull = FALSE;
924 for (int i = 0; i < nitems; i++) {
925 if (items[i].D_sock == NULL) onenull = TRUE;
926 if (items[i].state >= state1) {
928 state1 = items[i].state;
930 else if (items[i].state > state2) {
931 state2 = items[i].state;
933 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
936 if ((onenull == TRUE) && (state2 == idle)) {
940 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
943 if ((blinkstate) && (item->D_sock != NULL)) {
944 if (item->state > 1) { //Warning or error while running
945 xpm = generateXPM(running, item->oldstate);
948 xpm = generateXPM(idle, item->oldstate);
952 xpm = generateXPM(item->state, item->oldstate);
956 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
958 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
959 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
962 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
966 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
967 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
968 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
971 strcpy(address, "ff0000");
974 strcpy(address, "ffffff");
977 strcpy(address, "00ff00");
980 strcpy(address, "ffff00");
984 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
987 strcpy(address, "ff0000");
990 strcpy(address, "ffffff");
993 strcpy(address, "00ff00");
996 strcpy(address, "ffff00");
1000 return (const char**)xpm_generic_var;