]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tray-monitor/tray-monitor.c
- Add default Console resource in src/dird/bacula-dir.conf.in with
[bacula/bacula] / bacula / src / tray-monitor / tray-monitor.c
1 /*
2  *
3  *   Bacula Gnome Tray Monitor
4  *
5  *     Nicolas Boichat, August MMIV
6  *
7  *     Version $Id$
8  */
9
10 /*
11    Copyright (C) 2004 Kern Sibbald and John Walker
12
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.
17
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.
22
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,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "tray-monitor.h"
32
33 #include "eggstatusicon.h"
34
35 #include "idle.xpm"
36 #include "error.xpm"
37 #include "running.xpm"
38 //#include "saving.xpm"
39 #include "warn.xpm"
40
41 /* Imported functions */
42 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
43 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
44
45 /* Forward referenced functions */
46 void writecmd(monitoritem* item, const char* command);
47 int docmd(monitoritem* item, const char* command, GSList** list);
48 stateenum getstatus(monitoritem* item, int current, GString** str);
49
50 /* Static variables */
51 static char *configfile = NULL;
52 static MONITOR *monitor;
53 static POOLMEM *args;
54 static JCR jcr;
55 static int nitems = 0;
56 static int fullitem = -1; //Item to be display in detailled status window
57 static int lastupdated = -1; //Last item updated
58 static monitoritem items[32];
59
60 /* Data received from FD/SD */
61 static char OKqstatus[]   = "2000 OK .status\n";
62 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
63
64 /* UI variables and functions */
65
66 stateenum currentstatus = warn;
67
68 static gboolean fd_read(gpointer data);
69 void trayMessage(const char *fmt,...);
70 void changeStatus(monitoritem* item, stateenum status);
71 void changeStatusMessage(monitoritem* item, const char *fmt,...);
72
73 /* Callbacks */
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 gboolean delete_event(GtkWidget *widget, GdkEvent  *event, gpointer   data);
79
80 static gint timerTag;
81 static EggStatusIcon *mTrayIcon;
82 static GtkWidget *mTrayMenu;
83 static GtkWidget *window;
84 static GtkWidget *textview;
85 static GtkTextBuffer *buffer;
86
87 #define CONFIG_FILE "./tray-monitor.conf"   /* default configuration file */
88
89 static void usage()
90 {
91    fprintf(stderr, _(
92 "Copyright (C) 2000-2004 Kern Sibbald and John Walker\n"
93 "Written by Nicolas Boichat (2004)\n"
94 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
95 "Usage: tray-monitor [-c config_file] [-d debug_level]\n"
96 "       -c <file>     set configuration file to file\n"
97 "       -dnn          set debug level to nn\n"
98 "       -t            test - read configuration and exit\n"
99 "       -?            print this message.\n"  
100 "\n"), HOST_OS, DISTNAME, DISTVER);
101 }
102
103 static GtkWidget *new_image_button(const gchar *stock_id, 
104                                    const gchar *label_text) {
105     GtkWidget *button;
106     GtkWidget *box;
107     GtkWidget *label;
108     GtkWidget *image;
109
110     button = gtk_button_new();
111    
112     box = gtk_hbox_new(FALSE, 0);
113     gtk_container_set_border_width(GTK_CONTAINER(box), 2);
114     image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
115     label = gtk_label_new(label_text);
116     
117     gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
118     gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
119
120     gtk_widget_show(image);
121     gtk_widget_show(label);
122
123     gtk_widget_show(box);
124
125     gtk_container_add(GTK_CONTAINER(button), box);
126     
127     return button;
128 }
129
130 /*********************************************************************
131  *
132  *         Main Bacula Tray Monitor -- User Interface Program
133  *
134  */
135 int main(int argc, char *argv[])
136 {
137    int ch, i;
138    bool test_config = false;
139    CLIENT* filed;
140    STORE* stored;
141
142    init_stack_dump();
143    my_name_is(argc, argv, "tray-monitor");
144    textdomain("bacula");
145    init_msg(NULL, NULL);
146    working_directory = "/tmp";
147    args = get_pool_memory(PM_FNAME);
148
149    while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
150       switch (ch) {
151       case 'c':                    /* configuration file */
152          if (configfile != NULL) {
153             free(configfile);
154          }
155          configfile = bstrdup(optarg);
156          break;
157
158       case 'd':
159          debug_level = atoi(optarg);
160          if (debug_level <= 0) {
161             debug_level = 1;
162          }
163          break;
164
165       case 't':
166          test_config = true;
167          break;
168
169       case 'h':
170       case '?':
171       default:
172          usage();
173          exit(1);
174       }  
175    }
176    argc -= optind;
177    argv += optind;
178
179    if (argc) {
180       usage();
181       exit(1);
182    }
183
184    if (configfile == NULL) {
185       configfile = bstrdup(CONFIG_FILE);
186    }
187
188    parse_config(configfile);
189    
190    LockRes();
191    nitems = 0;
192    foreach_res(filed, R_CLIENT) {
193       items[nitems].type = R_CLIENT;
194       items[nitems].resource = filed;
195       items[nitems].D_sock = NULL;
196       items[nitems].state = warn;
197       nitems++;
198    }
199    foreach_res(stored, R_STORAGE) {
200       items[nitems].type = R_STORAGE;
201       items[nitems].resource = stored;
202       items[nitems].D_sock = NULL;
203       items[nitems].state = warn;
204       nitems++;
205    }
206    UnlockRes();
207      
208    if (nitems == 0) {
209       Emsg1(M_ERROR_TERM, 0, _("No Client nor Storage resource defined in %s\n\
210 Without that I don't how to get status from the File or Storage Daemon :-(\n"), configfile);
211    }
212
213    if (test_config) {
214       exit(0);
215    }
216    
217    (void)WSA_Init();                        /* Initialize Windows sockets */
218
219    LockRes();
220    monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
221    UnlockRes();
222    
223    gtk_init (&argc, &argv);
224    
225    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm_warn);
226    // This should be ideally replaced by a completely libpr0n-based icon rendering.
227    mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
228    g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
229    g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
230    g_object_unref(G_OBJECT(pixbuf));
231
232    mTrayMenu = gtk_menu_new();
233    
234    GtkWidget *entry;
235    
236    entry = gtk_menu_item_new_with_label("Open status window...");
237    g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
238    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
239       
240    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
241    
242    entry = gtk_menu_item_new_with_label("Exit");
243    g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
244    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
245    
246    gtk_widget_show_all(mTrayMenu);
247    
248    timerTag = g_timeout_add( 5000/nitems, fd_read, NULL );
249       
250    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
251    
252    gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
253    
254    g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
255    //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
256    
257    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
258    
259    GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
260    
261    /*textview = gtk_text_view_new();
262
263    buffer = gtk_text_buffer_new(NULL);
264
265    gtk_text_buffer_set_text(buffer, "", -1);
266
267    PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
268    gtk_widget_modify_font(textview, font_desc);
269    pango_font_description_free(font_desc);
270    
271    gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
272    gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
273    
274    gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
275    
276    gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
277    
278    gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, FALSE, 0);*/
279    
280    GtkWidget* daemon_table = gtk_table_new((nitems*2)+1, 3, FALSE);
281    
282    gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
283
284    GtkWidget* separator = gtk_hseparator_new();
285    gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
286       
287    GString *str;   
288    for (int i = 0; i < nitems; i++) {
289       switch (items[i].type) {
290       case R_CLIENT:
291          str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
292          g_string_append(str, _(" (FD)"));
293          break;
294       case R_STORAGE:
295          str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
296          g_string_append(str, _(" (SD)"));
297          break;
298       default:
299          continue;
300       }
301       
302       GtkWidget* label = gtk_label_new(str->str);
303       GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm_warn);
304       items[i].image = gtk_image_new_from_pixbuf(pixbuf);
305       
306       items[i].label =  gtk_label_new(_("Unknown status."));
307       
308       gtk_table_attach_defaults(GTK_TABLE(daemon_table), label, 0, 1, (i*2)+1, (i*2)+2);
309       gtk_table_attach_defaults(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2);
310       gtk_table_attach_defaults(GTK_TABLE(daemon_table), items[i].label, 2, 3, (i*2)+1, (i*2)+2);
311    
312       GtkWidget* separator = gtk_hseparator_new();
313       gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
314    }
315    
316    gtk_box_pack_start(GTK_BOX(vbox), daemon_table, TRUE, FALSE, 0);
317    
318    GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
319          
320    GtkWidget* button = new_image_button("gtk-help", "About");
321    g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
322    
323    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
324    
325    button = new_image_button("gtk-close", "Close");
326    g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
327    
328    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
329    
330    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
331    
332    gtk_container_add(GTK_CONTAINER (window), vbox);
333    
334    gtk_widget_show_all(vbox);
335    
336    gtk_main();
337       
338    for (i = 0; i < nitems; i++) {
339       if (items[i].D_sock) {
340          writecmd(&items[i], "quit");
341          bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
342          bnet_close(items[i].D_sock);
343       }
344    }
345
346    free_pool_memory(args);
347    (void)WSACleanup();               /* Cleanup Windows sockets */
348    return 0;
349 }
350
351 static void MonitorAbout(GtkWidget *widget, gpointer data) {
352 #if HAVE_GTK_2_4
353    GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
354       "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
355       "Copyright (C) 2004 Kern Sibbald and John Walker\n"
356       "Written by Nicolas Boichat\n"
357       "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
358    ), HOST_OS, DISTNAME, DISTVER);
359 #else
360    GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
361       "Bacula Tray Monitor\n\n"
362       "Copyright (C) 2004 Kern Sibbald and John Walker\n"
363       "Written by Nicolas Boichat\n"
364       "\nVersion: " VERSION " (" BDATE ") %s %s %s"
365    ), HOST_OS, DISTNAME, DISTVER); 
366 #endif
367    gtk_dialog_run(GTK_DIALOG(about));
368    gtk_widget_destroy(about);
369 }
370
371 static gboolean delete_event( GtkWidget *widget,
372                               GdkEvent  *event,
373                               gpointer   data ) {
374    gtk_widget_hide(window);
375    return TRUE; /* do not destroy the window */
376 }
377
378 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
379     gtk_widget_show(window);
380 }
381
382 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
383   gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
384   gtk_widget_show_all(mTrayMenu);
385 }
386
387 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
388    gtk_main_quit();
389 }
390
391 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
392    switch (item->type) {
393    case R_CLIENT:
394       return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
395       break;
396    case R_STORAGE:
397       return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
398       break;
399    default:
400       printf("Error, currentitem is not a Client or a Storage..\n");
401       gtk_main_quit();
402       return FALSE;
403    }
404 }
405
406 static gboolean fd_read(gpointer data) {
407    GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
408    GtkTextIter start, stop, nstart, nstop;
409
410    GSList *list, *it;
411    stateenum stat = idle, nstat;
412    
413    GString *strlast, *strcurrent;
414    
415    lastupdated++;
416    if (lastupdated == nitems) {
417       lastupdated = 0;
418    }
419    
420    nstat = getstatus(&items[lastupdated], 1, &strcurrent);
421    if (nstat > stat) stat = nstat;
422    nstat = getstatus(&items[lastupdated], 0, &strlast);
423    if (nstat > stat) stat = nstat;
424    
425    changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
426    changeStatus(&items[lastupdated], stat);
427    
428    g_string_free(strcurrent, TRUE);
429    g_string_free(strlast, TRUE);
430    
431    if (lastupdated == fullitem) {
432       docmd(&items[lastupdated], "status", &list);
433       
434       it = list->next;
435       do {
436          gtk_text_buffer_get_end_iter(newbuffer, &stop);
437          gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
438          if (it->data) g_string_free((GString*)it->data, TRUE);
439       } while ((it = it->next) != NULL);
440          
441       /* Keep the selection if necessary */
442       if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
443          gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
444          gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop,  gtk_text_iter_get_offset(&stop ));
445          
446    #if HAVE_GTK_2_4
447          gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
448    #else
449          gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
450          gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
451    #endif
452       }
453    
454       g_object_unref(buffer);
455       
456       buffer = newbuffer;
457       gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
458    }
459       
460    return 1;
461 }
462
463 stateenum getstatus(monitoritem* item, int current, GString** str) {
464    GSList *list, *it;
465    stateenum ret = error;
466    int jobid = 0, joberrors = 0;
467    char jobstatus = JS_ErrorTerminated;
468    int k;
469    
470    *str = g_string_sized_new(128);
471    
472    if (current) {
473       docmd(&items[lastupdated], ".status current", &list);
474    }
475    else {
476       docmd(&items[lastupdated], ".status last", &list);
477    }
478
479    it = list->next;
480    if ((it == NULL) || (strcmp(((GString*)it->data)->str, OKqstatus) != 0)) {
481       g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
482       ret = error;
483    }
484    else if ((it = it->next) == NULL) {
485       if (current) {
486          g_string_append(*str, _("No current job."));
487       }
488       else {
489          g_string_append(*str, _("No last job."));
490       }
491       ret = idle;
492    }
493    else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
494       switch (jobstatus) {
495       case JS_Created:
496          ret = (joberrors > 0) ? warn : running;
497          g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
498          break;
499       case JS_Running:
500          ret = (joberrors > 0) ? warn : running;
501          g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
502          break;
503       case JS_Error:
504          ret = (joberrors > 0) ? warn : running;
505          g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
506          break;
507       case JS_Terminated:
508          g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
509          ret = (joberrors > 0) ? warn : idle;
510          break;
511       case JS_ErrorTerminated:
512          g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
513          ret = error;
514          break;
515       case JS_FatalError:
516          g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
517          ret = error;
518          break;
519       case JS_Blocked:
520          g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
521          ret = warn;
522          break;
523       case JS_Canceled:
524          g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
525          ret = warn;
526          break;
527       default:
528          g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
529          ret = warn;
530          break;
531       }
532    }
533    else {
534       fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
535       ret = error;
536    }
537       
538    it = list;
539    do {
540       if (it->data) g_string_free((GString*)it->data, TRUE);
541    } while ((it = it->next) != NULL);
542    
543    g_slist_free(list);
544    
545    return ret;
546 }
547
548 int docmd(monitoritem* item, const char* command, GSList** list) {
549    int stat;
550    GString* str = NULL;
551    
552    *list = g_slist_alloc();
553
554    //str = g_string_sized_new(64);
555    
556    if (!item->D_sock) {
557       memset(&jcr, 0, sizeof(jcr));
558       
559       CLIENT* filed;
560       STORE* stored;
561       
562       switch (item->type) {
563       case R_CLIENT:
564          filed = (CLIENT*)item->resource;      
565          trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
566          changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
567          item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
568          jcr.file_bsock = item->D_sock;
569          break;
570       case R_STORAGE:
571          stored = (STORE*)item->resource;      
572          trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
573          changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
574          item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
575          jcr.store_bsock = item->D_sock;
576          break;
577       default:
578          printf("Error, currentitem is not a Client or a Storage..\n");
579          gtk_main_quit();
580          return 0;
581       }
582       
583       if (item->D_sock == NULL) {
584          g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
585          changeStatusMessage(item, "Cannot connect to daemon.");
586          changeStatus(NULL, error);
587          return 0;
588       }
589       
590       if (!authenticate_daemon(item, &jcr)) {
591          str = g_string_sized_new(64);
592          g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
593          g_slist_append(*list, str);
594          changeStatus(NULL, error);
595          changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
596          item->D_sock = NULL;
597          return 0;
598       }
599    
600       trayMessage("Opened connection with File daemon.\n");
601       changeStatusMessage(item, "Opened connection with File daemon.");
602    }
603       
604    writecmd(item, command);
605    
606    while(1) {
607       if ((stat = bnet_recv(item->D_sock)) >= 0) {
608          g_slist_append(*list, g_string_new(item->D_sock->msg));
609       }
610       else if (stat == BNET_SIGNAL) {
611          if (item->D_sock->msglen == BNET_EOD) {
612             return 1;
613          }
614          else if (item->D_sock->msglen == BNET_HEARTBEAT) {
615             bnet_sig(item->D_sock, BNET_HB_RESPONSE);
616             g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
617          }
618          else {
619             str = g_string_sized_new(64);
620             g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
621             g_slist_append(*list, str);
622          }
623       }
624       else { /* BNET_HARDEOF || BNET_ERROR */
625          g_slist_append(*list, g_string_new("<ERROR>\n"));
626          item->D_sock = NULL;
627          changeStatus(NULL, error);
628          changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
629          return 0;
630       }
631            
632       if (is_bnet_stop(item->D_sock)) {
633          g_string_append_printf(str, "<STOP>\n");
634          item->D_sock = NULL;
635          changeStatus(NULL, error);
636          changeStatusMessage(item, "Error : Connection closed.");
637          return 0;            /* error or term */
638       }
639    }
640 }
641
642 void writecmd(monitoritem* item, const char* command) {
643    if (item->D_sock) {
644       item->D_sock->msglen = strlen(command);
645       pm_strcpy(&item->D_sock->msg, command);
646       bnet_send(item->D_sock);
647    }
648 }
649
650 /* Note: Does not seem to work either on Gnome nor KDE... */
651 void trayMessage(const char *fmt,...) {
652    char buf[512];
653    va_list arg_ptr;
654    
655    va_start(arg_ptr, fmt);
656    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
657    va_end(arg_ptr);
658    
659    fprintf(stderr, buf);
660    
661    egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
662 }
663
664 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
665    char buf[512];
666    va_list arg_ptr;
667    
668    va_start(arg_ptr, fmt);
669    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
670    va_end(arg_ptr);
671
672    gtk_label_set_text(GTK_LABEL(item->label), buf);
673 }
674
675 void changeStatus(monitoritem* item, stateenum status) {
676    if (item == NULL) {
677       if (status == currentstatus)
678          return;
679    }
680    else {
681       if (status == item->state)
682          return;
683    }
684
685    const char** xpm;
686
687    switch (status) {
688    case error:
689       xpm = (const char**)&xpm_error;
690       break;
691    case idle:
692       xpm = (const char**)&xpm_idle;
693       break;
694    case running:
695       xpm = (const char**)&xpm_running;
696       break;
697 /*   case saving:
698       xpm = (const char**)&xpm_saving;
699       break;*/
700    case warn:
701       xpm = (const char**)&xpm_warn;
702       break;
703    default:
704       xpm = NULL;
705       break;
706    }
707    
708    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
709    if (item == NULL) {
710       egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
711       gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
712       currentstatus = status;
713    }
714    else {
715       gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
716       item->state = status;
717    }
718 }