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