]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tray-monitor/tray-monitor.c
- Add spin button to set refresh interval.
[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 "generic.xpm"
36
37 /* Imported functions */
38 int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director);
39 int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client);
40 int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store);
41
42 /* Forward referenced functions */
43 void writecmd(monitoritem* item, const char* command);
44 int docmd(monitoritem* item, const char* command, GSList** list);
45 void getstatus(monitoritem* item, int current, GString** str);
46
47 /* Static variables */
48 static char *configfile = NULL;
49 static MONITOR *monitor;
50 static POOLMEM *args;
51 static JCR jcr;
52 static int nitems = 0;
53 static int fullitem = 0; //Item to be display in detailled status window
54 static int lastupdated = -1; //Last item updated
55 static monitoritem items[32];
56
57 /* Data received from DIR/FD/SD */
58 static char OKqstatus[]   = "%c000 OK .status\n";
59 static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
60
61 /* UI variables and functions */
62
63 static gboolean fd_read(gpointer data);
64 void trayMessage(const char *fmt,...);
65 void updateStatusIcon(monitoritem* item);
66 void changeStatusMessage(monitoritem* item, const char *fmt,...);
67 static const char** generateXPM(stateenum newstate, stateenum oldstate);
68
69 /* Callbacks */
70 static void TrayIconActivate(GtkWidget *widget, gpointer data);
71 static void TrayIconExit(unsigned int activateTime, unsigned int button);
72 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
73 static void MonitorAbout(GtkWidget *widget, gpointer data);
74 static void MonitorRefresh(GtkWidget *widget, gpointer data);
75 static void IntervalChanged(GtkWidget *widget, gpointer data);
76 static void DaemonChanged(GtkWidget *widget, monitoritem* data);
77 static gboolean delete_event(GtkWidget *widget, GdkEvent  *event, gpointer   data);
78
79 static guint timerTag;
80 static EggStatusIcon *mTrayIcon;
81 static GtkWidget *mTrayMenu;
82 static GtkWidget *window;
83 static GtkWidget *textview;
84 static GtkTextBuffer *buffer;
85 static GtkWidget *timeoutspinner;
86 char** xpm_generic_var;
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    //Copy the content of xpm_generic in xpm_generic_var to be able to modify it
227    xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic));
228    for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
229       xpm_generic_var[i] = (char*)g_malloc(strlen(xpm_generic[i])*sizeof(char));
230       strcpy(xpm_generic_var[i], xpm_generic[i]);
231    }
232    
233    (void)WSA_Init();                /* Initialize Windows sockets */
234
235    LockRes();
236    monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
237    UnlockRes();
238    
239    gtk_init (&argc, &argv);
240    
241    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
242    // This should be ideally replaced by a completely libpr0n-based icon rendering.
243    mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
244    g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
245    g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
246    g_object_unref(G_OBJECT(pixbuf));
247
248    mTrayMenu = gtk_menu_new();
249    
250    GtkWidget *entry;
251    
252    entry = gtk_menu_item_new_with_label("Open status window...");
253    g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
254    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
255       
256    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
257    
258    entry = gtk_menu_item_new_with_label("Exit");
259    g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
260    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
261    
262    gtk_widget_show_all(mTrayMenu);
263    
264    timerTag = g_timeout_add( 2000, fd_read, NULL );
265       
266    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
267    
268    gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
269    
270    g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
271    //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
272    
273    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
274    
275    GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
276    
277    GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE);
278    
279    gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8);
280    
281    GtkWidget* separator = gtk_hseparator_new();
282    gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1);
283       
284    GString *str;
285    GSList *group = NULL;
286    GtkWidget* radio;
287    GtkWidget* align;
288    
289    for (int i = 0; i < nitems; i++) {
290       switch (items[i].type) {
291       case R_DIRECTOR:
292          str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name);
293          g_string_append(str, _(" (DIR)"));
294          break;
295       case R_CLIENT:
296          str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
297          g_string_append(str, _(" (FD)"));
298          break;
299       case R_STORAGE:
300          str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
301          g_string_append(str, _(" (SD)"));
302          break;
303       default:
304          continue;
305       }
306       
307       radio = gtk_radio_button_new_with_label(group, str->str);
308       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0);
309       g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i]));
310       
311       pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn));
312       items[i].image = gtk_image_new_from_pixbuf(pixbuf);
313       
314       items[i].label =  gtk_label_new(_("Unknown status."));
315       align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
316       gtk_container_add(GTK_CONTAINER(align), items[i].label);
317             
318       gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2, 
319          GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
320       gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2, 
321          GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
322       gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2, 
323          (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0);
324    
325       separator = gtk_hseparator_new();
326       gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3);
327       
328       group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
329    }
330    
331    gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0);
332    
333    textview = gtk_text_view_new();
334
335    buffer = gtk_text_buffer_new(NULL);
336
337    gtk_text_buffer_set_text(buffer, "", -1);
338
339    PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
340    gtk_widget_modify_font(textview, font_desc);
341    pango_font_description_free(font_desc);
342    
343    gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
344    gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
345    
346    gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
347    
348    gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
349    
350    gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0);
351    
352    GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
353
354    GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0);
355    GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: "));
356    gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0);
357    GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (nitems*2, nitems, 120.0, 1.0, 5.0, 5.0);
358    timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0);
359    g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL);
360    gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0);
361    gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0);
362    
363    GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now"));
364    g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL);
365    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
366    
367    button = new_image_button("gtk-help", _("About"));
368    g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
369    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
370    
371    button = new_image_button("gtk-close", _("Close"));
372    g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window)); 
373    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
374    
375    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
376    
377    gtk_container_add(GTK_CONTAINER (window), vbox);
378    
379    gtk_widget_show_all(vbox);
380    
381    gtk_main();
382       
383    for (i = 0; i < nitems; i++) {
384       if (items[i].D_sock) {
385          writecmd(&items[i], "quit");
386          bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */
387          bnet_close(items[i].D_sock);
388       }
389    }
390
391    free_pool_memory(args);
392    (void)WSACleanup();               /* Cleanup Windows sockets */
393    
394    //Free xpm_generic_var
395    for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) {
396       g_free(xpm_generic_var[i]);
397    }
398    g_free(xpm_generic_var);
399    
400    return 0;
401 }
402
403 static void MonitorAbout(GtkWidget *widget, gpointer data) {
404 #if HAVE_GTK_2_4
405    GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
406       "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
407       "Copyright (C) 2004 Kern Sibbald and John Walker\n"
408       "Written by Nicolas Boichat\n"
409       "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
410    ), HOST_OS, DISTNAME, DISTVER);
411 #else
412    GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
413       "Bacula Tray Monitor\n\n"
414       "Copyright (C) 2004 Kern Sibbald and John Walker\n"
415       "Written by Nicolas Boichat\n"
416       "\nVersion: " VERSION " (" BDATE ") %s %s %s"
417    ), HOST_OS, DISTNAME, DISTVER); 
418 #endif
419    gtk_dialog_run(GTK_DIALOG(about));
420    gtk_widget_destroy(about);
421 }
422
423 static void MonitorRefresh(GtkWidget *widget, gpointer data) {
424    for (int i = 0; i < nitems; i++) {
425       fd_read(NULL);
426    }
427 }
428
429 static gboolean delete_event( GtkWidget *widget,
430                               GdkEvent  *event,
431                               gpointer   data ) {
432    gtk_widget_hide(window);
433    return TRUE; /* do not destroy the window */
434 }
435
436 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
437     gtk_widget_show(window);
438 }
439
440 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
441   gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
442   gtk_widget_show_all(mTrayMenu);
443 }
444
445 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
446    gtk_main_quit();
447 }
448
449 static void IntervalChanged(GtkWidget *widget, gpointer data) {
450    g_source_remove(timerTag);
451    timerTag = g_timeout_add(
452       (guint)(
453          gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems
454       ), fd_read, NULL );
455 }
456
457 static void DaemonChanged(GtkWidget *widget, monitoritem* data) {
458    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
459       fullitem = -1;
460       for (int i = 0; i < nitems; i++) {
461          if (data == &(items[i])) {
462             fullitem = i;
463             break;
464          }
465       }
466       g_return_if_fail(fullitem != -1);
467
468       int oldlastupdated = lastupdated;
469       lastupdated = fullitem-1;
470       fd_read(NULL);
471       lastupdated = oldlastupdated;
472    }
473 }
474
475 static int authenticate_daemon(monitoritem* item, JCR *jcr) {
476    switch (item->type) {
477    case R_DIRECTOR:
478       return authenticate_director(jcr, monitor, (DIRRES*)item->resource);
479       break;      
480    case R_CLIENT:
481       return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource);
482       break;
483    case R_STORAGE:
484       return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource);
485       break;
486    default:
487       printf("Error, currentitem is not a Client or a Storage..\n");
488       gtk_main_quit();
489       return FALSE;
490    }
491 }
492
493 static gboolean fd_read(gpointer data) {
494    GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
495    GtkTextIter start, stop, nstart, nstop;
496
497    GSList *list, *it;
498    
499    GString *strlast, *strcurrent;
500    
501    lastupdated++;
502    if (lastupdated == nitems) {
503       lastupdated = 0;
504    }
505      
506    if (lastupdated == fullitem) {
507       if (items[lastupdated].type == R_DIRECTOR)
508          docmd(&items[lastupdated], "status Director\n", &list);
509       else
510          docmd(&items[lastupdated], "status\n", &list);
511       
512       it = list->next;
513       do {
514          gtk_text_buffer_get_end_iter(newbuffer, &stop);
515          gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1);
516          if (it->data) g_string_free((GString*)it->data, TRUE);
517       } while ((it = it->next) != NULL);
518          
519       /* Keep the selection if necessary */
520       if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
521          gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
522          gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop,  gtk_text_iter_get_offset(&stop ));
523          
524    #if HAVE_GTK_2_4
525          gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
526    #else
527          gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
528          gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
529    #endif
530       }
531    
532       g_object_unref(buffer);
533       
534       buffer = newbuffer;
535       gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
536    }
537    
538    getstatus(&items[lastupdated], 1, &strcurrent);
539    getstatus(&items[lastupdated], 0, &strlast);
540    updateStatusIcon(&items[lastupdated]);
541      
542    changeStatusMessage(&items[lastupdated], "Current job: %s\nLast job: %s", strcurrent->str, strlast->str);
543    
544    updateStatusIcon(NULL);
545    
546    g_string_free(strcurrent, TRUE);
547    g_string_free(strlast, TRUE);
548       
549    return 1;
550 }
551
552 void getstatus(monitoritem* item, int current, GString** str) {
553    GSList *list, *it;
554    stateenum ret = error;
555    int jobid = 0, joberrors = 0;
556    char jobstatus = JS_ErrorTerminated;
557    char num;
558    int k;
559    
560    *str = g_string_sized_new(128);
561    
562    if (current) {
563       if (item->type == R_DIRECTOR)
564          docmd(&items[lastupdated], ".status dir current\n", &list);
565       else
566          docmd(&items[lastupdated], ".status current\n", &list);
567    }
568    else {
569       if (item->type == R_DIRECTOR)
570          docmd(&items[lastupdated], ".status dir last\n", &list);
571       else
572          docmd(&items[lastupdated], ".status last\n", &list);
573    }
574
575    it = list->next;
576    if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) {
577       g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str);
578       ret = error;
579    }
580    else if ((it = it->next) == NULL) {
581       if (current) {
582          g_string_append(*str, _("No current job."));
583       }
584       else {
585          g_string_append(*str, _("No last job."));
586       }
587       ret = idle;
588    }
589    else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) {
590       switch (jobstatus) {
591       case JS_Created:
592          ret = (joberrors > 0) ? warn : running;
593          g_string_append_printf(*str, _("Job status: Created (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
594          break;
595       case JS_Running:
596          ret = (joberrors > 0) ? warn : running;
597          g_string_append_printf(*str, _("Job status: Running (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
598          break;
599       case JS_Error:
600          ret = (joberrors > 0) ? warn : running;
601          g_string_append_printf(*str, _("Job status: Error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
602          break;
603       case JS_Terminated:
604          g_string_append_printf(*str, _("Job status: Terminated (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
605          ret = (joberrors > 0) ? warn : idle;
606          break;
607       case JS_ErrorTerminated:
608          g_string_append_printf(*str, _("Job status: Terminated in error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
609          ret = error;
610          break;
611       case JS_FatalError:
612          g_string_append_printf(*str, _("Job status: Fatal error (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
613          ret = error;
614          break;
615       case JS_Blocked:
616          g_string_append_printf(*str, _("Job status: Blocked (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
617          ret = warn;
618          break;
619       case JS_Canceled:
620          g_string_append_printf(*str, _("Job status: Canceled (%d error%s)"), joberrors, (joberrors > 1) ? "s" : "");
621          ret = warn;
622          break;
623       default:
624          g_string_append_printf(*str, _("Job status: Unknown(%c) (%d error%s)"), jobstatus, joberrors, (joberrors > 1) ? "s" : "");
625          ret = warn;
626          break;
627       }
628    }
629    else {
630       fprintf(stderr, "Bad scan : '%s' %d\n", (it == NULL) ? "" : ((GString*)it->data)->str, k);
631       ret = error;
632    }
633       
634    it = list;
635    do {
636       if (it->data) g_string_free((GString*)it->data, TRUE);
637    } while ((it = it->next) != NULL);
638    
639    g_slist_free(list);
640    
641    if (current) {
642       item->state = ret;
643    }
644    else {
645       item->oldstate = ret;
646    }
647 }
648
649 int docmd(monitoritem* item, const char* command, GSList** list) {
650    int stat;
651    GString* str = NULL;
652    
653    *list = g_slist_alloc();
654
655    //str = g_string_sized_new(64);
656    
657    if (!item->D_sock) {
658       memset(&jcr, 0, sizeof(jcr));
659       
660       DIRRES* dird;
661       CLIENT* filed;
662       STORE* stored;
663       
664       switch (item->type) {
665       case R_DIRECTOR:
666          dird = (DIRRES*)item->resource;      
667          trayMessage("Connecting to Director %s:%d\n", dird->address, dird->DIRport);
668          changeStatusMessage(item, "Connecting to Director %s:%d", dird->address, dird->DIRport);
669          item->D_sock = bnet_connect(NULL, 0, 0, "Director daemon", dird->address, NULL, dird->DIRport, 0);
670          jcr.dir_bsock = item->D_sock;
671          break;
672       case R_CLIENT:
673          filed = (CLIENT*)item->resource;      
674          trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
675          changeStatusMessage(item, "Connecting to Client %s:%d", filed->address, filed->FDport);
676          item->D_sock = bnet_connect(NULL, 0, 0, "File daemon", filed->address, NULL, filed->FDport, 0);
677          jcr.file_bsock = item->D_sock;
678          break;
679       case R_STORAGE:
680          stored = (STORE*)item->resource;      
681          trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
682          changeStatusMessage(item, "Connecting to Storage %s:%d", stored->address, stored->SDport);
683          item->D_sock = bnet_connect(NULL, 0, 0, "Storage daemon", stored->address, NULL, stored->SDport, 0);
684          jcr.store_bsock = item->D_sock;
685          break;
686       default:
687          printf("Error, currentitem is not a Client, a Storage or a Director..\n");
688          gtk_main_quit();
689          return 0;
690       }
691       
692       if (item->D_sock == NULL) {
693          g_slist_append(*list, g_string_new("Cannot connect to daemon.\n"));
694          changeStatusMessage(item, "Cannot connect to daemon.");
695          item->state = error;
696          item->oldstate = error;
697          return 0;
698       }
699       
700       if (!authenticate_daemon(item, &jcr)) {
701          str = g_string_sized_new(64);
702          g_string_printf(str, "ERR=%s\n", item->D_sock->msg);
703          g_slist_append(*list, str);
704          item->state = error;
705          item->oldstate = error;
706          changeStatusMessage(item, "Authentication error : %s", item->D_sock->msg);
707          item->D_sock = NULL;
708          return 0;
709       }
710       
711       switch (item->type) {
712       case R_DIRECTOR:
713          trayMessage("Opened connection with Director daemon.\n");
714          changeStatusMessage(item, "Opened connection with Director daemon.");
715          break;
716       case R_CLIENT:
717          trayMessage("Opened connection with File daemon.\n");
718          changeStatusMessage(item, "Opened connection with File daemon.");
719          break;
720       case R_STORAGE:
721          trayMessage("Opened connection with Storage daemon.\n");
722          changeStatusMessage(item, "Opened connection with Storage daemon.");
723          break;
724       default:
725          printf("Error, currentitem is not a Client, a Storage or a Director..\n");
726          gtk_main_quit();
727          return 0;
728          break;
729       }
730       
731       if (item->type == R_DIRECTOR) { /* Read connection messages... */
732          GSList *list, *it;
733          docmd(item, "", &list); /* Usually invalid, but no matter */
734          it = list;
735          do {
736             if (it->data) g_string_free((GString*)it->data, TRUE);
737          } while ((it = it->next) != NULL);
738          
739          g_slist_free(list);         
740       }
741    }
742    
743    if (command[0] != 0)
744       writecmd(item, command);
745    
746    while(1) {
747       if ((stat = bnet_recv(item->D_sock)) >= 0) {
748          g_slist_append(*list, g_string_new(item->D_sock->msg));
749       }
750       else if (stat == BNET_SIGNAL) {
751          if (item->D_sock->msglen == BNET_EOD) {
752             //fprintf(stderr, "<< EOD >>\n");
753             return 1;
754          }
755          else if (item->D_sock->msglen == BNET_PROMPT) {
756             //fprintf(stderr, "<< PROMPT >>\n");
757             g_slist_append(*list, g_string_new("<< Error: BNET_PROMPT signal received. >>\n"));
758             return 0;
759          }
760          else if (item->D_sock->msglen == BNET_HEARTBEAT) {
761             bnet_sig(item->D_sock, BNET_HB_RESPONSE);
762             g_slist_append(*list, g_string_new("<< Heartbeat signal received, answered. >>\n"));
763          }
764          else {
765             str = g_string_sized_new(64);
766             g_string_printf(str, "<< Unexpected signal received : %s >>\n", bnet_sig_to_ascii(item->D_sock));
767             g_slist_append(*list, str);
768          }
769       }
770       else { /* BNET_HARDEOF || BNET_ERROR */
771          g_slist_append(*list, g_string_new("<ERROR>\n"));
772          item->D_sock = NULL;
773          item->state = error;
774          item->oldstate = error;
775          changeStatusMessage(item, "Error : BNET_HARDEOF or BNET_ERROR");
776          //fprintf(stderr, "<< ERROR >>\n");
777          return 0;
778       }
779            
780       if (is_bnet_stop(item->D_sock)) {
781          g_string_append_printf(str, "<STOP>\n");
782          item->D_sock = NULL;
783          item->state = error;
784          item->oldstate = error;
785          changeStatusMessage(item, "Error : Connection closed.");
786          //fprintf(stderr, "<< STOP >>\n");
787          return 0;            /* error or term */
788       }
789    }
790 }
791
792 void writecmd(monitoritem* item, const char* command) {
793    if (item->D_sock) {
794       item->D_sock->msglen = strlen(command);
795       pm_strcpy(&item->D_sock->msg, command);
796       bnet_send(item->D_sock);
797    }
798 }
799
800 /* Note: Does not seem to work either on Gnome nor KDE... */
801 void trayMessage(const char *fmt,...) {
802    char buf[512];
803    va_list arg_ptr;
804    
805    va_start(arg_ptr, fmt);
806    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
807    va_end(arg_ptr);
808    
809    fprintf(stderr, buf);
810    
811    egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
812 }
813
814 void changeStatusMessage(monitoritem* item, const char *fmt,...) {
815    char buf[512];
816    va_list arg_ptr;
817    
818    va_start(arg_ptr, fmt);
819    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
820    va_end(arg_ptr);
821
822    gtk_label_set_text(GTK_LABEL(item->label), buf);
823 }
824
825 void updateStatusIcon(monitoritem* item) {
826    const char** xpm;
827    
828    if (item == NULL) {
829       stateenum state, oldstate;
830       state = idle;
831       oldstate = idle;
832       for (int i = 0; i < nitems; i++) {
833          if (items[i].state > state) state = items[i].state;
834          if (items[i].oldstate > oldstate) oldstate = items[i].oldstate;
835       }
836       xpm = generateXPM(state, oldstate);
837    }
838    else {
839       xpm = generateXPM(item->state, item->oldstate);
840    }
841    
842    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
843    if (item == NULL) {
844       egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
845       gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
846    }
847    else {
848       gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf);
849    }
850 }
851
852 /* Note: result should not be stored, as it is a reference to xpm_generic_var */
853 static const char** generateXPM(stateenum newstate, stateenum oldstate) {
854    char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column];
855    switch (newstate) {
856    case error:
857       strcpy(address, "ff0000");
858       break;
859    case idle:
860       strcpy(address, "ffffff");
861       break;
862    case running:
863       strcpy(address, "00ff00");
864       break;
865    case warn:
866       strcpy(address, "ffff00");
867       break;
868    }
869    
870    address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column];
871    switch (oldstate) {
872    case error:
873       strcpy(address, "ff0000");
874       break;
875    case idle:
876       strcpy(address, "ffffff");
877       break;
878    case running:
879       strcpy(address, "00ff00");
880       break;
881    case warn:
882       strcpy(address, "ffff00");
883       break;
884    }
885    
886    return (const char**)xpm_generic_var;
887 }