3 * Bacula Gnome Tray Monitor
5 * Nicolas Boichat, August MMIV
11 Copyright (C) 2000-2004 Kern Sibbald and John Walker
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.
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.
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,
31 #include "tray-monitor.h"
33 #include "eggstatusicon.h"
38 #include "running.xpm"
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);
46 /* Forward referenced functions */
47 void writecmd(const char* command);
49 /* Static variables */
50 static char *configfile = NULL;
51 static BSOCK *D_sock = NULL;
52 static MONITOR *monitor;
55 static int nitems = 0;
56 static monitoritem items[32];
57 static monitoritem* currentitem;
59 /* UI variables and functions */
68 stateenum currentstatus = warn;
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,...);
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);
83 static EggStatusIcon *mTrayIcon;
84 static GtkWidget *mTrayMenu;
85 static GtkWidget *window;
86 static GtkWidget *textview;
87 static GtkTextBuffer *buffer;
89 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
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"
100 " -t test - read configuration and exit\n"
101 " -? print this message.\n"
102 "\n"), HOST_OS, DISTNAME, DISTVER);
105 /*********************************************************************
107 * Main Bacula Tray Monitor -- User Interface Program
110 int main(int argc, char *argv[])
113 bool test_config = false;
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);
124 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
126 case 'c': /* configuration file */
127 if (configfile != NULL) {
130 configfile = bstrdup(optarg);
134 debug_level = atoi(optarg);
135 if (debug_level <= 0) {
158 if (configfile == NULL) {
159 configfile = bstrdup(CONFIG_FILE);
162 parse_config(configfile);
166 foreach_res(filed, R_CLIENT) {
167 items[nitems].type = R_CLIENT;
168 items[nitems].resource = filed;
171 foreach_res(stored, R_STORAGE) {
172 items[nitems].type = R_STORAGE;
173 items[nitems].resource = stored;
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);
183 currentitem = &(items[0]);
189 (void)WSA_Init(); /* Initialize Windows sockets */
192 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
195 gtk_init (&argc, &argv);
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));
204 mTrayMenu = gtk_menu_new();
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);
212 GtkWidget *submenu = gtk_menu_new();
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);
218 GSList *group = NULL;
221 for (int i = 0; i < nitems; i++) {
222 switch (items[i].type) {
224 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
225 g_string_append(str, " (FD)");
228 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
229 g_string_append(str, " (SD)");
234 entry = gtk_radio_menu_item_new_with_label(group, str->str);
235 g_string_free(str, TRUE);
237 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(entry));
239 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(entry), TRUE);
242 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconDaemonChanged), &(items[i]));
243 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), entry);
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);
250 gtk_widget_show_all(mTrayMenu);
252 timerTag = g_timeout_add( 5000, fd_read, NULL );
254 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
256 gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
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);
261 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
263 textview = gtk_text_view_new();
265 buffer = gtk_text_buffer_new(NULL);
267 gtk_text_buffer_set_text(buffer, "", -1);
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);
273 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
274 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
276 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
278 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
280 gtk_container_add(GTK_CONTAINER (window), textview);
282 gtk_widget_show(textview);
290 bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
294 free_pool_memory(args);
295 (void)WSACleanup(); /* Cleanup Windows sockets */
299 static gboolean delete_event( GtkWidget *widget,
302 gtk_widget_hide(window);
303 return TRUE; /* do not destroy the window */
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);
311 bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
320 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
321 gtk_widget_show(window);
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);
329 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
333 static int authenticate_daemon(JCR *jcr) {
334 switch (currentitem->type) {
336 return authenticate_file_daemon(jcr, monitor, (CLIENT*)currentitem->resource);
339 return authenticate_storage_daemon(jcr, monitor, (STORE*)currentitem->resource);
342 printf("Error, currentitem is not a Client or a Storage..\n");
348 static gboolean fd_read(gpointer data) {
350 int statuschanged = 0;
351 GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
352 GtkTextIter start, stop, nstart, nstop;
354 gtk_text_buffer_set_text (newbuffer, "", -1);
357 memset(&jcr, 0, sizeof(jcr));
362 switch (currentitem->type) {
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;
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;
376 printf("Error, currentitem is not a Client or a Storage..\n");
381 if (D_sock == NULL) {
382 writeToTextBuffer(newbuffer, "Cannot connect to daemon.\n");
387 if (!authenticate_daemon(&jcr)) {
388 writeToTextBuffer(newbuffer, "ERR=%s\n", D_sock->msg);
394 writeToTextBuffer(newbuffer, "Opened connection with File daemon.\n");
400 if ((stat = bnet_recv(D_sock)) >= 0) {
401 writeToTextBuffer(newbuffer, D_sock->msg);
402 if (strstr(D_sock->msg, " is running.") != NULL) {
406 else if (strstr(D_sock->msg, "No Jobs running.") != NULL) {
411 else if (stat == BNET_SIGNAL) {
412 if (D_sock->msglen == BNET_EOD) {
413 if (statuschanged == 0) {
418 else if (D_sock->msglen == BNET_HEARTBEAT) {
419 bnet_sig(D_sock, BNET_HB_RESPONSE);
420 writeToTextBuffer(newbuffer, "<< Heartbeat signal received, answered. >>");
423 writeToTextBuffer(newbuffer, "<< Unexpected signal received : %s >>", bnet_sig_to_ascii(D_sock));
426 else { /* BNET_HARDEOF || BNET_ERROR */
427 writeToTextBuffer(newbuffer, "<ERROR>\n");
433 if (is_bnet_stop(D_sock)) {
434 writeToTextBuffer(newbuffer, "<STOP>\n");
437 break; /* error or term */
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);
448 g_object_unref(buffer);
451 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
456 void writecmd(const char* command) {
458 D_sock->msglen = strlen(command);
459 pm_strcpy(&D_sock->msg, command);
464 /* Note: Does not seem to work either on Gnome nor KDE... */
465 void trayMessage(const char *fmt,...) {
469 va_start(arg_ptr, fmt);
470 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
473 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
476 void changeIcon(stateenum status) {
477 if (status == currentstatus)
484 xpm = (const char**)&xpm_error;
487 xpm = (const char**)&xpm_idle;
490 xpm = (const char**)&xpm_running;
493 xpm = (const char**)&xpm_saving;
496 xpm = (const char**)&xpm_warn;
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);
507 currentstatus = status;
510 void writeToTextBuffer(GtkTextBuffer *buffer, const char *fmt,...) {
515 va_start(arg_ptr, fmt);
516 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
519 gtk_text_buffer_get_end_iter(buffer, &iter);
520 gtk_text_buffer_insert(buffer, &iter, buf, -1);