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) {
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);
397 static gboolean delete_event( GtkWidget *widget,
400 gtk_widget_hide(window);
401 return TRUE; /* do not destroy the window */
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);
409 bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
418 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
419 gtk_widget_show(window);
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);
427 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
431 static int authenticate_daemon(JCR *jcr) {
432 switch (currentitem->type) {
434 return authenticate_file_daemon(jcr, monitor, (CLIENT*)currentitem->resource);
437 return authenticate_storage_daemon(jcr, monitor, (STORE*)currentitem->resource);
440 printf("Error, currentitem is not a Client or a Storage..\n");
446 static gboolean fd_read(gpointer data) {
448 int statuschanged = 0;
449 GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
450 GtkTextIter start, stop, nstart, nstop;
452 gtk_text_buffer_set_text (newbuffer, "", -1);
455 memset(&jcr, 0, sizeof(jcr));
460 switch (currentitem->type) {
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;
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;
474 printf("Error, currentitem is not a Client or a Storage..\n");
479 if (D_sock == NULL) {
480 writeToTextBuffer(newbuffer, "Cannot connect to daemon.\n");
485 if (!authenticate_daemon(&jcr)) {
486 writeToTextBuffer(newbuffer, "ERR=%s\n", D_sock->msg);
492 trayMessage("Opened connection with File daemon.\n");
498 if ((stat = bnet_recv(D_sock)) >= 0) {
499 writeToTextBuffer(newbuffer, D_sock->msg);
500 if (strstr(D_sock->msg, " is running.") != NULL) {
504 else if (strstr(D_sock->msg, "No Jobs running.") != NULL) {
509 else if (stat == BNET_SIGNAL) {
510 if (D_sock->msglen == BNET_EOD) {
511 if (statuschanged == 0) {
516 else if (D_sock->msglen == BNET_HEARTBEAT) {
517 bnet_sig(D_sock, BNET_HB_RESPONSE);
518 writeToTextBuffer(newbuffer, "<< Heartbeat signal received, answered. >>");
521 writeToTextBuffer(newbuffer, "<< Unexpected signal received : %s >>", bnet_sig_to_ascii(D_sock));
524 else { /* BNET_HARDEOF || BNET_ERROR */
525 writeToTextBuffer(newbuffer, "<ERROR>\n");
531 if (is_bnet_stop(D_sock)) {
532 writeToTextBuffer(newbuffer, "<STOP>\n");
535 break; /* error or term */
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);
546 g_object_unref(buffer);
549 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
554 void writecmd(const char* command) {
556 D_sock->msglen = strlen(command);
557 pm_strcpy(&D_sock->msg, command);
562 /* Note: Does not seem to work either on Gnome nor KDE... */
563 void trayMessage(const char *fmt,...) {
567 va_start(arg_ptr, fmt);
568 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
571 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
574 void changeIcon(stateenum status) {
575 if (status == currentstatus)
582 xpm = (const char**)&xpm_error;
585 xpm = (const char**)&xpm_idle;
588 xpm = (const char**)&xpm_running;
591 xpm = (const char**)&xpm_saving;
594 xpm = (const char**)&xpm_warn;
601 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
602 egg_status_icon_set_from_pixbuf(mTrayIcon, pixbuf);
604 gtk_window_set_icon(GTK_WINDOW(window), pixbuf);
606 currentstatus = status;
609 void writeToTextBuffer(GtkTextBuffer *buffer, const char *fmt,...) {
614 va_start(arg_ptr, fmt);
615 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
618 gtk_text_buffer_get_end_iter(buffer, &iter);
619 gtk_text_buffer_insert(buffer, &iter, buf, -1);