3 * Bacula Gnome Tray Monitor
5 * Nicolas Boichat, August MMIV
11 Copyright (C) 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 void MonitorAbout(GtkWidget *widget, gpointer data);
81 static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data);
84 static EggStatusIcon *mTrayIcon;
85 static GtkWidget *mTrayMenu;
86 static GtkWidget *window;
87 static GtkWidget *textview;
88 static GtkTextBuffer *buffer;
90 #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */
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);
108 static GtkWidget *new_image_button(const gchar *stock_id,
109 const gchar *label_text) {
115 button = gtk_button_new();
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);
122 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3);
123 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3);
125 gtk_widget_show(image);
126 gtk_widget_show(label);
128 gtk_widget_show(box);
130 gtk_container_add(GTK_CONTAINER(button), box);
135 /*********************************************************************
137 * Main Bacula Tray Monitor -- User Interface Program
140 int main(int argc, char *argv[])
143 bool test_config = false;
144 char* deffiled = NULL;
145 char* defstored = NULL;
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);
156 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
158 case 'c': /* configuration file */
159 if (configfile != NULL) {
162 configfile = bstrdup(optarg);
166 if ((deffiled != NULL) || (defstored != NULL)) {
167 fprintf(stderr, "Error: You can only use one -f <filed> or -s <stored> parameter\n");
170 deffiled = bstrdup(optarg);
174 if ((deffiled != NULL) || (defstored != NULL)) {
175 fprintf(stderr, "Error: You can only use one -f <filed> or -s <stored> parameter\n");
178 defstored = bstrdup(optarg);
182 debug_level = atoi(optarg);
183 if (debug_level <= 0) {
207 if (configfile == NULL) {
208 configfile = bstrdup(CONFIG_FILE);
211 parse_config(configfile);
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]);
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]);
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);
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");
247 currentitem = &(items[0]);
254 (void)WSA_Init(); /* Initialize Windows sockets */
257 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
260 gtk_init (&argc, &argv);
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));
269 mTrayMenu = gtk_menu_new();
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);
277 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
279 GtkWidget *submenu = gtk_menu_new();
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);
285 GSList *group = NULL;
288 for (int i = 0; i < nitems; i++) {
289 switch (items[i].type) {
291 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
292 g_string_append(str, " (FD)");
295 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
296 g_string_append(str, " (SD)");
301 entry = gtk_radio_menu_item_new_with_label(group, str->str);
302 g_string_free(str, TRUE);
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);
309 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconDaemonChanged), &(items[i]));
310 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), entry);
313 gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new());
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);
319 gtk_widget_show_all(mTrayMenu);
321 timerTag = g_timeout_add( 5000, fd_read, NULL );
323 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
325 gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
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);
330 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
332 GtkWidget* vbox = gtk_vbox_new(FALSE, 10);
334 textview = gtk_text_view_new();
336 buffer = gtk_text_buffer_new(NULL);
338 gtk_text_buffer_set_text(buffer, "", -1);
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);
344 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
345 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
347 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
349 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
351 gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, FALSE, 0);
353 GtkWidget* hbox = gtk_hbox_new(FALSE, 10);
355 GtkWidget* button = new_image_button("gtk-help", "About");
356 g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL);
358 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
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));
363 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0);
365 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
367 gtk_container_add(GTK_CONTAINER (window), vbox);
369 gtk_widget_show_all(vbox);
377 bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
381 free_pool_memory(args);
382 (void)WSACleanup(); /* Cleanup Windows sockets */
386 static void MonitorAbout(GtkWidget *widget, gpointer data) {
388 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
389 "<span size='x-large' weight='bold'>Bacula Tray Monitor</span>\n\n"
390 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
391 "Written by Nicolas Boichat\n"
392 "\n<small>Version: " VERSION " (" BDATE ") %s %s %s</small>"
393 ), HOST_OS, DISTNAME, DISTVER);
395 GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, _(
396 "Bacula Tray Monitor\n\n"
397 "Copyright (C) 2004 Kern Sibbald and John Walker\n"
398 "Written by Nicolas Boichat\n"
399 "\nVersion: " VERSION " (" BDATE ") %s %s %s"
400 ), HOST_OS, DISTNAME, DISTVER);
402 gtk_dialog_run(GTK_DIALOG(about));
403 gtk_widget_destroy(about);
406 static gboolean delete_event( GtkWidget *widget,
409 gtk_widget_hide(window);
410 return TRUE; /* do not destroy the window */
413 static void TrayIconDaemonChanged(GtkWidget *widget, monitoritem* data) {
414 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) { // && (data != currentitem)
415 printf("!!%s\n", ((STORE*)data->resource)->hdr.name);
418 bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
427 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
428 gtk_widget_show(window);
431 static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) {
432 gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, 1, 0);
433 gtk_widget_show_all(mTrayMenu);
436 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
440 static int authenticate_daemon(JCR *jcr) {
441 switch (currentitem->type) {
443 return authenticate_file_daemon(jcr, monitor, (CLIENT*)currentitem->resource);
446 return authenticate_storage_daemon(jcr, monitor, (STORE*)currentitem->resource);
449 printf("Error, currentitem is not a Client or a Storage..\n");
455 static gboolean fd_read(gpointer data) {
457 int statuschanged = 0;
458 GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
459 GtkTextIter start, stop, nstart, nstop;
461 gtk_text_buffer_set_text (newbuffer, "", -1);
464 memset(&jcr, 0, sizeof(jcr));
469 switch (currentitem->type) {
471 filed = (CLIENT*)currentitem->resource;
472 trayMessage("Connecting to Client %s:%d\n", filed->address, filed->FDport);
473 D_sock = bnet_connect(NULL, 3, 3, "File daemon", filed->address, NULL, filed->FDport, 0);
474 jcr.file_bsock = D_sock;
477 stored = (STORE*)currentitem->resource;
478 trayMessage("Connecting to Storage %s:%d\n", stored->address, stored->SDport);
479 D_sock = bnet_connect(NULL, 3, 3, "Storage daemon", stored->address, NULL, stored->SDport, 0);
480 jcr.store_bsock = D_sock;
483 printf("Error, currentitem is not a Client or a Storage..\n");
488 if (D_sock == NULL) {
489 writeToTextBuffer(newbuffer, "Cannot connect to daemon.\n");
494 if (!authenticate_daemon(&jcr)) {
495 writeToTextBuffer(newbuffer, "ERR=%s\n", D_sock->msg);
501 trayMessage("Opened connection with File daemon.\n");
507 if ((stat = bnet_recv(D_sock)) >= 0) {
508 writeToTextBuffer(newbuffer, D_sock->msg);
509 if (strstr(D_sock->msg, " is running.") != NULL) {
513 else if (strstr(D_sock->msg, "No Jobs running.") != NULL) {
518 else if (stat == BNET_SIGNAL) {
519 if (D_sock->msglen == BNET_EOD) {
520 if (statuschanged == 0) {
525 else if (D_sock->msglen == BNET_HEARTBEAT) {
526 bnet_sig(D_sock, BNET_HB_RESPONSE);
527 writeToTextBuffer(newbuffer, "<< Heartbeat signal received, answered. >>");
530 writeToTextBuffer(newbuffer, "<< Unexpected signal received : %s >>", bnet_sig_to_ascii(D_sock));
533 else { /* BNET_HARDEOF || BNET_ERROR */
534 writeToTextBuffer(newbuffer, "<ERROR>\n");
540 if (is_bnet_stop(D_sock)) {
541 writeToTextBuffer(newbuffer, "<STOP>\n");
544 break; /* error or term */
548 /* Keep the selection if necessary */
549 if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) {
550 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start));
551 gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop ));
554 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop);
556 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart);
557 gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop);
561 g_object_unref(buffer);
564 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
569 void writecmd(const char* command) {
571 D_sock->msglen = strlen(command);
572 pm_strcpy(&D_sock->msg, command);
577 /* Note: Does not seem to work either on Gnome nor KDE... */
578 void trayMessage(const char *fmt,...) {
582 va_start(arg_ptr, fmt);
583 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
586 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
589 void changeIcon(stateenum status) {
590 if (status == currentstatus)
597 xpm = (const char**)&xpm_error;
600 xpm = (const char**)&xpm_idle;
603 xpm = (const char**)&xpm_running;
606 xpm = (const char**)&xpm_saving;
609 xpm = (const char**)&xpm_warn;
616 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
617 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
619 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
621 currentstatus = status;
624 void writeToTextBuffer(GtkTextBuffer *buffer, const char *fmt,...) {
629 va_start(arg_ptr, fmt);
630 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
633 gtk_text_buffer_get_end_iter(buffer, &iter);
634 gtk_text_buffer_insert(buffer, &iter, buf, -1);