3 * Bacula GNOME Console interface to the Director
5 * Kern Sibbald, March MMII
11 Copyright (C) 2002-2004 Kern Sibbald and John Walker
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License
15 as published by the Free Software Foundation; either version 2
16 of the License, or (at your option) any later version.
18 This program 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
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #include "interface.h"
34 /* Imported functions */
35 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
36 void select_restore_setup();
39 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
41 /* Exported variables */
42 GtkWidget *console; /* application window */
43 GtkWidget *text1; /* text window */
44 GtkWidget *entry1; /* entry box */
45 GtkWidget *status1; /* status bar */
46 GtkWidget *combo1; /* director combo */
47 GtkWidget *scroll1; /* main scroll bar */
48 GtkWidget *run_dialog; /* run dialog */
49 GtkWidget *dir_dialog; /* director selection dialog */
50 GtkWidget *restore_dialog; /* restore dialog */
51 GtkWidget *restore_file_selection;
52 GtkWidget *dir_select;
53 GtkWidget *about1; /* about box */
54 GtkWidget *label_dialog;
55 GdkFont *text_font = NULL;
56 PangoFontDescription *font_desc;
57 pthread_mutex_t cmd_mutex = PTHREAD_MUTEX_INITIALIZER;
58 pthread_cond_t cmd_wait;
61 BSOCK *UA_sock = NULL;
62 GList *job_list, *client_list, *fileset_list;
63 GList *messages_list, *pool_list, *storage_list;
64 GList *type_list, *level_list;
66 /* Forward referenced functions */
67 void terminate_console(int sig);
70 static gint message_handler(gpointer data);
71 static int initial_connect_to_director(gpointer data);
74 static void set_scroll_bar_to_end(void);
76 /* Static variables */
77 static char *configfile = NULL;
80 static bool director_reader_running = false;
81 static bool at_prompt = false;
82 static bool ready = false;
83 static bool quit = false;
86 #define CONFIG_FILE "./gnome-console.conf" /* default configuration file */
91 "Copyright (C) 2002-2004 Kern Sibbald and John Walker\n"
92 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
93 "Usage: gnome-console [-s] [-c config_file] [-d debug_level] [config_file]\n"
94 " -c <file> set configuration file to file\n"
95 " -dnn set debug level to nn\n"
97 " -t test - read configuration and exit\n"
98 " -? print this message.\n"
99 "\n"), HOST_OS, DISTNAME, DISTVER);
105 /*********************************************************************
107 * Main Bacula GNOME Console -- User Interface Program
110 int main(int argc, char *argv[])
113 int no_signals = TRUE;
114 int test_config = FALSE;
116 const char *gargv[2] = {"gnome-console", NULL};
117 CONFONTRES *con_font;
120 my_name_is(argc, argv, "gnome-console");
121 init_msg(NULL, NULL);
122 working_directory = "/tmp";
124 struct sigaction sigignore;
125 sigignore.sa_flags = 0;
126 sigignore.sa_handler = SIG_IGN;
127 sigfillset(&sigignore.sa_mask);
128 sigaction(SIGPIPE, &sigignore, NULL);
130 if ((stat=pthread_cond_init(&cmd_wait, NULL)) != 0) {
131 Emsg1(M_ABORT, 0, _("Pthread cond init error = %s\n"),
135 gnome_init("bacula", VERSION, gargc, (char **)&gargv);
137 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
139 case 'c': /* configuration file */
140 if (configfile != NULL)
142 configfile = bstrdup(optarg);
146 debug_level = atoi(optarg);
147 if (debug_level <= 0)
151 case 's': /* turn off signals */
169 init_signals(terminate_console);
176 if (configfile == NULL) {
177 configfile = bstrdup(CONFIG_FILE);
180 parse_config(configfile);
184 foreach_res(dir, R_DIRECTOR) {
189 Emsg1(M_ERROR_TERM, 0, _("No director resource defined in %s\n"
190 "Without that I don't how to speak to the Director :-(\n"), configfile);
195 console = create_console();
196 gtk_window_set_default_size(GTK_WINDOW(console), 800, 700);
197 run_dialog = create_RunDialog();
198 label_dialog = create_label_dialog();
199 restore_dialog = create_RestoreDialog();
200 about1 = create_about1();
202 text1 = lookup_widget(console, "text1");
203 entry1 = lookup_widget(console, "entry1");
204 status1 = lookup_widget(console, "status1");
205 scroll1 = lookup_widget(console, "scroll1");
207 select_restore_setup();
209 gtk_widget_show(console);
212 * Thanks to Phil Stracchino for providing the font configuration code.
214 text_font = gdk_font_load("-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-koi8-r");
216 text_font = gdk_font_load("-Bigelow & Holmes-lucida console-medium-r-semi condensed-*-12-0-100-100-m-0-iso8859-1");
217 * and, new automagic:font specification!
221 foreach_res(con_font, R_CONSOLE_FONT) {
222 if (!con_font->fontface) {
223 Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
226 text_font = gdk_font_load(con_font->fontface);
227 if (text_font == NULL) {
228 Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
229 con_font->hdr.name, con_font->fontface);
231 Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
232 con_font->hdr.name, con_font->fontface);
238 if (text_font == NULL) {
239 Dmsg1(400, "Attempting to load fallback font %s\n",
240 "-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
241 text_font = gdk_font_load("-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
243 font_desc = pango_font_description_from_string("LucidaTypewriter 9");
244 gtk_widget_modify_font(console, font_desc);
245 gtk_widget_modify_font(text1, font_desc);
246 gtk_widget_modify_font(entry1, font_desc);
247 gtk_widget_modify_font(status1, font_desc);
248 pango_font_description_free(font_desc);
251 terminate_console(0);
255 initial = gtk_timeout_add(100, initial_connect_to_director, (gpointer)NULL);
259 disconnect_from_director((gpointer)NULL);
264 * Every 5 seconds, ask the Director for our
267 static gint message_handler(gpointer data)
269 if (ready && UA_sock) {
270 bnet_fsend(UA_sock, ".messages");
275 int disconnect_from_director(gpointer data)
278 set_status(_(" Not Connected"));
281 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
289 * Called just after the main loop is started to allow
290 * us to connect to the Director.
292 static int initial_connect_to_director(gpointer data)
294 gtk_timeout_remove(initial);
295 if (connect_to_director(data)) {
296 start_director_reader(data);
298 gtk_timeout_add(5000, message_handler, (gpointer)NULL);
302 static GList *get_list(char *cmd)
309 while (bnet_recv(UA_sock) > 0) {
310 strip_trailing_junk(UA_sock->msg);
311 msg = (char *)malloc(strlen(UA_sock->msg) + 1);
312 strcpy(msg, UA_sock->msg);
313 options = g_list_append(options, msg);
319 static GList *get_and_fill_combo(GtkWidget *dialog, const char *combo_name, const char *cm)
324 combo = lookup_widget(dialog, combo_name);
325 options = get_list(cmd);
326 if (combo && options) {
327 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
332 static void fill_combo(GtkWidget *dialog, const char *combo_name, GList *options)
336 combo = lookup_widget(dialog, combo_name);
337 if (combo && options) {
338 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
345 * Connect to Director. If there are more than one, put up
346 * a modal dialog so that the user chooses one.
348 int connect_to_director(gpointer data)
361 foreach_res(dir, R_DIRECTOR) {
362 dirs = g_list_append(dirs, dir->hdr.name);
365 dir_dialog = create_SelectDirectorDialog();
366 combo = lookup_widget(dir_dialog, "combo1");
367 dir_select = lookup_widget(dir_dialog, "dirselect");
369 gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);
371 gtk_widget_show(dir_dialog);
375 gchar *ecmd = gtk_editable_get_chars((GtkEditable *)dir_select, 0, -1);
376 dir = (DIRRES *)GetResWithName(R_DIRECTOR, ecmd);
378 g_free(ecmd); /* release director name string */
384 gtk_widget_destroy(dir_dialog);
387 /* Just take the first Director */
389 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
397 memset(&jcr, 0, sizeof(jcr));
399 set_statusf(_(" Connecting to Director %s:%d"), dir->address,dir->DIRport);
400 set_textf(_("Connecting to Director %s:%d\n\n"), dir->address,dir->DIRport);
402 while (gtk_events_pending()) { /* fully paint screen */
403 gtk_main_iteration();
405 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
406 NULL, dir->DIRport, 0);
407 if (UA_sock == NULL) {
411 jcr.dir_bsock = UA_sock;
413 /* If cons==NULL, default console will be used */
414 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
416 if (!authenticate_director(&jcr, dir, cons)) {
417 set_text(UA_sock->msg, UA_sock->msglen);
421 set_status(" Initializing ...");
423 bnet_fsend(UA_sock, "autodisplay on");
425 /* Read and display all initial messages */
426 while (bnet_recv(UA_sock) > 0) {
427 set_text(UA_sock->msg, UA_sock->msglen);
431 while (gtk_events_pending()) {
432 gtk_main_iteration();
435 /* Fill the run_dialog combo boxes */
436 job_list = get_and_fill_combo(run_dialog, "combo_job", ".jobs");
437 client_list = get_and_fill_combo(run_dialog, "combo_client", ".clients");
438 fileset_list = get_and_fill_combo(run_dialog, "combo_fileset", ".filesets");
439 messages_list = get_and_fill_combo(run_dialog, "combo_messages", ".msgs");
440 pool_list = get_and_fill_combo(run_dialog, "combo_pool", ".pools");
441 storage_list = get_and_fill_combo(run_dialog, "combo_storage", ".storage");
442 type_list = get_and_fill_combo(run_dialog, "combo_type", ".types");
443 level_list = get_and_fill_combo(run_dialog, "combo_level", ".levels");
445 /* Fill the label dialog combo boxes */
446 fill_combo(label_dialog, "label_combo_storage", storage_list);
447 fill_combo(label_dialog, "label_combo_pool", pool_list);
450 /* Fill the restore_dialog combo boxes */
451 fill_combo(restore_dialog, "combo_restore_job", job_list);
452 fill_combo(restore_dialog, "combo_restore_client", client_list);
453 fill_combo(restore_dialog, "combo_restore_fileset", fileset_list);
454 fill_combo(restore_dialog, "combo_restore_pool", pool_list);
455 fill_combo(restore_dialog, "combo_restore_storage", storage_list);
457 set_status(" Connected");
461 void write_director(const gchar *msg)
465 set_status(_(" Processing command ..."));
466 UA_sock->msglen = strlen(msg);
467 pm_strcpy(&UA_sock->msg, msg);
470 if (strcmp(msg, ".quit") == 0 || strcmp(msg, ".exit") == 0) {
471 disconnect_from_director((gpointer)NULL);
477 void read_director(gpointer data, gint fd, GdkInputCondition condition)
481 if (!UA_sock || UA_sock->fd != fd) {
484 stat = bnet_recv(UA_sock);
490 set_text(UA_sock->msg, UA_sock->msglen);
493 if (is_bnet_stop(UA_sock)) { /* error or term request */
497 /* Must be a signal -- either do something or ignore it */
498 if (UA_sock->msglen == BNET_PROMPT) {
500 set_status(_(" At prompt waiting for input ..."));
502 if (UA_sock->msglen == BNET_EOD) {
510 void start_director_reader(gpointer data)
513 if (director_reader_running || !UA_sock) {
516 tag = gdk_input_add(UA_sock->fd, GDK_INPUT_READ, read_director, NULL);
517 director_reader_running = true;
520 void stop_director_reader(gpointer data)
522 if (!director_reader_running) {
525 gdk_input_remove(tag);
526 gdk_input_remove(tag);
527 gdk_input_remove(tag);
528 gdk_input_remove(tag);
529 gdk_input_remove(tag);
530 gdk_input_remove(tag);
531 gdk_input_remove(tag);
532 gdk_input_remove(tag);
533 gdk_input_remove(tag);
534 gdk_input_remove(tag);
535 gdk_input_remove(tag);
536 director_reader_running = false;
541 /* Cleanup and then exit */
542 void terminate_console(int sig)
544 static int already_here = FALSE;
546 if (already_here) /* avoid recursive temination problems */
549 disconnect_from_director((gpointer)NULL);
555 /* Buffer approx 2000 lines -- assume 60 chars/line */
556 #define MAX_TEXT_CHARS (2000 * 60)
557 static int text_chars = 0;
559 static void truncate_text_chars()
561 GtkTextBuffer *textbuf;
562 GtkTextIter iter, iter2;
564 int del_chars = MAX_TEXT_CHARS / 4;
566 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
567 len = gtk_text_buffer_get_char_count(textbuf);
568 gtk_text_buffer_get_iter_at_offset (textbuf, &iter, 0);
569 gtk_text_buffer_get_iter_at_offset (textbuf, &iter2, del_chars);
570 gtk_text_buffer_delete (textbuf, &iter, &iter2);
571 text_chars -= del_chars;
572 len = gtk_text_buffer_get_char_count(textbuf);
573 gtk_text_iter_set_offset(&iter, len);
576 void set_textf(const char *fmt, ...)
581 va_start(arg_ptr, fmt);
582 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
587 void set_text(const char *buf, int len)
589 GtkTextBuffer *textbuf;
593 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
594 buf_len = gtk_text_buffer_get_char_count(textbuf);
595 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
596 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
598 if (text_chars > MAX_TEXT_CHARS) {
599 truncate_text_chars();
601 set_scroll_bar_to_end();
604 void set_statusf(const char *fmt, ...)
609 va_start(arg_ptr, fmt);
610 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
612 gtk_label_set_text(GTK_LABEL(status1), buf);
613 // set_scroll_bar_to_end();
617 void set_status_ready()
619 gtk_label_set_text(GTK_LABEL(status1), " Ready");
621 // set_scroll_bar_to_end();
624 void set_status(const char *buf)
626 gtk_label_set_text(GTK_LABEL(status1), buf);
627 // set_scroll_bar_to_end();
631 static void set_scroll_bar_to_end(void)
633 GtkTextBuffer* textbuf = NULL;
637 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
638 buf_len = gtk_text_buffer_get_char_count(textbuf);
639 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
640 gtk_text_iter_set_offset(&iter, buf_len);
641 gtk_text_buffer_place_cursor(textbuf, &iter);
642 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text1),
643 gtk_text_buffer_get_mark(textbuf, "insert"),