]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tray-monitor/tray-monitor.c
Fix copyright dates.
[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 #include <gtk/gtk.h>
35
36 #include "idle.xpm"
37 #include "error.xpm"
38 #include "running.xpm"
39 #include "saving.xpm"
40 #include "warn.xpm"
41
42 /* Imported functions */
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(const char* command);
48
49 /* Static variables */
50 static char *configfile = NULL;
51 static BSOCK *D_sock = NULL;
52 static MONITOR *monitor;
53 static POOLMEM *args;
54 static JCR jcr;
55 static int nitems = 0;
56 static monitoritem items[32];
57 static monitoritem* currentitem;
58
59 /* UI variables and functions */
60 enum stateenum {
61    error,
62    idle,
63    running,
64    saving,
65    warn
66 };
67
68 stateenum currentstatus = warn;
69
70 static gboolean fd_read(gpointer data);
71 void trayMessage(const char *fmt,...);
72 void changeIcon(stateenum status);
73 void writeToTextBuffer(GtkTextBuffer *buffer, const char *fmt,...);
74
75 /* Callbacks */
76 static void TrayIconDaemonChanged(GtkWidget *widget, monitoritem* data);
77 static void TrayIconActivate(GtkWidget *widget, gpointer data);
78 static void TrayIconExit(unsigned int activateTime, unsigned int button);
79 static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime);
80 static void MonitorAbout(GtkWidget *widget, gpointer data);
81 static gboolean delete_event(GtkWidget *widget, GdkEvent  *event, gpointer   data);
82
83 static gint timerTag;
84 static EggStatusIcon *mTrayIcon;
85 static GtkWidget *mTrayMenu;
86 static GtkWidget *window;
87 static GtkWidget *textview;
88 static GtkTextBuffer *buffer;
89
90 #define CONFIG_FILE "./tray-monitor.conf"   /* default configuration file */
91
92 static void usage()
93 {
94    fprintf(stderr, _(
95 "Copyright (C) 2000-2004 Kern Sibbald and John Walker\n"
96 "Written by Nicolas Boichat (2004)\n"
97 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
98 "Usage: tray-monitor [-c config_file] [-d debug_level] [-f filed | -s stored]\n"
99 "       -c <file>     set configuration file to file\n"
100 "       -dnn          set debug level to nn\n"
101 "       -t            test - read configuration and exit\n"
102 "       -f <filed>    monitor <filed>\n"
103 "       -s <stored> monitor <stored>\n"
104 "       -?            print this message.\n"  
105 "\n"), HOST_OS, DISTNAME, DISTVER);
106 }
107
108 static GtkWidget *new_image_button(const gchar *stock_id, 
109                                    const gchar *label_text) {
110     GtkWidget *button;
111     GtkWidget *box;
112     GtkWidget *label;
113     GtkWidget *image;
114
115     button = gtk_button_new();
116    
117     box = gtk_hbox_new(FALSE, 0);
118     gtk_container_set_border_width(GTK_CONTAINER(box), 2);
119     image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
120     label = gtk_label_new(label_text);
121     
122     gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
123     gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
124
125     gtk_widget_show(image);
126     gtk_widget_show(label);
127
128     gtk_widget_show(box);
129
130     gtk_container_add(GTK_CONTAINER(button), box);
131     
132     return button;
133 }
134
135 /*********************************************************************
136  *
137  *         Main Bacula Tray Monitor -- User Interface Program
138  *
139  */
140 int main(int argc, char *argv[])
141 {
142    int ch;
143    bool test_config = false;
144    char* deffiled = NULL;
145    char* defstored = NULL;
146    CLIENT* filed;
147    STORE* stored;
148
149    init_stack_dump();
150    my_name_is(argc, argv, "tray-monitor");
151    textdomain("bacula");
152    init_msg(NULL, NULL);
153    working_directory = "/tmp";
154    args = get_pool_memory(PM_FNAME);
155
156    while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
157       switch (ch) {
158       case 'c':                    /* configuration file */
159          if (configfile != NULL) {
160             free(configfile);
161          }
162          configfile = bstrdup(optarg);
163          break;
164          
165       case 'f':
166          if ((deffiled != NULL) || (defstored != NULL)) {
167             fprintf(stderr, "Error: You can only use one -f <filed> or -s <stored> parameter\n");
168             exit(1);
169          }
170          deffiled = bstrdup(optarg);
171          break;
172          
173       case 's':
174          if ((deffiled != NULL) || (defstored != NULL)) {
175             fprintf(stderr, "Error: You can only use one -f <filed> or -s <stored> parameter\n");
176             exit(1);
177          }
178          defstored = bstrdup(optarg);
179          break;
180
181       case 'd':
182          debug_level = atoi(optarg);
183          if (debug_level <= 0) {
184             debug_level = 1;
185          }
186          break;
187
188       case 't':
189          test_config = true;
190          break;
191
192       case 'h':
193       case '?':
194       default:
195          usage();
196          exit(1);
197       }  
198    }
199    argc -= optind;
200    argv += optind;
201
202    if (argc) {
203       usage();
204       exit(1);
205    }
206
207    if (configfile == NULL) {
208       configfile = bstrdup(CONFIG_FILE);
209    }
210
211    parse_config(configfile);
212
213    currentitem = NULL;
214    
215    LockRes();
216    nitems = 0;
217    foreach_res(filed, R_CLIENT) {
218       items[nitems].type = R_CLIENT;
219       items[nitems].resource = filed;
220       if ((deffiled != NULL) && (strcmp(deffiled, filed->hdr.name) == 0)) {
221          currentitem = &(items[nitems]);
222       }
223       nitems++;
224    }
225    foreach_res(stored, R_STORAGE) {
226       items[nitems].type = R_STORAGE;
227       items[nitems].resource = stored;
228       if ((defstored != NULL) && (strcmp(defstored, stored->hdr.name) == 0)) {
229          currentitem = &(items[nitems]);
230       }
231       nitems++;
232    }
233    UnlockRes();
234      
235    if (nitems == 0) {
236       Emsg1(M_ERROR_TERM, 0, _("No Client nor Storage resource defined in %s\n\
237 Without that I don't how to get status from the File or Storage Daemon :-(\n"), configfile);
238    }
239    
240    if ((deffiled != NULL) || (defstored != NULL)) {
241      if (currentitem == NULL) {
242         fprintf(stderr, "Error: The file or storage daemon specified by parameters doesn't exists on your configuration file. Exiting...\n");
243         exit(1);
244      }
245    }
246    else {
247       currentitem = &(items[0]);
248    }
249
250    if (test_config) {
251       exit(0);
252    }
253    
254    (void)WSA_Init();                        /* Initialize Windows sockets */
255
256    LockRes();
257    monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
258    UnlockRes();
259    
260    gtk_init (&argc, &argv);
261    
262    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm_warn);
263    // This should be ideally replaced by a completely libpr0n-based icon rendering.
264    mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
265    g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
266    g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
267    g_object_unref(G_OBJECT(pixbuf));
268
269    mTrayMenu = gtk_menu_new();
270    
271    GtkWidget *entry;
272    
273    entry = gtk_menu_item_new_with_label("Open status window...");
274    g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
275    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
276    
277    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
278    
279    GtkWidget *submenu = gtk_menu_new();
280    
281    entry = gtk_menu_item_new_with_label("Set monitored daemon");
282    gtk_menu_item_set_submenu(GTK_MENU_ITEM(entry), submenu);
283    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
284       
285    GSList *group = NULL;
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       entry = gtk_radio_menu_item_new_with_label(group, str->str);
302       g_string_free(str, TRUE);
303       
304       group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(entry));
305       if (currentitem == &(items[i])) {
306          gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry), TRUE);
307       }
308       
309       g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconDaemonChanged), &(items[i]));
310       gtk_menu_shell_append(GTK_MENU_SHELL(submenu), entry);         
311    }
312    
313    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
314    
315    entry = gtk_menu_item_new_with_label("Exit");
316    g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
317    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
318    
319    gtk_widget_show_all(mTrayMenu);
320    
321    timerTag = g_timeout_add( 5000, fd_read, NULL );
322       
323    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
324    
325    gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
326    
327    g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
328    //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
329    
330    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
331    
332    GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
333    
334    textview = gtk_text_view_new();
335
336    buffer = gtk_text_buffer_new(NULL);
337
338    gtk_text_buffer_set_text(buffer, "", -1);
339
340    PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
341    gtk_widget_modify_font(textview, font_desc);
342    pango_font_description_free(font_desc);
343    
344    gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
345    gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
346    
347    gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
348    
349    gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
350    
351    gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, FALSE, 0);
352       
353    GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
354          
355    GtkWidget* button = new_image_button("gtk-help", "About");
356    g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
357    
358    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
359    
360    button = new_image_button("gtk-close", "Close");
361    g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window));
362    
363    gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
364    
365    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
366    
367    gtk_container_add(GTK_CONTAINER (window), vbox);
368    
369    gtk_widget_show_all(vbox);
370    
371    fd_read(NULL);
372    
373    gtk_main();
374       
375    if (D_sock) {
376       writecmd("quit");
377       bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
378       bnet_close(D_sock);
379    }
380
381    free_pool_memory(args);
382    (void)WSACleanup();               /* Cleanup Windows sockets */
383    return 0;
384 }
385
386 static void MonitorAbout(GtkWidget *widget, gpointer data) {
387    GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
388       "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
389       "Copyright (C) 2004 Kern Sibbald and John Walker\n"
390       "Written by Nicolas Boichat\n"
391       "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
392    ), HOST_OS, DISTNAME, DISTVER);
393    gtk_dialog_run(GTK_DIALOG(about));
394    gtk_widget_destroy(about);
395 }
396
397 static gboolean delete_event( GtkWidget *widget,
398                               GdkEvent  *event,
399                               gpointer   data ) {
400    gtk_widget_hide(window);
401    return TRUE; /* do not destroy the window */
402 }
403
404 static void TrayIconDaemonChanged(GtkWidget *widget, monitoritem* data) {
405    if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) { // && (data != currentitem)
406       printf("!!%s\n", ((STORE*)data->resource)->hdr.name);
407       if (D_sock) {
408          writecmd("quit");
409          bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
410          bnet_close(D_sock);
411          D_sock = NULL;
412       }
413       currentitem = data;
414       fd_read(NULL);
415    }
416 }
417
418 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
419     gtk_widget_show(window);
420 }
421
422 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
423   gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
424   gtk_widget_show_all(mTrayMenu);
425 }
426
427 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
428    gtk_main_quit();
429 }
430
431 static int authenticate_daemon(JCR *jcr) {
432    switch (currentitem->type) {
433    case R_CLIENT:
434       return authenticate_file_daemon(jcr, monitor, (CLIENT*)currentitem->resource);
435       break;
436    case R_STORAGE:
437       return authenticate_storage_daemon(jcr, monitor, (STORE*)currentitem->resource);
438       break;
439    default:
440       printf("Error, currentitem is not a Client or a Storage..\n");
441       gtk_main_quit();
442       return FALSE;
443    }
444 }
445
446 static gboolean fd_read(gpointer data) {
447    int stat;
448    int statuschanged = 0;
449    GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
450    GtkTextIter start, stop, nstart, nstop;
451    
452    gtk_text_buffer_set_text (newbuffer, "", -1);
453       
454    if (!D_sock) {
455       memset(&jcr, 0, sizeof(jcr));
456       
457       CLIENT* filed;
458       STORE* stored;
459       
460       switch (currentitem->type) {
461       case R_CLIENT:
462          filed = (CLIENT*)currentitem->resource;      
463          trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
464          D_sock = bnet_connect(NULL, 3, 3, "File daemon", filed->address, NULL, filed->FDport, 0);
465          jcr.file_bsock = D_sock;
466          break;
467       case R_STORAGE:
468          stored = (STORE*)currentitem->resource;      
469          trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
470          D_sock = bnet_connect(NULL, 3, 3, "Storage daemon", stored->address, NULL, stored->SDport, 0);
471          jcr.store_bsock = D_sock;
472          break;
473       default:
474          printf("Error, currentitem is not a Client or a Storage..\n");
475          gtk_main_quit();
476          return FALSE;
477       }
478       
479       if (D_sock == NULL) {
480          writeToTextBuffer(newbuffer, "Cannot connect to daemon.\n");
481          changeIcon(error);
482          return 1;
483       }
484       
485       if (!authenticate_daemon(&jcr)) {
486          writeToTextBuffer(newbuffer, "ERR=%s\n", D_sock->msg);
487          D_sock = NULL;
488          changeIcon(error);
489          return 0;
490       }
491    
492       trayMessage("Opened connection with File daemon.\n");
493    }
494       
495    writecmd("status");
496    
497    while(1) {
498       if ((stat = bnet_recv(D_sock)) >= 0) {
499          writeToTextBuffer(newbuffer, D_sock->msg);
500          if (strstr(D_sock->msg, " is running.") != NULL) {
501             changeIcon(running);
502             statuschanged = 1;
503          }
504          else if (strstr(D_sock->msg, "No Jobs running.") != NULL) {
505             changeIcon(idle);
506             statuschanged = 1;
507          }
508       }
509       else if (stat == BNET_SIGNAL) {
510          if (D_sock->msglen == BNET_EOD) {
511             if (statuschanged == 0) {
512                changeIcon(warn);
513             }
514             break;
515          }
516          else if (D_sock->msglen == BNET_HEARTBEAT) {
517             bnet_sig(D_sock, BNET_HB_RESPONSE);
518             writeToTextBuffer(newbuffer, "<< Heartbeat signal received, answered. >>");
519          }
520          else {
521             writeToTextBuffer(newbuffer, "<< Unexpected signal received : %s >>", bnet_sig_to_ascii(D_sock));
522          }
523       }
524       else { /* BNET_HARDEOF || BNET_ERROR */
525          writeToTextBuffer(newbuffer, "<ERROR>\n");
526          D_sock = NULL;
527          changeIcon(error);
528          break;
529       }
530            
531       if (is_bnet_stop(D_sock)) {
532          writeToTextBuffer(newbuffer, "<STOP>\n");
533          D_sock = NULL;
534          changeIcon(error);
535          break;            /* error or term */
536       }
537    }
538    
539    /* Keep the selection if necessary */
540    if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
541       gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
542       gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop,  gtk_text_iter_get_offset(&stop ));
543       gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
544    }
545
546    g_object_unref(buffer);
547    
548    buffer = newbuffer;
549    gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
550       
551    return 1;
552 }
553
554 void writecmd(const char* command) {
555    if (D_sock) {
556       D_sock->msglen = strlen(command);
557       pm_strcpy(&D_sock->msg, command);
558       bnet_send(D_sock);
559    }
560 }
561
562 /* Note: Does not seem to work either on Gnome nor KDE... */
563 void trayMessage(const char *fmt,...) {
564    char buf[3000];
565    va_list arg_ptr;
566    
567    va_start(arg_ptr, fmt);
568    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
569    va_end(arg_ptr);
570    
571    egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
572 }
573
574 void changeIcon(stateenum status) {
575    if (status == currentstatus)
576       return;
577
578    const char** xpm;
579
580    switch (status) {
581    case error:
582       xpm = (const char**)&xpm_error;
583       break;
584    case idle:
585       xpm = (const char**)&xpm_idle;
586       break;
587    case running:
588       xpm = (const char**)&xpm_running;
589       break;
590    case saving:
591       xpm = (const char**)&xpm_saving;
592       break;
593    case warn:
594       xpm = (const char**)&xpm_warn;
595       break;
596    default:
597       xpm = NULL;
598       break;
599    }
600    
601    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
602    egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
603    
604    gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
605    
606    currentstatus = status;
607 }
608
609 void writeToTextBuffer(GtkTextBuffer *buffer, const char *fmt,...) {
610    char buf[3000];
611    va_list arg_ptr;
612    GtkTextIter iter;
613    
614    va_start(arg_ptr, fmt);
615    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
616    va_end(arg_ptr);
617    
618    gtk_text_buffer_get_end_iter(buffer, &iter);
619    gtk_text_buffer_insert(buffer, &iter, buf, -1);
620 }
621