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)
602 #if TRAY_DEBUG_MEMORY
603 printf("sm_line=%d\n", sm_line);
605 GtkTextBuffer *newbuffer;
606 GtkTextIter start, stop, nstart, nstop;
610 GString *strlast, *strcurrent;
613 if (lastupdated == nitems) {
617 if (lastupdated == fullitem) {
618 newbuffer = gtk_text_buffer_new(NULL);
620 if (items[lastupdated].type == R_DIRECTOR)
621 docmd(&items[lastupdated], "status Director\n", &list);
623 docmd(&items[lastupdated], "status\n", &list);
627 gtk_text_buffer_get_end_iter(newbuffer, &stop);
628 gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
629 if (it->data) g_string_free((GString*)it->data, TRUE);
630 } while ((it = it->next) != NULL);
634 /* Keep the selection if necessary */
635 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
636 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
637 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
640 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
642 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
643 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
647 g_object_unref(buffer);
650 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
653 getstatus(&items[lastupdated], 1, &strcurrent);
654 getstatus(&items[lastupdated], 0, &strlast);
655 updateStatusIcon(&items[lastupdated]);
657 changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str);
659 updateStatusIcon(NULL);
661 g_string_free(strcurrent, TRUE);
662 g_string_free(strlast, TRUE);
667 void append_error_string(GString* str, int joberrors) {
669 g_string_append_printf(str, _(" (%d errors)"), joberrors);
672 g_string_append_printf(str, _(" (%d error)"), joberrors);
676 void getstatus(monitoritem* item, int current, GString** str)
679 stateenum ret = error;
680 int jobid = 0, joberrors = 0;
681 char jobstatus = JS_ErrorTerminated;
685 *str = g_string_sized_new(128);
688 if (item->type == R_DIRECTOR)
689 docmd(&items[lastupdated], ".status dir current\n", &list);
691 docmd(&items[lastupdated], ".status current\n", &list);
694 if (item->type == R_DIRECTOR)
695 docmd(&items[lastupdated], ".status dir last\n", &list);
697 docmd(&items[lastupdated], ".status last\n", &list);
701 if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
702 g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
703 while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) {
704 g_string_set_size(*str, (*str)->len-1);
708 else if ((it = it->next) == NULL) {
710 g_string_append(*str, _("No current job."));
713 g_string_append(*str, _("No last job."));
717 else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
720 ret = (joberrors > 0) ? warn : running;
721 g_string_append_printf(*str, _("Job status: Created"));
722 append_error_string(*str, joberrors);
725 ret = (joberrors > 0) ? warn : running;
726 g_string_append_printf(*str, _("Job status: Running"));
727 append_error_string(*str, joberrors);
730 g_string_append_printf(*str, _("Job status: Blocked"));
731 append_error_string(*str, joberrors);
735 g_string_append_printf(*str, _("Job status: Terminated"));
736 append_error_string(*str, joberrors);
737 ret = (joberrors > 0) ? warn : idle;
739 case JS_ErrorTerminated:
740 g_string_append_printf(*str, _("Job status: Terminated in error"));
741 append_error_string(*str, joberrors);
745 ret = (joberrors > 0) ? warn : running;
746 g_string_append_printf(*str, _("Job status: Error"));
747 append_error_string(*str, joberrors);
750 g_string_append_printf(*str, _("Job status: Fatal error"));
751 append_error_string(*str, joberrors);
755 g_string_append_printf(*str, _("Job status: Verify differences"));
756 append_error_string(*str, joberrors);
760 g_string_append_printf(*str, _("Job status: Canceled"));
761 append_error_string(*str, joberrors);
765 g_string_append_printf(*str, _("Job status: Waiting on File daemon"));
766 append_error_string(*str, joberrors);
770 g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon"));
771 append_error_string(*str, joberrors);
775 g_string_append_printf(*str, _("Job status: Waiting for new media"));
776 append_error_string(*str, joberrors);
780 g_string_append_printf(*str, _("Job status: Waiting for Mount"));
781 append_error_string(*str, joberrors);
784 case JS_WaitStoreRes:
785 g_string_append_printf(*str, _("Job status: Waiting for storage resource"));
786 append_error_string(*str, joberrors);
790 g_string_append_printf(*str, _("Job status: Waiting for job resource"));
791 append_error_string(*str, joberrors);
794 case JS_WaitClientRes:
795 g_string_append_printf(*str, _("Job status: Waiting for Client resource"));
796 append_error_string(*str, joberrors);
800 g_string_append_printf(*str, _("Job status: Waiting for maximum jobs"));
801 append_error_string(*str, joberrors);
804 case JS_WaitStartTime:
805 g_string_append_printf(*str, _("Job status: Waiting for start time"));
806 append_error_string(*str, joberrors);
809 case JS_WaitPriority:
810 g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish"));
811 append_error_string(*str, joberrors);
815 g_warning(_("Unknown job status %c."), jobstatus);
816 g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus);
817 append_error_string(*str, joberrors);
823 fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k);
830 g_string_free((GString*)it->data, TRUE);
832 } while ((it = it->next) != NULL);
840 item->oldstate = ret;
844 int docmd(monitoritem* item, const char* command, GSList** list)
849 *list = g_slist_alloc();
851 //str = g_string_sized_new(64);
854 memset(&jcr, 0, sizeof(jcr));
860 switch (item->type) {
862 dird = (DIRRES*)item->resource;
863 trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport);
864 changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport);
865 item->D_sock = bnet_connect(NULL, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0);
866 jcr.dir_bsock = item->D_sock;
869 filed = (CLIENT*)item->resource;
870 trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport);
871 changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport);
872 item->D_sock = bnet_connect(NULL, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0);
873 jcr.file_bsock = item->D_sock;
876 stored = (STORE*)item->resource;
877 trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport);
878 changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport);
879 item->D_sock = bnet_connect(NULL, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0);
880 jcr.store_bsock = item->D_sock;
883 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
888 if (item->D_sock == NULL) {
889 *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n")));
890 changeStatusMessage(item, _("Cannot connect to daemon."));
892 item->oldstate = error;
896 if (!authenticate_daemon(item, &jcr)) {
897 str = g_string_sized_new(64);
898 g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
899 *list = g_slist_append(*list, str);
901 item->oldstate = error;
902 changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg);
907 switch (item->type) {
909 trayMessage(_("Opened connection with Director daemon.\n"));
910 changeStatusMessage(item, _("Opened connection with Director daemon."));
913 trayMessage(_("Opened connection with File daemon.\n"));
914 changeStatusMessage(item, _("Opened connection with File daemon."));
917 trayMessage(_("Opened connection with Storage daemon.\n"));
918 changeStatusMessage(item, _("Opened connection with Storage daemon."));
921 printf(_("Error, currentitem is not a Client, a Storage or a Director..\n"));
927 if (item->type == R_DIRECTOR) { /* Read connection messages... */
929 docmd(item, "", &tlist); /* Usually invalid, but no matter */
933 g_string_free((GString*)it->data, TRUE);
935 } while ((it = it->next) != NULL);
942 writecmd(item, command);
945 if ((stat = bnet_recv(item->D_sock)) >= 0) {
946 *list = g_slist_append(*list, g_string_new(item->D_sock->msg));
948 else if (stat == BNET_SIGNAL) {
949 if (item->D_sock->msglen == BNET_EOD) {
950 //fprintf(stderr, "<< EOD >>\n");
953 else if (item->D_sock->msglen == BNET_PROMPT) {
954 //fprintf(stderr, "<< PROMPT >>\n");
955 *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n")));
958 else if (item->D_sock->msglen == BNET_HEARTBEAT) {
959 bnet_sig(item->D_sock, BNET_HB_RESPONSE);
960 *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n")));
963 str = g_string_sized_new(64);
964 g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock));
965 *list = g_slist_append(*list, str);
968 else { /* BNET_HARDEOF || BNET_ERROR */
969 *list = g_slist_append(*list, g_string_new(_("<ERROR>\n")));
972 item->oldstate = error;
973 changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR"));
974 //fprintf(stderr, _("<< ERROR >>\n"));
978 if (is_bnet_stop(item->D_sock)) {
979 g_string_append_printf(str, _("<STOP>\n"));
982 item->oldstate = error;
983 changeStatusMessage(item, _("Error : Connection closed."));
984 //fprintf(stderr, "<< STOP >>\n");
985 return 0; /* error or term */
990 void writecmd(monitoritem* item, const char* command) {
992 item->D_sock->msglen = strlen(command);
993 pm_strcpy(&item->D_sock->msg, command);
994 bnet_send(item->D_sock);
998 /* Note: Does not seem to work either on Gnome nor KDE... */
999 void trayMessage(const char *fmt,...)
1004 va_start(arg_ptr, fmt);
1005 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1008 fprintf(stderr, buf);
1010 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
1013 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
1017 va_start(arg_ptr, fmt);
1018 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
1021 gtk_label_set_text(GTK_LABEL(item->label), buf);
1024 void updateStatusIcon(monitoritem* item) {
1028 /* For the current status, select the two worse for blinking,
1029 but never blink a D_Sock == NULL error with idle. */
1030 stateenum state1, state2, oldstate;
1031 gboolean onenull = FALSE;
1035 for (int i = 0; i < nitems; i++) {
1036 if (items[i].D_sock == NULL) onenull = TRUE;
1037 if (items[i].state >= state1) {
1039 state1 = items[i].state;
1041 else if (items[i].state > state2) {
1042 state2 = items[i].state;
1044 if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
1047 if ((onenull == TRUE) && (state2 == idle)) {
1051 xpm = generateXPM(blinkstate ? state1 : state2, oldstate);
1054 if ((blinkstate) && (item->D_sock != NULL)) {
1055 if (item->state > 1) { //Warning or error while running
1056 xpm = generateXPM(running, item->oldstate);
1059 xpm = generateXPM(idle, item->oldstate);
1063 xpm = generateXPM(item->state, item->oldstate);
1067 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
1069 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
1070 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
1073 gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
1075 g_object_unref(G_OBJECT(pixbuf));
1078 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
1079 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
1080 char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
1083 strcpy(address, "ff0000");
1086 strcpy(address, "ffffff");
1089 strcpy(address, "00ff00");
1092 strcpy(address, "ffff00");
1096 address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
1099 strcpy(address, "ff0000");
1102 strcpy(address, "ffffff");
1105 strcpy(address, "00ff00");
1108 strcpy(address, "ffff00");
1112 return (const char**)xpm_generic_var;