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