]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tray-monitor/tray-monitor.c
- Add support for storage daemon.
[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) 2000-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 gboolean delete_event(GtkWidget *widget, GdkEvent  *event, gpointer   data);
81
82 static gint timerTag;
83 static EggStatusIcon *mTrayIcon;
84 static GtkWidget *mTrayMenu;
85 static GtkWidget *window;
86 static GtkWidget *textview;
87 static GtkTextBuffer *buffer;
88
89 #define CONFIG_FILE "./tray-monitor.conf"   /* default configuration file */
90
91 static void usage()
92 {
93    fprintf(stderr, _(
94 "Copyright (C) 2000-2004 Kern Sibbald and John Walker\n"
95 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
96 "Usage: tray-monitor [-s] [-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 "       -s          no signals\n"
100 "       -t          test - read configuration and exit\n"
101 "       -?          print this message.\n"  
102 "\n"), HOST_OS, DISTNAME, DISTVER);
103 }
104
105 /*********************************************************************
106  *
107  *         Main Bacula Tray Monitor -- User Interface Program
108  *
109  */
110 int main(int argc, char *argv[])
111 {
112    int ch;
113    bool test_config = false;
114    CLIENT* filed;
115    STORE* stored;
116
117    init_stack_dump();
118    my_name_is(argc, argv, "tray-monitor");
119    textdomain("bacula");
120    init_msg(NULL, NULL);
121    working_directory = "/tmp";
122    args = get_pool_memory(PM_FNAME);
123
124    while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
125       switch (ch) {
126       case 'c':                    /* configuration file */
127          if (configfile != NULL) {
128             free(configfile);
129          }
130          configfile = bstrdup(optarg);
131          break;
132
133       case 'd':
134          debug_level = atoi(optarg);
135          if (debug_level <= 0) {
136             debug_level = 1;
137          }
138          break;
139
140       case 't':
141          test_config = true;
142          break;
143
144       case '?':
145       default:
146          usage();
147          exit(1);
148       }  
149    }
150    argc -= optind;
151    argv += optind;
152
153    if (argc) {
154       usage();
155       exit(1);
156    }
157
158    if (configfile == NULL) {
159       configfile = bstrdup(CONFIG_FILE);
160    }
161
162    parse_config(configfile);
163
164    LockRes();
165    nitems = 0;
166    foreach_res(filed, R_CLIENT) {
167       items[nitems].type = R_CLIENT;
168       items[nitems].resource = filed;
169       nitems++;
170    }
171    foreach_res(stored, R_STORAGE) {
172       items[nitems].type = R_STORAGE;
173       items[nitems].resource = stored;
174       nitems++;
175    }
176    UnlockRes();
177    
178    if (nitems == 0) {
179       Emsg1(M_ERROR_TERM, 0, _("No Client nor Storage resource defined in %s\n\
180 Without that I don't how to get status from the File or Storage Daemon :-(\n"), configfile);
181    }
182    
183    currentitem = &(items[0]);
184
185    if (test_config) {
186       exit(0);
187    }
188    
189    (void)WSA_Init();                        /* Initialize Windows sockets */
190
191    LockRes();
192    monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
193    UnlockRes();
194    
195    gtk_init (&argc, &argv);
196    
197    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm_warn);
198    // This should be ideally replaced by a completely libpr0n-based icon rendering.
199    mTrayIcon = egg_status_icon_new_from_pixbuf(pixbuf);
200    g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL);
201    g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL);
202    g_object_unref(G_OBJECT(pixbuf));
203
204    mTrayMenu = gtk_menu_new();
205    
206    GtkWidget *entry;
207    
208    entry = gtk_menu_item_new_with_label("Open status window...");
209    g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL);
210    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
211    
212    GtkWidget *submenu = gtk_menu_new();
213    
214    entry = gtk_menu_item_new_with_label("Set monitored daemon");
215    gtk_menu_item_set_submenu(GTK_MENU_ITEM(entry), submenu);
216    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
217       
218    GSList *group = NULL;
219    
220    GString *str;   
221    for (int i = 0; i < nitems; i++) {
222       switch (items[i].type) {
223       case R_CLIENT:
224          str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
225          g_string_append(str, " (FD)");
226          break;
227       case R_STORAGE:
228          str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
229          g_string_append(str, " (SD)");
230          break;
231       default:
232          continue;
233       }
234       entry = gtk_radio_menu_item_new_with_label(group, str->str);
235       g_string_free(str, TRUE);
236       
237       group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(entry));
238       if (i == 0) {
239          gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry), TRUE);
240       }
241       
242       g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconDaemonChanged), &(items[i]));
243       gtk_menu_shell_append(GTK_MENU_SHELL(submenu), entry);         
244    }
245    
246    entry = gtk_menu_item_new_with_label("Exit");
247    g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL);
248    gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry);
249    
250    gtk_widget_show_all(mTrayMenu);
251    
252    timerTag = g_timeout_add( 5000, fd_read, NULL );
253         
254    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
255    
256    gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
257    
258    g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
259    //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
260    
261    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
262    
263    textview = gtk_text_view_new();
264
265    buffer = gtk_text_buffer_new(NULL);
266
267    gtk_text_buffer_set_text(buffer, "", -1);
268
269    PangoFontDescription *font_desc = pango_font_description_from_string ("Fixed 10");
270    gtk_widget_modify_font(textview, font_desc);
271    pango_font_description_free (font_desc);
272    
273    gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
274    gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
275    
276    gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
277    
278    gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
279       
280    gtk_container_add(GTK_CONTAINER (window), textview);
281    
282    gtk_widget_show(textview);
283    
284    fd_read(NULL);
285    
286    gtk_main();
287       
288    if (D_sock) {
289       writecmd("quit");
290       bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
291       bnet_close(D_sock);
292    }
293
294    free_pool_memory(args);
295    (void)WSACleanup();               /* Cleanup Windows sockets */
296    return 0;
297 }
298
299 static gboolean delete_event( GtkWidget *widget,
300                               GdkEvent  *event,
301                               gpointer   data ) {
302    gtk_widget_hide(window);
303    return TRUE; /* do not destroy the window */
304 }
305
306 static void TrayIconDaemonChanged(GtkWidget *widget, monitoritem* data) {
307    if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) { // && (data != currentitem)
308       printf("!!%s\n", ((STORE*)data->resource)->hdr.name);
309       if (D_sock) {
310          writecmd("quit");
311          bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
312          bnet_close(D_sock);
313          D_sock = NULL;
314       }
315       currentitem = data;
316       fd_read(NULL);
317    }
318 }
319
320 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
321     gtk_widget_show(window);
322 }
323
324 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
325   gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
326   gtk_widget_show_all(mTrayMenu);
327 }
328
329 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
330    gtk_main_quit();
331 }
332
333 static int authenticate_daemon(JCR *jcr) {
334    switch (currentitem->type) {
335    case R_CLIENT:
336       return authenticate_file_daemon(jcr, monitor, (CLIENT*)currentitem->resource);
337       break;
338    case R_STORAGE:
339       return authenticate_storage_daemon(jcr, monitor, (STORE*)currentitem->resource);
340       break;
341    default:
342       printf("Error, currentitem is not a Client or a Storage..\n");
343       gtk_main_quit();
344       return FALSE;
345    }
346 }
347
348 static gboolean fd_read(gpointer data) {
349    int stat;
350    int statuschanged = 0;
351    GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
352    GtkTextIter start, stop, nstart, nstop;
353    
354    gtk_text_buffer_set_text (newbuffer, "", -1);
355       
356    if (!D_sock) {
357       memset(&jcr, 0, sizeof(jcr));
358       
359       CLIENT* filed;
360       STORE* stored;
361       
362       switch (currentitem->type) {
363       case R_CLIENT:
364          filed = (CLIENT*)currentitem->resource;      
365          writeToTextBuffer(newbuffer, "Connecting to Client %s:%d\n", filed->address, filed->FDport);
366          D_sock = bnet_connect(NULL, 3, 3, "File daemon", filed->address, NULL, filed->FDport, 0);
367          jcr.file_bsock = D_sock;
368          break;
369       case R_STORAGE:
370          stored = (STORE*)currentitem->resource;      
371          writeToTextBuffer(newbuffer, "Connecting to Storage %s:%d\n", stored->address, stored->SDport);
372          D_sock = bnet_connect(NULL, 3, 3, "Storage daemon", stored->address, NULL, stored->SDport, 0);
373          jcr.store_bsock = D_sock;
374          break;
375       default:
376          printf("Error, currentitem is not a Client or a Storage..\n");
377          gtk_main_quit();
378          return FALSE;
379       }
380       
381       if (D_sock == NULL) {
382          writeToTextBuffer(newbuffer, "Cannot connect to daemon.\n");
383          changeIcon(error);
384          return 1;
385       }
386       
387       if (!authenticate_daemon(&jcr)) {
388          writeToTextBuffer(newbuffer, "ERR=%s\n", D_sock->msg);
389          D_sock = NULL;
390          changeIcon(error);
391          return 0;
392       }
393    
394       writeToTextBuffer(newbuffer, "Opened connection with File daemon.\n");
395    }
396       
397    writecmd("status");
398    
399    while(1) {
400       if ((stat = bnet_recv(D_sock)) >= 0) {
401          writeToTextBuffer(newbuffer, D_sock->msg);
402          if (strstr(D_sock->msg, " is running.") != NULL) {
403             changeIcon(running);
404             statuschanged = 1;
405          }
406          else if (strstr(D_sock->msg, "No Jobs running.") != NULL) {
407             changeIcon(idle);
408             statuschanged = 1;
409          }
410       }
411       else if (stat == BNET_SIGNAL) {
412          if (D_sock->msglen == BNET_EOD) {
413             if (statuschanged == 0) {
414                changeIcon(warn);
415             }
416             break;
417          }
418          else if (D_sock->msglen == BNET_HEARTBEAT) {
419             bnet_sig(D_sock, BNET_HB_RESPONSE);
420             writeToTextBuffer(newbuffer, "<< Heartbeat signal received, answered. >>");
421          }
422          else {
423             writeToTextBuffer(newbuffer, "<< Unexpected signal received : %s >>", bnet_sig_to_ascii(D_sock));
424          }
425       }
426       else { /* BNET_HARDEOF || BNET_ERROR */
427          writeToTextBuffer(newbuffer, "<ERROR>\n");
428          D_sock = NULL;
429          changeIcon(error);
430          break;
431       }
432            
433       if (is_bnet_stop(D_sock)) {
434          writeToTextBuffer(newbuffer, "<STOP>\n");
435          D_sock = NULL;
436          changeIcon(error);
437          break;            /* error or term */
438       }
439    }
440    
441    /* Keep the selection if necessary */
442    if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
443       gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
444       gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop,  gtk_text_iter_get_offset(&stop ));
445       gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
446    }
447
448    g_object_unref(buffer);
449    
450    buffer = newbuffer;
451    gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
452       
453    return 1;
454 }
455
456 void writecmd(const char* command) {
457    if (D_sock) {
458       D_sock->msglen = strlen(command);
459       pm_strcpy(&D_sock->msg, command);
460       bnet_send(D_sock);
461    }
462 }
463
464 /* Note: Does not seem to work either on Gnome nor KDE... */
465 void trayMessage(const char *fmt,...) {
466    char buf[3000];
467    va_list arg_ptr;
468    
469    va_start(arg_ptr, fmt);
470    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
471    va_end(arg_ptr);
472    
473    egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
474 }
475
476 void changeIcon(stateenum status) {
477    if (status == currentstatus)
478       return;
479
480    const char** xpm;
481
482    switch (status) {
483    case error:
484       xpm = (const char**)&xpm_error;
485       break;
486    case idle:
487       xpm = (const char**)&xpm_idle;
488       break;
489    case running:
490       xpm = (const char**)&xpm_running;
491       break;
492    case saving:
493       xpm = (const char**)&xpm_saving;
494       break;
495    case warn:
496       xpm = (const char**)&xpm_warn;
497       break;
498    default:
499       xpm = NULL;
500       break;
501    }
502    
503    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
504    // This should be ideally replaced by a completely libpr0n-based icon rendering.
505    egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
506    
507    currentstatus = status;
508 }
509
510 void writeToTextBuffer(GtkTextBuffer *buffer, const char *fmt,...) {
511    char buf[3000];
512    va_list arg_ptr;
513    GtkTextIter iter;
514    
515    va_start(arg_ptr, fmt);
516    bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
517    va_end(arg_ptr);
518    
519    gtk_text_buffer_get_end_iter(buffer, &iter);
520    gtk_text_buffer_insert(buffer, &iter, buf, -1);
521 }
522