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