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 "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);
107 /*********************************************************************
109 * Main Bacula Tray Monitor -- User Interface Program
112 int main(int argc, char *argv[])
115 bool test_config = false;
116 char* deffiled = NULL;
117 char* defstored = NULL;
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);
128 while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) {
130 case 'c': /* configuration file */
131 if (configfile != NULL) {
134 configfile = bstrdup(optarg);
138 if ((deffiled != NULL) || (defstored != NULL)) {
139 fprintf(stderr, "Error: You can only use one -f <filed> or -s <stored> parameter\n");
142 deffiled = bstrdup(optarg);
146 if ((deffiled != NULL) || (defstored != NULL)) {
147 fprintf(stderr, "Error: You can only use one -f <filed> or -s <stored> parameter\n");
150 defstored = bstrdup(optarg);
154 debug_level = atoi(optarg);
155 if (debug_level <= 0) {
179 if (configfile == NULL) {
180 configfile = bstrdup(CONFIG_FILE);
183 parse_config(configfile);
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]);
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]);
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);
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");
219 currentitem = &(items[0]);
226 (void)WSA_Init(); /* Initialize Windows sockets */
229 monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL);
232 gtk_init (&argc, &argv);
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));
241 mTrayMenu = gtk_menu_new();
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);
249 GtkWidget *submenu = gtk_menu_new();
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);
255 GSList *group = NULL;
258 for (int i = 0; i < nitems; i++) {
259 switch (items[i].type) {
261 str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name);
262 g_string_append(str, " (FD)");
265 str = g_string_new(((STORE*)(items[i].resource))->hdr.name);
266 g_string_append(str, " (SD)");
271 entry = gtk_radio_menu_item_new_with_label(group, str->str);
272 g_string_free(str, TRUE);
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);
279 g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconDaemonChanged), &(items[i]));
280 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), entry);
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);
287 gtk_widget_show_all(mTrayMenu);
289 timerTag = g_timeout_add( 5000, fd_read, NULL );
291 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
293 gtk_window_set_title(GTK_WINDOW(window), "Bacula tray monitor");
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);
298 gtk_container_set_border_width(GTK_CONTAINER(window), 10);
300 textview = gtk_text_view_new();
302 buffer = gtk_text_buffer_new(NULL);
304 gtk_text_buffer_set_text(buffer, "", -1);
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);
310 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20);
311 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20);
313 gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
315 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
317 gtk_container_add(GTK_CONTAINER (window), textview);
319 gtk_widget_show(textview);
327 bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
331 free_pool_memory(args);
332 (void)WSACleanup(); /* Cleanup Windows sockets */
336 static gboolean delete_event( GtkWidget *widget,
339 gtk_widget_hide(window);
340 return TRUE; /* do not destroy the window */
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);
348 bnet_sig(D_sock, BNET_TERMINATE); /* send EOF */
357 static void TrayIconActivate(GtkWidget *widget, gpointer data) {
358 gtk_widget_show(window);
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);
366 static void TrayIconExit(unsigned int activateTime, unsigned int button) {
370 static int authenticate_daemon(JCR *jcr) {
371 switch (currentitem->type) {
373 return authenticate_file_daemon(jcr, monitor, (CLIENT*)currentitem->resource);
376 return authenticate_storage_daemon(jcr, monitor, (STORE*)currentitem->resource);
379 printf("Error, currentitem is not a Client or a Storage..\n");
385 static gboolean fd_read(gpointer data) {
387 int statuschanged = 0;
388 GtkTextBuffer *newbuffer = gtk_text_buffer_new(NULL);
389 GtkTextIter start, stop, nstart, nstop;
391 gtk_text_buffer_set_text (newbuffer, "", -1);
394 memset(&jcr, 0, sizeof(jcr));
399 switch (currentitem->type) {
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;
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;
413 printf("Error, currentitem is not a Client or a Storage..\n");
418 if (D_sock == NULL) {
419 writeToTextBuffer(newbuffer, "Cannot connect to daemon.\n");
424 if (!authenticate_daemon(&jcr)) {
425 writeToTextBuffer(newbuffer, "ERR=%s\n", D_sock->msg);
431 writeToTextBuffer(newbuffer, "Opened connection with File daemon.\n");
437 if ((stat = bnet_recv(D_sock)) >= 0) {
438 writeToTextBuffer(newbuffer, D_sock->msg);
439 if (strstr(D_sock->msg, " is running.") != NULL) {
443 else if (strstr(D_sock->msg, "No Jobs running.") != NULL) {
448 else if (stat == BNET_SIGNAL) {
449 if (D_sock->msglen == BNET_EOD) {
450 if (statuschanged == 0) {
455 else if (D_sock->msglen == BNET_HEARTBEAT) {
456 bnet_sig(D_sock, BNET_HB_RESPONSE);
457 writeToTextBuffer(newbuffer, "<< Heartbeat signal received, answered. >>");
460 writeToTextBuffer(newbuffer, "<< Unexpected signal received : %s >>", bnet_sig_to_ascii(D_sock));
463 else { /* BNET_HARDEOF || BNET_ERROR */
464 writeToTextBuffer(newbuffer, "<ERROR>\n");
470 if (is_bnet_stop(D_sock)) {
471 writeToTextBuffer(newbuffer, "<STOP>\n");
474 break; /* error or term */
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);
485 g_object_unref(buffer);
488 gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer);
493 void writecmd(const char* command) {
495 D_sock->msglen = strlen(command);
496 pm_strcpy(&D_sock->msg, command);
501 /* Note: Does not seem to work either on Gnome nor KDE... */
502 void trayMessage(const char *fmt,...) {
506 va_start(arg_ptr, fmt);
507 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
510 egg_tray_icon_send_message(egg_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1);
513 void changeIcon(stateenum status) {
514 if (status == currentstatus)
521 xpm = (const char**)&xpm_error;
524 xpm = (const char**)&xpm_idle;
527 xpm = (const char**)&xpm_running;
530 xpm = (const char**)&xpm_saving;
533 xpm = (const char**)&xpm_warn;
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);
544 currentstatus = status;
547 void writeToTextBuffer(GtkTextBuffer *buffer, const char *fmt,...) {
552 va_start(arg_ptr, fmt);
553 bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
556 gtk_text_buffer_get_end_iter(buffer, &iter);
557 gtk_text_buffer_insert(buffer, &iter, buf, -1);