/* Bacula® - The Network Backup Solution Copyright (C) 2004-2007 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or modify it under the terms of version two of the GNU General Public License as published by the Free Software Foundation and included in the file LICENSE. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Bacula® is a registered trademark of John Walker. The licensor of Bacula is the Free Software Foundation Europe (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, Switzerland, email:ftf@fsfeurope.org. */ /* * * Bacula Gnome Tray Monitor * * Nicolas Boichat, August MMIV * * Version $Id: tray-monitor.c 5752 2007-10-16 19:00:51Z kerns $ */ #include "bacula.h" #include "tray-monitor.h" #include "generic.xpm" #define TRAY_DEBUG_MEMORY 0 /* Imported functions */ int authenticate_director(JCR *jcr, MONITOR *monitor, DIRRES *director); int authenticate_file_daemon(JCR *jcr, MONITOR *monitor, CLIENT* client); int authenticate_storage_daemon(JCR *jcr, MONITOR *monitor, STORE* store); /* Dummy functions */ int generate_daemon_event(JCR *jcr, const char *event) { return 1; } /* Forward referenced functions */ void writecmd(monitoritem* item, const char* command); int docmd(monitoritem* item, const char* command, GSList** list); void getstatus(monitoritem* item, int current, GString** str); /* Static variables */ static char *configfile = NULL; static MONITOR *monitor; static JCR jcr; static int nitems = 0; static int fullitem = 0; //Item to be display in detailled status window static int lastupdated = -1; //Last item updated static monitoritem items[32]; /* Data received from DIR/FD/SD */ static char OKqstatus[] = "%c000 OK .status\n"; static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n"; /* UI variables and functions */ extern "C" { static gboolean fd_read(gpointer data); static gboolean blink(gpointer data); } void trayMessage(const char *fmt,...); void updateStatusIcon(monitoritem* item); void changeStatusMessage(monitoritem* item, const char *fmt,...); static const char** generateXPM(stateenum newstate, stateenum oldstate); /* Callbacks */ static void TrayIconActivate(GtkWidget *widget, gpointer data); static void TrayIconExit(unsigned int activateTime, unsigned int button); static void TrayIconPopupMenu(unsigned int button, unsigned int activateTime); static void MonitorAbout(GtkWidget *widget, gpointer data); static void MonitorRefresh(GtkWidget *widget, gpointer data); static void IntervalChanged(GtkWidget *widget, gpointer data); static void DaemonChanged(GtkWidget *widget, monitoritem* data); static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data); static guint timerTag; static GtkStatusIcon *mTrayIcon; static GtkWidget *mTrayMenu; static GtkWidget *window; static GtkWidget *textview; static GtkTextBuffer *buffer; static GtkWidget *timeoutspinner; char** xpm_generic_var; static gboolean blinkstate = TRUE; PangoFontDescription *font_desc = NULL; #define CONFIG_FILE "./tray-monitor.conf" /* default configuration file */ static void usage() { fprintf(stderr, _( PROG_COPYRIGHT "Written by Nicolas Boichat (2004)\n" "\nVersion: %s (%s) %s %s %s\n\n" "Usage: tray-monitor [-c config_file] [-d debug_level]\n" " -c set configuration file to file\n" " -dnn set debug level to nn\n" " -t test - read configuration and exit\n" " -? print this message.\n" "\n"), 2004, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER); } static GtkWidget *new_image_button(const gchar *stock_id, const gchar *label_text) { GtkWidget *button; GtkWidget *box; GtkWidget *label; GtkWidget *image; button = gtk_button_new(); box = gtk_hbox_new(FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(box), 2); image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON); label = gtk_label_new(label_text); gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 3); gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 3); gtk_widget_show(image); gtk_widget_show(label); gtk_widget_show(box); gtk_container_add(GTK_CONTAINER(button), box); return button; } int sm_line = 0; #if TRAY_DEBUG_MEMORY gpointer smt_malloc(gsize n_bytes) { return sm_malloc("GLib", sm_line, n_bytes); } gpointer smt_realloc(gpointer mem, gsize n_bytes) { return sm_realloc("GLib", sm_line, mem, n_bytes); } gpointer smt_calloc(gsize n_blocks, gsize n_block_bytes) { return sm_calloc("GLib", sm_line, n_blocks, n_block_bytes); } void smt_free(gpointer mem) { sm_free("Glib", sm_line, mem); } #endif /********************************************************************* * * Main Bacula Tray Monitor -- User Interface Program * */ int main(int argc, char *argv[]) { #if TRAY_DEBUG_MEMORY GMemVTable smvtable; smvtable.malloc = &smt_malloc; smvtable.realloc = &smt_realloc; smvtable.free = &smt_free; smvtable.calloc = &smt_calloc; smvtable.try_malloc = NULL; smvtable.try_realloc = NULL; g_mem_set_vtable(&smvtable); #endif int ch, i; bool test_config = false; DIRRES* dird; CLIENT* filed; STORE* stored; CONFONTRES *con_font; setlocale(LC_ALL, ""); bindtextdomain("bacula", LOCALEDIR); textdomain("bacula"); init_stack_dump(); my_name_is(argc, argv, "tray-monitor"); init_msg(NULL, NULL); working_directory = "/tmp"; struct sigaction sigignore; sigignore.sa_flags = 0; sigignore.sa_handler = SIG_IGN; sigfillset(&sigignore.sa_mask); sigaction(SIGPIPE, &sigignore, NULL); gtk_init(&argc, &argv); while ((ch = getopt(argc, argv, "bc:d:th?f:s:")) != -1) { switch (ch) { case 'c': /* configuration file */ if (configfile != NULL) { free(configfile); } configfile = bstrdup(optarg); break; case 'd': debug_level = atoi(optarg); if (debug_level <= 0) { debug_level = 1; } break; case 't': test_config = true; break; case 'h': case '?': default: usage(); exit(1); } } argc -= optind; //argv += optind; if (argc) { usage(); exit(1); } if (configfile == NULL) { configfile = bstrdup(CONFIG_FILE); } parse_config(configfile); LockRes(); nitems = 0; foreach_res(monitor, R_MONITOR) { nitems++; } if (nitems != 1) { Emsg2(M_ERROR_TERM, 0, _("Error: %d Monitor resources defined in %s. You must define one and only one Monitor resource.\n"), nitems, configfile); } nitems = 0; foreach_res(dird, R_DIRECTOR) { items[nitems].type = R_DIRECTOR; items[nitems].resource = dird; items[nitems].D_sock = NULL; items[nitems].state = warn; items[nitems].oldstate = warn; nitems++; } foreach_res(filed, R_CLIENT) { items[nitems].type = R_CLIENT; items[nitems].resource = filed; items[nitems].D_sock = NULL; items[nitems].state = warn; items[nitems].oldstate = warn; nitems++; } foreach_res(stored, R_STORAGE) { items[nitems].type = R_STORAGE; items[nitems].resource = stored; items[nitems].D_sock = NULL; items[nitems].state = warn; items[nitems].oldstate = warn; nitems++; } UnlockRes(); if (nitems == 0) { Emsg1(M_ERROR_TERM, 0, _("No Client, Storage or Director resource defined in %s\n" "Without that I don't how to get status from the File, Storage or Director Daemon :-(\n"), configfile); } if (test_config) { exit(0); } //Copy the content of xpm_generic in xpm_generic_var to be able to modify it g_assert((xpm_generic_var = (char**)g_malloc(sizeof(xpm_generic)))); for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) { g_assert((xpm_generic_var[i] = (char*)g_malloc((strlen(xpm_generic[i])+1)*sizeof(char)))); strcpy(xpm_generic_var[i], xpm_generic[i]); } (void)WSA_Init(); /* Initialize Windows sockets */ LockRes(); monitor = (MONITOR*)GetNextRes(R_MONITOR, (RES *)NULL); UnlockRes(); if ((monitor->RefreshInterval < 1) || (monitor->RefreshInterval > 600)) { Emsg2(M_ERROR_TERM, 0, _("Invalid refresh interval defined in %s\n" "This value must be greater or equal to 1 second and less or equal to 10 minutes (read value: %d).\n"), configfile, monitor->RefreshInterval); } GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn)); mTrayIcon = gtk_status_icon_new_from_pixbuf(pixbuf); gtk_status_icon_set_tooltip(mTrayIcon, _("Bacula daemon status monitor")); g_signal_connect(G_OBJECT(mTrayIcon), "activate", G_CALLBACK(TrayIconActivate), NULL); g_signal_connect(G_OBJECT(mTrayIcon), "popup-menu", G_CALLBACK(TrayIconPopupMenu), NULL); g_object_unref(G_OBJECT(pixbuf)); mTrayMenu = gtk_menu_new(); GtkWidget *entry; entry = gtk_menu_item_new_with_label(_("Open status window...")); g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconActivate), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry); gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), gtk_separator_menu_item_new()); entry = gtk_menu_item_new_with_label(_("Exit")); g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(TrayIconExit), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(mTrayMenu), entry); gtk_widget_show_all(mTrayMenu); timerTag = g_timeout_add( 1000*monitor->RefreshInterval/nitems, fd_read, NULL ); g_timeout_add( 1000, blink, NULL); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), _("Bacula tray monitor")); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL); //g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL); gtk_container_set_border_width(GTK_CONTAINER(window), 10); GtkWidget* vbox = gtk_vbox_new(FALSE, 10); GtkWidget* daemon_table = gtk_table_new((nitems*2)+2, 3, FALSE); gtk_table_set_col_spacings(GTK_TABLE(daemon_table), 8); GtkWidget* separator = gtk_hseparator_new(); gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, 0, 1); GString *str; GSList *group = NULL; GtkWidget* radio; GtkWidget* align; for (int i = 0; i < nitems; i++) { switch (items[i].type) { case R_DIRECTOR: str = g_string_new(((DIRRES*)(items[i].resource))->hdr.name); g_string_append(str, _(" (DIR)")); break; case R_CLIENT: str = g_string_new(((CLIENT*)(items[i].resource))->hdr.name); g_string_append(str, _(" (FD)")); break; case R_STORAGE: str = g_string_new(((STORE*)(items[i].resource))->hdr.name); g_string_append(str, _(" (SD)")); break; default: continue; } radio = gtk_radio_button_new_with_label(group, str->str); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), i == 0); g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(DaemonChanged), &(items[i])); pixbuf = gdk_pixbuf_new_from_xpm_data(generateXPM(warn, warn)); items[i].image = gtk_image_new_from_pixbuf(pixbuf); items[i].label = gtk_label_new(_("Unknown status.")); align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0); gtk_container_add(GTK_CONTAINER(align), items[i].label); gtk_table_attach(GTK_TABLE(daemon_table), radio, 0, 1, (i*2)+1, (i*2)+2, GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0); gtk_table_attach(GTK_TABLE(daemon_table), items[i].image, 1, 2, (i*2)+1, (i*2)+2, GTK_FILL, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0); gtk_table_attach(GTK_TABLE(daemon_table), align, 2, 3, (i*2)+1, (i*2)+2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0); separator = gtk_hseparator_new(); gtk_table_attach_defaults(GTK_TABLE(daemon_table), separator, 0, 3, (i*2)+2, (i*2)+3); group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)); } gtk_box_pack_start(GTK_BOX(vbox), daemon_table, FALSE, FALSE, 0); textview = gtk_text_view_new(); buffer = gtk_text_buffer_new(NULL); gtk_text_buffer_set_text(buffer, "", -1); /* * Gtk2/pango have different font names. Gnome2 comes with "Monospace 10" */ LockRes(); foreach_res(con_font, R_CONSOLE_FONT) { if (!con_font->fontface) { Dmsg1(400, "No fontface for %s\n", con_font->hdr.name); continue; } Dmsg1(100, "Now loading: %s\n",con_font->fontface); font_desc = pango_font_description_from_string(con_font->fontface); if (font_desc == NULL) { Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n", con_font->hdr.name, con_font->fontface); } else { Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n", con_font->hdr.name, con_font->fontface); break; } } UnlockRes(); if (!font_desc) { font_desc = pango_font_description_from_string("Monospace 10"); } if (!font_desc) { font_desc = pango_font_description_from_string("monospace"); } gtk_widget_modify_font(textview, font_desc); pango_font_description_free(font_desc); gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 20); gtk_text_view_set_right_margin(GTK_TEXT_VIEW(textview), 20); gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE); gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer); gtk_box_pack_start(GTK_BOX(vbox), textview, TRUE, TRUE, 0); GtkWidget* hbox = gtk_hbox_new(FALSE, 10); GtkWidget* hbox2 = gtk_hbox_new(FALSE, 0); GtkWidget* label = gtk_label_new(_("Refresh interval in seconds: ")); gtk_box_pack_start(GTK_BOX(hbox2), label, TRUE, FALSE, 0); GtkAdjustment *spinner_adj = (GtkAdjustment *) gtk_adjustment_new (monitor->RefreshInterval, 1.0, 600.0, 1.0, 5.0, 5.0); timeoutspinner = gtk_spin_button_new (spinner_adj, 1.0, 0); g_signal_connect(G_OBJECT(timeoutspinner), "value-changed", G_CALLBACK(IntervalChanged), NULL); gtk_box_pack_start(GTK_BOX(hbox2), timeoutspinner, TRUE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, FALSE, 0); GtkWidget* button = new_image_button("gtk-refresh", _("Refresh now")); g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorRefresh), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0); button = new_image_button("gtk-help", _("About")); g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(MonitorAbout), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0); button = new_image_button("gtk-close", _("Close")); g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(gtk_widget_hide), G_OBJECT(window)); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER (window), vbox); gtk_widget_show_all(vbox); gtk_main(); g_source_remove(timerTag); sm_line = 0; for (i = 0; i < nitems; i++) { if (items[i].D_sock) { switch (items[i].type) { case R_DIRECTOR: trayMessage(_("Disconnecting from Director %s:%d\n"), ((DIRRES*)items[i].resource)->address, ((DIRRES*)items[i].resource)->DIRport); break; case R_CLIENT: trayMessage(_("Disconnecting from Client %s:%d\n"), ((CLIENT*)items[i].resource)->address, ((CLIENT*)items[i].resource)->FDport); break; case R_STORAGE: trayMessage(_("Disconnecting from Storage %s:%d\n"), ((STORE*)items[i].resource)->address, ((STORE*)items[i].resource)->SDport); break; default: break; } //writecmd(&items[i], "quit"); bnet_sig(items[i].D_sock, BNET_TERMINATE); /* send EOF */ bnet_close(items[i].D_sock); } } (void)WSACleanup(); /* Cleanup Windows sockets */ //Free xpm_generic_var for (i = 0; i < (int)(sizeof(xpm_generic)/sizeof(const char*)); i++) { g_free(xpm_generic_var[i]); } g_free(xpm_generic_var); gtk_object_destroy(GTK_OBJECT(window)); gtk_object_destroy(GTK_OBJECT(mTrayMenu)); term_msg(); #if TRAY_DEBUG_MEMORY sm_dump(false); #endif return 0; } static void MonitorAbout(GtkWidget *widget, gpointer data) { #if HAVE_GTK_2_4 GtkWidget* about = gtk_message_dialog_new_with_markup(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s\n\n" PROG_COPYRIGHT "%s" "\n%s: %s (%s) %s %s %s", _("Bacula Tray Monitor"), 2004, _("Written by Nicolas Boichat\n"), _("Version"), VERSION, BDATE, HOST_OS, DISTNAME, DISTVER); #else GtkWidget* about = gtk_message_dialog_new(GTK_WINDOW(window),GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s\n\n" PROG_COPYRIGHT "%s" "\n%s: %s (%s) %s %s %s", _("Bacula Tray Monitor"), 2004, _("Written by Nicolas Boichat\n"), _("Version"), VERSION, BDATE, HOST_OS, DISTNAME, DISTVER); #endif gtk_dialog_run(GTK_DIALOG(about)); gtk_widget_destroy(about); } static void MonitorRefresh(GtkWidget *widget, gpointer data) { for (int i = 0; i < nitems; i++) { fd_read(NULL); } } static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) { gtk_widget_hide(window); return TRUE; /* do not destroy the window */ } /* * Come here when the user right clicks on the icon. * We display the icon menu. */ static void TrayIconActivate(GtkWidget *widget, gpointer data) { gtk_widget_show(window); } /* * Come here when the user left clicks on the icon. * We popup the status window. */ static void TrayIconPopupMenu(unsigned int activateTime, unsigned int button) { gtk_menu_popup(GTK_MENU(mTrayMenu), NULL, NULL, NULL, NULL, button, gtk_get_current_event_time()); gtk_widget_show(mTrayMenu); } static void TrayIconExit(unsigned int activateTime, unsigned int button) { gtk_main_quit(); } static void IntervalChanged(GtkWidget *widget, gpointer data) { g_source_remove(timerTag); timerTag = g_timeout_add( (guint)( gtk_spin_button_get_value(GTK_SPIN_BUTTON(timeoutspinner))*1000/nitems ), fd_read, NULL ); } static void DaemonChanged(GtkWidget *widget, monitoritem* data) { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { fullitem = -1; for (int i = 0; i < nitems; i++) { if (data == &(items[i])) { fullitem = i; break; } } g_return_if_fail(fullitem != -1); int oldlastupdated = lastupdated; lastupdated = fullitem-1; fd_read(NULL); lastupdated = oldlastupdated; } } static int authenticate_daemon(monitoritem* item, JCR *jcr) { switch (item->type) { case R_DIRECTOR: return authenticate_director(jcr, monitor, (DIRRES*)item->resource); case R_CLIENT: return authenticate_file_daemon(jcr, monitor, (CLIENT*)item->resource); case R_STORAGE: return authenticate_storage_daemon(jcr, monitor, (STORE*)item->resource); default: printf(_("Error, currentitem is not a Client or a Storage..\n")); gtk_main_quit(); return FALSE; } return false; } static gboolean blink(gpointer data) { blinkstate = !blinkstate; for (int i = 0; i < nitems; i++) { updateStatusIcon(&items[i]); } updateStatusIcon(NULL); return true; } static gboolean fd_read(gpointer data) { sm_line++; #if TRAY_DEBUG_MEMORY printf("sm_line=%d\n", sm_line); #endif GtkTextBuffer *newbuffer; GtkTextIter start, stop, nstart, nstop; GSList *list, *it; GString *strlast, *strcurrent; lastupdated++; if (lastupdated == nitems) { lastupdated = 0; } if (lastupdated == fullitem) { newbuffer = gtk_text_buffer_new(NULL); if (items[lastupdated].type == R_DIRECTOR) docmd(&items[lastupdated], "status Director\n", &list); else docmd(&items[lastupdated], "status\n", &list); it = list->next; do { gtk_text_buffer_get_end_iter(newbuffer, &stop); gtk_text_buffer_insert (newbuffer, &stop, ((GString*)it->data)->str, -1); if (it->data) g_string_free((GString*)it->data, TRUE); } while ((it = it->next) != NULL); g_slist_free(list); /* Keep the selection if necessary */ if (gtk_text_buffer_get_selection_bounds(buffer, &start, &stop)) { gtk_text_buffer_get_iter_at_offset(newbuffer, &nstart, gtk_text_iter_get_offset(&start)); gtk_text_buffer_get_iter_at_offset(newbuffer, &nstop, gtk_text_iter_get_offset(&stop )); #if HAVE_GTK_2_4 gtk_text_buffer_select_range(newbuffer, &nstart, &nstop); #else gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "insert"), &nstart); gtk_text_buffer_move_mark(newbuffer, gtk_text_buffer_get_mark(newbuffer, "selection_bound"), &nstop); #endif } g_object_unref(buffer); buffer = newbuffer; gtk_text_view_set_buffer(GTK_TEXT_VIEW(textview), buffer); } getstatus(&items[lastupdated], 1, &strcurrent); getstatus(&items[lastupdated], 0, &strlast); updateStatusIcon(&items[lastupdated]); changeStatusMessage(&items[lastupdated], _("Current job: %s\nLast job: %s"), strcurrent->str, strlast->str); updateStatusIcon(NULL); g_string_free(strcurrent, TRUE); g_string_free(strlast, TRUE); return 1; } void append_error_string(GString* str, int joberrors) { if (joberrors > 1) { g_string_append_printf(str, _(" (%d errors)"), joberrors); } else { g_string_append_printf(str, _(" (%d error)"), joberrors); } } void getstatus(monitoritem* item, int current, GString** str) { GSList *list, *it; stateenum ret = error; int jobid = 0, joberrors = 0; char jobstatus = JS_ErrorTerminated; char num; int k; *str = g_string_sized_new(128); if (current) { if (item->type == R_DIRECTOR) docmd(&items[lastupdated], ".status dir current\n", &list); else docmd(&items[lastupdated], ".status current\n", &list); } else { if (item->type == R_DIRECTOR) docmd(&items[lastupdated], ".status dir last\n", &list); else docmd(&items[lastupdated], ".status last\n", &list); } it = list->next; if ((it == NULL) || (sscanf(((GString*)it->data)->str, OKqstatus, &num) != 1)) { g_string_append_printf(*str, ".status error : %s", (it == NULL) ? "" : ((GString*)it->data)->str); while (((*str)->str[(*str)->len-1] == '\n') || ((*str)->str[(*str)->len-1] == '\r')) { g_string_set_size(*str, (*str)->len-1); } ret = error; } else if ((it = it->next) == NULL) { if (current) { g_string_append(*str, _("No current job.")); } else { g_string_append(*str, _("No last job.")); } ret = idle; } else if ((k = sscanf(((GString*)it->data)->str, DotStatusJob, &jobid, &jobstatus, &joberrors)) == 3) { switch (jobstatus) { case JS_Created: ret = (joberrors > 0) ? warn : running; g_string_append_printf(*str, _("Job status: Created")); append_error_string(*str, joberrors); break; case JS_Running: ret = (joberrors > 0) ? warn : running; g_string_append_printf(*str, _("Job status: Running")); append_error_string(*str, joberrors); break; case JS_Blocked: g_string_append_printf(*str, _("Job status: Blocked")); append_error_string(*str, joberrors); ret = warn; break; case JS_Terminated: g_string_append_printf(*str, _("Job status: Terminated")); append_error_string(*str, joberrors); ret = (joberrors > 0) ? warn : idle; break; case JS_ErrorTerminated: g_string_append_printf(*str, _("Job status: Terminated in error")); append_error_string(*str, joberrors); ret = error; break; case JS_Error: ret = (joberrors > 0) ? warn : running; g_string_append_printf(*str, _("Job status: Error")); append_error_string(*str, joberrors); break; case JS_FatalError: g_string_append_printf(*str, _("Job status: Fatal error")); append_error_string(*str, joberrors); ret = error; break; case JS_Differences: g_string_append_printf(*str, _("Job status: Verify differences")); append_error_string(*str, joberrors); ret = warn; break; case JS_Canceled: g_string_append_printf(*str, _("Job status: Canceled")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitFD: g_string_append_printf(*str, _("Job status: Waiting on File daemon")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitSD: g_string_append_printf(*str, _("Job status: Waiting on the Storage daemon")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitMedia: g_string_append_printf(*str, _("Job status: Waiting for new media")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitMount: g_string_append_printf(*str, _("Job status: Waiting for Mount")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitStoreRes: g_string_append_printf(*str, _("Job status: Waiting for storage resource")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitJobRes: g_string_append_printf(*str, _("Job status: Waiting for job resource")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitClientRes: g_string_append_printf(*str, _("Job status: Waiting for Client resource")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitMaxJobs: g_string_append_printf(*str, _("Job status: Waiting for maximum jobs")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitStartTime: g_string_append_printf(*str, _("Job status: Waiting for start time")); append_error_string(*str, joberrors); ret = warn; break; case JS_WaitPriority: g_string_append_printf(*str, _("Job status: Waiting for higher priority jobs to finish")); append_error_string(*str, joberrors); ret = warn; break; default: g_warning(_("Unknown job status %c."), jobstatus); g_string_append_printf(*str, _("Job status: Unknown(%c)"), jobstatus); append_error_string(*str, joberrors); ret = warn; break; } } else { fprintf(stderr, _("Bad scan : '%s' %d\n"), (it == NULL) ? "" : ((GString*)it->data)->str, k); ret = error; } it = list; do { if (it->data) { g_string_free((GString*)it->data, TRUE); } } while ((it = it->next) != NULL); g_slist_free(list); if (current) { item->state = ret; } else { item->oldstate = ret; } } int docmd(monitoritem* item, const char* command, GSList** list) { int stat; GString* str = NULL; *list = g_slist_alloc(); //str = g_string_sized_new(64); if (!item->D_sock) { memset(&jcr, 0, sizeof(jcr)); DIRRES* dird; CLIENT* filed; STORE* stored; switch (item->type) { case R_DIRECTOR: dird = (DIRRES*)item->resource; trayMessage(_("Connecting to Director %s:%d\n"), dird->address, dird->DIRport); changeStatusMessage(item, _("Connecting to Director %s:%d"), dird->address, dird->DIRport); item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Director daemon"), dird->address, NULL, dird->DIRport, 0); jcr.dir_bsock = item->D_sock; break; case R_CLIENT: filed = (CLIENT*)item->resource; trayMessage(_("Connecting to Client %s:%d\n"), filed->address, filed->FDport); changeStatusMessage(item, _("Connecting to Client %s:%d"), filed->address, filed->FDport); item->D_sock = bnet_connect(NULL, 0, 0, 0, _("File daemon"), filed->address, NULL, filed->FDport, 0); jcr.file_bsock = item->D_sock; break; case R_STORAGE: stored = (STORE*)item->resource; trayMessage(_("Connecting to Storage %s:%d\n"), stored->address, stored->SDport); changeStatusMessage(item, _("Connecting to Storage %s:%d"), stored->address, stored->SDport); item->D_sock = bnet_connect(NULL, 0, 0, 0, _("Storage daemon"), stored->address, NULL, stored->SDport, 0); jcr.store_bsock = item->D_sock; break; default: printf(_("Error, currentitem is not a Client, a Storage or a Director..\n")); gtk_main_quit(); return 0; } if (item->D_sock == NULL) { *list = g_slist_append(*list, g_string_new(_("Cannot connect to daemon.\n"))); changeStatusMessage(item, _("Cannot connect to daemon.")); item->state = error; item->oldstate = error; return 0; } if (!authenticate_daemon(item, &jcr)) { str = g_string_sized_new(64); g_string_printf(str, "ERR=%s\n", item->D_sock->msg); *list = g_slist_append(*list, str); item->state = error; item->oldstate = error; changeStatusMessage(item, _("Authentication error : %s"), item->D_sock->msg); item->D_sock = NULL; return 0; } switch (item->type) { case R_DIRECTOR: trayMessage(_("Opened connection with Director daemon.\n")); changeStatusMessage(item, _("Opened connection with Director daemon.")); break; case R_CLIENT: trayMessage(_("Opened connection with File daemon.\n")); changeStatusMessage(item, _("Opened connection with File daemon.")); break; case R_STORAGE: trayMessage(_("Opened connection with Storage daemon.\n")); changeStatusMessage(item, _("Opened connection with Storage daemon.")); break; default: printf(_("Error, currentitem is not a Client, a Storage or a Director..\n")); gtk_main_quit(); return 0; break; } if (item->type == R_DIRECTOR) { /* Read connection messages... */ GSList *tlist, *it; docmd(item, "", &tlist); /* Usually invalid, but no matter */ it = tlist; do { if (it->data) { g_string_free((GString*)it->data, TRUE); } } while ((it = it->next) != NULL); g_slist_free(tlist); } } if (command[0] != 0) writecmd(item, command); while(1) { if ((stat = bnet_recv(item->D_sock)) >= 0) { *list = g_slist_append(*list, g_string_new(item->D_sock->msg)); } else if (stat == BNET_SIGNAL) { if (item->D_sock->msglen == BNET_EOD) { //fprintf(stderr, "<< EOD >>\n"); return 1; } else if (item->D_sock->msglen == BNET_PROMPT) { //fprintf(stderr, "<< PROMPT >>\n"); *list = g_slist_append(*list, g_string_new(_("<< Error: BNET_PROMPT signal received. >>\n"))); return 0; } else if (item->D_sock->msglen == BNET_HEARTBEAT) { bnet_sig(item->D_sock, BNET_HB_RESPONSE); *list = g_slist_append(*list, g_string_new(_("<< Heartbeat signal received, answered. >>\n"))); } else { str = g_string_sized_new(64); g_string_printf(str, _("<< Unexpected signal received : %s >>\n"), bnet_sig_to_ascii(item->D_sock)); *list = g_slist_append(*list, str); } } else { /* BNET_HARDEOF || BNET_ERROR */ *list = g_slist_append(*list, g_string_new(_("\n"))); item->D_sock = NULL; item->state = error; item->oldstate = error; changeStatusMessage(item, _("Error : BNET_HARDEOF or BNET_ERROR")); //fprintf(stderr, _("<< ERROR >>\n")); return 0; } if (is_bnet_stop(item->D_sock)) { g_string_append_printf(str, _("\n")); item->D_sock = NULL; item->state = error; item->oldstate = error; changeStatusMessage(item, _("Error : Connection closed.")); //fprintf(stderr, "<< STOP >>\n"); return 0; /* error or term */ } } } void writecmd(monitoritem* item, const char* command) { if (item->D_sock) { item->D_sock->msglen = strlen(command); pm_strcpy(&item->D_sock->msg, command); bnet_send(item->D_sock); } } /* Note: Does not seem to work either on Gnome nor KDE... */ void trayMessage(const char *fmt,...) { char buf[512]; va_list arg_ptr; va_start(arg_ptr, fmt); bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr); va_end(arg_ptr); fprintf(stderr, buf); // gtk_tray_icon_send_message(gtk_status_icon_get_tray_icon(mTrayIcon), 5000, (const char*)&buf, -1); } void changeStatusMessage(monitoritem* item, const char *fmt,...) { char buf[512]; va_list arg_ptr; va_start(arg_ptr, fmt); bvsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr); va_end(arg_ptr); gtk_label_set_text(GTK_LABEL(item->label), buf); } void updateStatusIcon(monitoritem* item) { const char** xpm; if (item == NULL) { /* For the current status, select the two worse for blinking, but never blink a D_Sock == NULL error with idle. */ stateenum state1, state2, oldstate; gboolean onenull = FALSE; state1 = idle; state2 = idle; oldstate = idle; for (int i = 0; i < nitems; i++) { if (items[i].D_sock == NULL) onenull = TRUE; if (items[i].state >= state1) { state2 = state1; state1 = items[i].state; } else if (items[i].state > state2) { state2 = items[i].state; } if (items[i].oldstate > oldstate) oldstate = items[i].oldstate; } if ((onenull == TRUE) && (state2 == idle)) { state2 = error; } xpm = generateXPM(blinkstate ? state1 : state2, oldstate); } else { if ((blinkstate) && (item->D_sock != NULL)) { if (item->state > 1) { //Warning or error while running xpm = generateXPM(running, item->oldstate); } else { xpm = generateXPM(idle, item->oldstate); } } else { xpm = generateXPM(item->state, item->oldstate); } } GdkPixbuf* pixbuf = gdk_pixbuf_new_from_xpm_data(xpm); if (item == NULL) { gtk_status_icon_set_from_pixbuf(mTrayIcon, pixbuf); gtk_window_set_icon(GTK_WINDOW(window), pixbuf); } else { gtk_image_set_from_pixbuf(GTK_IMAGE(item->image), pixbuf); } g_object_unref(G_OBJECT(pixbuf)); } /* Note: result should not be stored, as it is a reference to xpm_generic_var */ static const char** generateXPM(stateenum newstate, stateenum oldstate) { char* address = &xpm_generic_var[xpm_generic_first_color][xpm_generic_column]; switch (newstate) { case error: strcpy(address, "ff0000"); break; case idle: strcpy(address, "ffffff"); break; case running: strcpy(address, "00ff00"); break; case warn: strcpy(address, "ffff00"); break; } address = &xpm_generic_var[xpm_generic_second_color][xpm_generic_column]; switch (oldstate) { case error: strcpy(address, "ff0000"); break; case idle: strcpy(address, "ffffff"); break; case running: strcpy(address, "00ff00"); break; case warn: strcpy(address, "ffff00"); break; } return (const char**)xpm_generic_var; }