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: %s (%s) %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"), VERSION, BDATE, 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;
185 setlocale(LC_ALL, "");
186 bindtextdomain("bacula", LOCALEDIR);
187 textdomain("bacula");
190 my_name_is(argc, argv, "tray-monitor");
191 init_msg(NULL, NULL);
192 working_directory = "/tmp";
194 struct sigaction sigignore;
195 sigignore.sa_flags = 0;
196 sigignore.sa_handler = SIG_IGN;
197 sigfillset(&sigignore.sa_mask);
198 sigaction(SIGPIPE, &sigignore, NULL);
200 gtk_init (&argc, &argv);
202 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
204 case 'c': /* configuration file */
205 if (configfile != NULL) {
208 configfile = bstrdup(optarg);
212 debug_level = atoi(optarg);
213 if (debug_level <= 0) {
237 if (configfile == NULL) {
238 configfile = bstrdup(CONFIG_FILE);
241 parse_config(configfile);
245 foreach_res(monitor, R_MONITOR) {
250 Emsg2(M_ERROR_TERM, 0,
251 _("Error: %d Monitor resource defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile);
255 foreach_res(dird, R_DIRECTOR) {
256 items[nitems].type = R_DIRECTOR;
257 items[nitems].resource = dird;
258 items[nitems].D_sock = NULL;
259 items[nitems].state = warn;
260 items[nitems].oldstate = warn;
263 foreach_res(filed, R_CLIENT) {
264 items[nitems].type = R_CLIENT;
265 items[nitems].resource = filed;
266 items[nitems].D_sock = NULL;
267 items[nitems].state = warn;
268 items[nitems].oldstate = warn;
271 foreach_res(stored, R_STORAGE) {
272 items[nitems].type = R_STORAGE;
273 items[nitems].resource = stored;
274 items[nitems].D_sock = NULL;
275 items[nitems].state = warn;
276 items[nitems].oldstate = warn;
282 Emsg1(M_ERROR_TERM, 0, _("No Client, Storage nor Director resource defined in %s\n"
283 "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile);
290 //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
291 g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic))));
292 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
293 g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char))));
294 strcpy(xpm_generic_var[i], xpm_generic[i]);
297 (void)WSA_Init(); /* Initialize Windows sockets */
300 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
303 if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) {
304 Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n"
305 "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval);
308 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
309 // This should be ideally replaced by a completely libpr0n-based icon rendering.
310 mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
311 g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
312 g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
313 g_object_unref(G_OBJECT(pixbuf));
315 mTrayMenu = gtk_menu_new();
319 entry = gtk_menu_item_new_with_label(_("Open status window..."));
320 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
321 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
323 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
325 entry = gtk_menu_item_new_with_label(_("Exit"));
326 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
327 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
329 gtk_widget_show_all(mTrayMenu);
331 timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL );
333 g_timeout_add( 1000, blink, NULL );
335 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
337 gtk_window_set_title(GTK_WINDOW(window), _("Bacula tray monitor"));
339 g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
340 //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
342 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
344 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
346 GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
348 gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
350 GtkWidget* separator = gtk_hseparator_new();
351 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
354 GSList *group = NULL;
358 for (int i = 0; i < nitems; i++) {
359 switch (items[i].type) {
361 str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
362 g_string_append(str, _(" (DIR)"));
365 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
366 g_string_append(str, _(" (FD)"));
369 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
370 g_string_append(str, _(" (SD)"));
376 radio = gtk_radio_button_new_with_label(group, str->str);
377 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
378 g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
380 pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
381 items[i].image = gtk_image_new_from_pixbuf(pixbuf);
383 items[i].label = gtk_label_new(_("Unknown status."));
384 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
385 gtk_container_add(GTK_CONTAINER(align), items[i].label);
387 gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2,
388 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
389 gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2,
390 GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
391 gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2,
392 (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
394 separator = gtk_hseparator_new();
395 gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
397 group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
400 gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
402 textview = gtk_text_view_new();
404 buffer = gtk_text_buffer_new(NULL);
406 gtk_text_buffer_set_text(buffer, "", -1);
408 PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
409 gtk_widget_modify_font(textview, font_desc);
410 pango_font_description_free(font_desc);
412 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
413 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
415 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
417 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
419 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
421 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
423 GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
424 GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
425 gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
426 GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0);
427 timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
428 g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
429 gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
430 gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
432 GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
433 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
434 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
436 button = new_image_button("gtk-help", _("About"));
437 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
438 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
440 button = new_image_button("gtk-close", _("Close"));
441 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
442 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
444 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
446 gtk_container_add(GTK_CONTAINER (window), vbox);
448 gtk_widget_show_all(vbox);
452 g_source_remove(timerTag);
456 for (i = 0; i < nitems; i++) {
457 if (items[i].D_sock) {
458 switch (items[i].type) {
460 trayMessage(_("Disconnecting from Director %s:%d\n"), ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport);
463 trayMessage(_("Disconnecting from Client %s:%d\n"), ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport);
466 trayMessage(_("Disconnecting from Storage %s:%d\n"), ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport);
471 //writecmd(&items[i], "quit");
472 bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
473 bnet_close(items[i].D_sock);
477 (void)WSACleanup(); /* Cleanup Windows sockets */
479 //Free xpm_generic_var
480 for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
481 g_free(xpm_generic_var[i]);
483 g_free(xpm_generic_var);
485 gtk_object_destroy(GTK_OBJECT(window));
486 gtk_object_destroy(GTK_OBJECT(mTrayMenu));
489 #if TRAY_DEBUG_MEMORY
496 static void MonitorAbout(GtkWidget *widget, gpointer data) {
498 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
499 "<span size='x-large' weight='bold'>%s</span>\n\n"
501 "\n<small>%s: %s (%s) %s %s %s</small>",
502 _("Bacula Tray Monitor"),
503 _("Copyright (C) 2004-2005 Kern Sibbald\n"
504 "Written by Nicolas Boichat\n"),
506 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
508 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
511 "\n%s %s (%s) %s %s %s",
512 _("Bacula Tray Monitor"),
513 _("Copyright (C) 2004-2005 Kern Sibbald\n"
514 "Written by Nicolas Boichat\n"),
516 VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
518 gtk_dialog_run(GTK_DIALOG(about));
519 gtk_widget_destroy(about);
522 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
523 for (int i = 0; i < nitems; i++) {
528 static gboolean delete_event( GtkWidget *widget,
531 gtk_widget_hide(window);
532 return TRUE; /* do not destroy the window */
535 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
536 gtk_widget_show(window);
539 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
540 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
541 gtk_widget_show_all(mTrayMenu);
544 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
548 static void IntervalChanged(GtkWidget *widget, gpointer data) {
549 g_source_remove(timerTag);
550 timerTag = g_timeout_add(
552 gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
556 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
557 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
559 for (int i = 0; i < nitems; i++) {
560 if (data == &(items[i])) {
565 g_return_if_fail(fullitem != -1);
567 int oldlastupdated = lastupdated;
568 lastupdated = fullitem-1;
570 lastupdated = oldlastupdated;
574 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
575 switch (item->type) {
577 return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
579 return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
581 return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
583 printf(_("Error, currentitem is not a Client or a Storage..\n"));
590 static gboolean blink(gpointer data) {
591 blinkstate = !blinkstate;
592 for (int i = 0; i < nitems; i++) {
593 updateStatusIcon(&items[i]);
595 updateStatusIcon(NULL);
599 static gboolean fd_read(gpointer data) {
601 #if TRAY_DEBUG_MEMORY
602 printf("sm_line=%d\n", sm_line);
604 GtkTextBuffer *newbuffer;
605 GtkTextIter start, stop, nstart, nstop;
609 GString *strlast, *strcurrent;
612 if (lastupdated == nitems) {
616 if (lastupdated == fullitem) {
617 newbuffer = gtk_text_buffer_new(NULL);
619 if (items[lastupdated].type == R_DIRECTOR)
620 docmd(&items[lastupdated], "status Director\n", &list);
622 docmd(&items[lastupdated], "status\n", &list);
626 gtk_text_buffer_get_end_iter(newbuffer, &stop);
627 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
628 if (it->data) g_string_free((GString*)it->data, TRUE);
629 } while ((it = it->next) != NULL);
633 /* Keep the selection if necessary */
634 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
635 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
636 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
639 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
641 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
642 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
646 g_object_unref(buffer);
649 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
652 getstatus(&items[lastupdated], 1, &strcurrent);
653 getstatus(&items[lastupdated], 0, &strlast);
654 updateStatusIcon(&items[lastupdated]);
656 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
658 updateStatusIcon(NULL);
660 g_string_free(strcurrent, TRUE);
661 g_string_free(strlast, TRUE);
666 void append_error_string(GString* str, int joberrors) {
668 g_string_append_printf(str, _(" (%d errors)"), joberrors);
671 g_string_append_printf(str, _(" (%d error)"), joberrors);
675 void getstatus(monitoritem* item, int current, GString** str) {
677 stateenum ret = error;
678 int jobid = 0, joberrors = 0;
679 char jobstatus = JS_ErrorTerminated;
683 *str = g_string_sized_new(128);
686 if (item->type == R_DIRECTOR)
687 docmd(&items[lastupdated], ".status dir current\n", &list);
689 docmd(&items[lastupdated], ".status current\n", &list);
692 if (item->type == R_DIRECTOR)
693 docmd(&items[lastupdated], ".status dir last\n", &list);
695 docmd(&items[lastupdated], ".status last\n", &list);
699 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
700 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
701 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
702 g_string_set_size(*str, (*str)->len-1);
706 else if ((it = it->next) == NULL) {
708 g_string_append(*str, _("No current job."));
711 g_string_append(*str, _("No last job."));
715 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
718 ret = (joberrors > 0) ? warn : running;
719 g_string_append_printf(*str, _("Job status: Created"));
720 append_error_string(*str, joberrors);
723 ret = (joberrors > 0) ? warn : running;
724 g_string_append_printf(*str, _("Job status: Running"));
725 append_error_string(*str, joberrors);
728 g_string_append_printf(*str, _("Job status: Blocked"));
729 append_error_string(*str, joberrors);
733 g_string_append_printf(*str, _("Job status: Terminated"));
734 append_error_string(*str, joberrors);
735 ret = (joberrors > 0) ? warn : idle;
737 case JS_ErrorTerminated:
738 g_string_append_printf(*str, _("Job status: Terminated in error"));
739 append_error_string(*str, joberrors);
743 ret = (joberrors > 0) ? warn : running;
744 g_string_append_printf(*str, _("Job status: Error"));
745 append_error_string(*str, joberrors);
748 g_string_append_printf(*str, _("Job status: Fatal error"));
749 append_error_string(*str, joberrors);
753 g_string_append_printf(*str, _("Job status: Verify differences"));
754 append_error_string(*str, joberrors);
758 g_string_append_printf(*str, _("Job status: Canceled"));
759 append_error_string(*str, joberrors);
763 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
764 append_error_string(*str, joberrors);
768 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
769 append_error_string(*str, joberrors);
773 g_string_append_printf(*str, _("Job status: Waiting for new media"));
774 append_error_string(*str, joberrors);
778 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
779 append_error_string(*str, joberrors);
782 case JS_WaitStoreRes:
783 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
784 append_error_string(*str, joberrors);
788 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
789 append_error_string(*str, joberrors);
792 case JS_WaitClientRes:
793 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
794 append_error_string(*str, joberrors);
798 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
799 append_error_string(*str, joberrors);
802 case JS_WaitStartTime:
803 g_string_append_printf(*str, _("Job status: Waiting for start time"));
804 append_error_string(*str, joberrors);
807 case JS_WaitPriority:
808 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
809 append_error_string(*str, joberrors);
813 g_warning(_("Unknown job status %c."), jobstatus);
814 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
815 append_error_string(*str, joberrors);
821 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
827 if (it->data) g_string_free((GString*)it->data, TRUE);
828 } while ((it = it->next) != NULL);
836 item->oldstate = ret;
840 int docmd(monitoritem* item, const char* command, GSList** list) {
844 *list = g_slist_alloc();
846 //str = g_string_sized_new(64);
849 memset(&jcr, 0, sizeof(jcr));
855 switch (item->type) {
857 dird = (DIRRES*)item->resource;
858 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
859 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
860 item->D_sock = bnet_connect(NULL, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
861 jcr.dir_bsock = item->D_sock;
864 filed = (CLIENT*)item->resource;
865 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
866 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
867 item->D_sock = bnet_connect(NULL, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
868 jcr.file_bsock = item->D_sock;
871 stored = (STORE*)item->resource;
872 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
873 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
874 item->D_sock = bnet_connect(NULL, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
875 jcr.store_bsock = item->D_sock;
878 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
883 if (item->D_sock == NULL) {
884 g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
885 changeStatusMessage(item, _("Cannot connect to daemon."));
887 item->oldstate = error;
891 if (!authenticate_daemon(item, &jcr)) {
892 str = g_string_sized_new(64);
893 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
894 g_slist_append(*list, str);
896 item->oldstate = error;
897 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
902 switch (item->type) {
904 trayMessage(_("Opened connection with Director daemon.\n"));
905 changeStatusMessage(item, _("Opened connection with Director daemon."));
908 trayMessage(_("Opened connection with File daemon.\n"));
909 changeStatusMessage(item, _("Opened connection with File daemon."));
912 trayMessage(_("Opened connection with Storage daemon.\n"));
913 changeStatusMessage(item, _("Opened connection with Storage daemon."));
916 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
922 if (item->type == R_DIRECTOR) { /* Read connection messages... */
924 docmd(item, "", &list); /* Usually invalid, but no matter */
927 if (it->data) g_string_free((GString*)it->data, TRUE);
928 } while ((it = it->next) != NULL);
935 writecmd(item, command);
938 if ((stat = bnet_recv(item->D_sock)) >= 0) {
939 g_slist_append(*list, g_string_new(item->D_sock->msg));
941 else if (stat == BNET_SIGNAL) {
942 if (item->D_sock->msglen == BNET_EOD) {
943 //fprintf(stderr, "<< EOD >>\n");
946 else if (item->D_sock->msglen == BNET_PROMPT) {
947 //fprintf(stderr, "<< PROMPT >>\n");
948 g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
951 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
952 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
953 g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
956 str = g_string_sized_new(64);
957 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
958 g_slist_append(*list, str);
961 else { /* BNET_HARDEOF || BNET_ERROR */
962 g_slist_append(*list, g_string_new(_("<ERROR>\n")));
965 item->oldstate = error;
966 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
967 //fprintf(stderr, _("<< ERROR >>\n"));
971 if (is_bnet_stop(item->D_sock)) {
972 g_string_append_printf(str, _("<STOP>\n"));
975 item->oldstate = error;
976 changeStatusMessage(item, _("Error : Connection closed."));
977 //fprintf(stderr, "<< STOP >>\n");
978 return 0; /* error or term */
983 void writecmd(monitoritem* item, const char* command) {
985 item->D_sock->msglen = strlen(command);
986 pm_strcpy(&item->D_sock->msg, command);
987 bnet_send(item->D_sock);
991 /* Note: Does not seem to work either on Gnome nor KDE... */
992 void trayMessage(const char *fmt,...) {
996 va_start(arg_ptr, fmt);
997 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1000 fprintf(stderr, buf);
1002 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1005 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1009 va_start(arg_ptr, fmt);
1010 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1013 gtk_label_set_text(GTK_LABEL(item->label), buf);
1016 void updateStatusIcon(monitoritem* item) {
1020 /* For the current status, select the two worse for blinking,
1021 but never blink a D_Sock == NULL error with idle. */
1022 stateenum state1, state2, oldstate;
1023 gboolean onenull = FALSE;
1027 for (int i = 0; i < nitems; i++) {
1028 if (items[i].D_sock == NULL) onenull = TRUE;
1029 if (items[i].state >= state1) {
1031 state1 = items[i].state;
1033 else if (items[i].state > state2) {
1034 state2 = items[i].state;
1036 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1039 if ((onenull == TRUE) && (state2 == idle)) {
1043 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1046 if ((blinkstate) && (item->D_sock != NULL)) {
1047 if (item->state > 1) { //Warning or error while running
1048 xpm = generateXPM(running, item->oldstate);
1051 xpm = generateXPM(idle, item->oldstate);
1055 xpm = generateXPM(item->state, item->oldstate);
1059 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1061 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1062 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1065 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1067 g_object_unref(G_OBJECT(pixbuf));
1070 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1071 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1072 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1075 strcpy(address, "ff0000");
1078 strcpy(address, "ffffff");
1081 strcpy(address, "00ff00");
1084 strcpy(address, "ffff00");
1088 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1091 strcpy(address, "ff0000");
1094 strcpy(address, "ffffff");
1097 strcpy(address, "00ff00");
1100 strcpy(address, "ffff00");
1104 return (const char**)xpm_generic_var;