3 * Bacula GNOME Console interface to the Director
5 * Kern Sibbald, March MMII
11 Copyright (C) 2002 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);
37 /* Exported variables */
38 GtkWidget *app1; /* application window */
39 GtkWidget *text1; /* text window */
40 GtkWidget *entry1; /* entry box */
41 GtkWidget *status1; /* status bar */
42 GtkWidget *combo1; /* director combo */
43 GtkWidget *scroll1; /* main scroll bar */
44 GtkWidget *run_dialog; /* run dialog */
45 GtkWidget *dir_dialog; /* director selection dialog */
46 GtkWidget *restore_dialog; /* restore dialog */
47 GtkWidget *restore_files; /* restore files dialog */
48 GtkWidget *dir_select;
49 GtkWidget *about1; /* about box */
50 GtkWidget *label_dialog;
51 GdkFont *text_font = NULL;
52 PangoFontDescription *font_desc;
53 pthread_mutex_t cmd_mutex = PTHREAD_MUTEX_INITIALIZER;
54 pthread_cond_t cmd_wait;
57 BSOCK *UA_sock = NULL;
58 GList *job_list, *client_list, *fileset_list;
59 GList *messages_list, *pool_list, *storage_list;
60 GList *type_list, *level_list;
62 /* Forward referenced functions */
63 static void terminate_console(int sig);
64 static gint message_handler(gpointer data);
65 static int initial_connect_to_director(gpointer data);
66 static void set_scroll_bar_to_end(void);
68 /* Static variables */
69 static char *configfile = NULL;
73 static int director_reader_running = FALSE;
74 static bool at_prompt = false;
75 static bool ready = false;
76 static bool quit = false;
79 #define CONFIG_FILE "./gnome-console.conf" /* default configuration file */
84 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
85 "Usage: gnome-console [-s] [-c config_file] [-d debug_level] [config_file]\n"
86 " -c <file> set configuration file to file\n"
87 " -dnn set debug level to nn\n"
89 " -t test - read configuration and exit\n"
90 " -? print this message.\n"
91 "\n"), HOST_OS, DISTNAME, DISTVER);
97 /*********************************************************************
99 * Main Bacula GNOME Console -- User Interface Program
102 int main(int argc, char *argv[])
105 int no_signals = TRUE;
106 int test_config = FALSE;
108 char *gargv[2] = {"gnome-console", NULL};
111 my_name_is(argc, argv, "gnome-console");
112 init_msg(NULL, NULL);
113 working_directory = "/tmp";
115 struct sigaction sigignore;
116 sigignore.sa_flags = 0;
117 sigignore.sa_handler = SIG_IGN;
118 sigfillset(&sigignore.sa_mask);
119 sigaction(SIGPIPE, &sigignore, NULL);
121 if ((stat=pthread_cond_init(&cmd_wait, NULL)) != 0) {
122 Emsg1(M_ABORT, 0, _("Pthread cond init error = %s\n"),
126 gnome_init("bacula", VERSION, gargc, (char **)&gargv);
128 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
130 case 'c': /* configuration file */
131 if (configfile != NULL)
133 configfile = bstrdup(optarg);
137 debug_level = atoi(optarg);
138 if (debug_level <= 0)
142 case 's': /* turn off signals */
161 init_signals(terminate_console);
168 if (configfile == NULL) {
169 configfile = bstrdup(CONFIG_FILE);
172 parse_config(configfile);
176 for (dir=NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
181 Emsg1(M_ERROR_TERM, 0, _("No director resource defined in %s\n\
182 Without that I don't how to speak to the Director :-(\n"), configfile);
186 terminate_console(0);
191 app1 = create_app1();
192 gtk_window_set_default_size(GTK_WINDOW(app1), 800, 700);
193 run_dialog = create_RunDialog();
194 label_dialog = create_label_dialog();
195 restore_dialog = create_restore_dialog();
196 restore_files = create_restore_files();
197 about1 = create_about1();
199 gtk_widget_show(app1);
201 text1 = lookup_widget(app1, "text1");
202 entry1 = lookup_widget(app1, "entry1");
203 status1 = lookup_widget(app1, "status1");
204 scroll1 = lookup_widget(app1, "scroll1");
207 * Thanks to Phil Stracchino for providing the font configuration code.
209 text_font = gdk_font_load("-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-koi8-r");
211 text_font = gdk_font_load("-Bigelow & Holmes-lucida console-medium-r-semi condensed-*-12-0-100-100-m-0-iso8859-1");
212 * and, new automagic:font specification!
216 for (con = NULL; (con = (CONRES *)GetNextRes(R_CONSOLE, (RES *)con)); ) {
217 text_font = gdk_font_load(con->fontface);
218 if (text_font == NULL) {
219 Dmsg2(404, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
220 con->hdr.name, con->fontface);
222 Dmsg2(404, "ConsoleFont \"%s\" (%s) loaded.\n",
223 con->hdr.name, con->fontface);
229 if (text_font == NULL) {
230 Dmsg1(100, "Attempting to load fallback font %s\n",
231 "-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
232 text_font = gdk_font_load("-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
234 font_desc = pango_font_description_from_string("LucidaTypewriter 9");
235 gtk_widget_modify_font (app1, font_desc);
236 gtk_widget_modify_font (text1, font_desc);
237 gtk_widget_modify_font (entry1, font_desc);
238 gtk_widget_modify_font (status1, font_desc);
239 pango_font_description_free (font_desc);
242 initial = gtk_timeout_add(100, initial_connect_to_director, (gpointer)NULL);
246 disconnect_from_director((gpointer)NULL);
251 * Every 5 seconds, ask the Director for our
254 static gint message_handler(gpointer data)
256 if (ready && UA_sock) {
257 bnet_fsend(UA_sock, ".messages");
262 int disconnect_from_director(gpointer data)
265 set_status(_(" Not Connected"));
267 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
275 * Called just after the main loop is started to allow
276 * us to connect to the Director.
278 static int initial_connect_to_director(gpointer data)
280 gtk_timeout_remove(initial);
281 if (connect_to_director(data)) {
282 start_director_reader(data);
284 gtk_timeout_add(5000, message_handler, (gpointer)NULL);
288 static GList *get_list(char *cmd)
295 while (bnet_recv(UA_sock) > 0) {
296 strip_trailing_junk(UA_sock->msg);
297 msg = (char *)malloc(strlen(UA_sock->msg) + 1);
298 strcpy(msg, UA_sock->msg);
299 options = g_list_append(options, msg);
305 static GList *get_and_fill_combo(GtkWidget *dialog, char *combo_name, char *cmd)
310 combo = lookup_widget(dialog, combo_name);
311 options = get_list(cmd);
312 if (combo && options) {
313 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
318 static void fill_combo(GtkWidget *dialog, char *combo_name, GList *options)
322 combo = lookup_widget(dialog, combo_name);
324 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
331 * Connect to Director. If there are more than one, put up
332 * a modal dialog so that the user chooses one.
334 int connect_to_director(gpointer data)
348 for (dir = NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
349 sprintf(buf, "%s at %s:%d", dir->hdr.name, dir->address,
352 dirs = g_list_append(dirs, dir->hdr.name);
355 dir_dialog = create_SelectDirectorDialog();
356 combo = lookup_widget(dir_dialog, "combo1");
357 dir_select = lookup_widget(dir_dialog, "dirselect");
358 gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);
359 printf("dialog run\n");
360 gtk_widget_show(dir_dialog);
364 gchar *ecmd = gtk_editable_get_chars((GtkEditable *)dir_select, 0, -1);
365 dir = (DIRRES *)GetResWithName(R_DIRECTOR, ecmd);
367 g_free(ecmd); /* release director name string */
373 gtk_widget_destroy(dir_dialog);
376 /* Just take the first Director */
378 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
383 printf("dir is NULL\n");
387 memset(&jcr, 0, sizeof(jcr));
389 set_statusf(_(" Connecting to Director %s:%d"), dir->address,dir->DIRport);
390 set_textf(_("Connecting to Director %s:%d\n\n"), dir->address,dir->DIRport);
392 while (gtk_events_pending()) { /* fully paint screen */
393 gtk_main_iteration();
395 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
396 NULL, dir->DIRport, 0);
397 if (UA_sock == NULL) {
401 jcr.dir_bsock = UA_sock;
402 if (!authenticate_director(&jcr, dir)) {
403 set_text(UA_sock->msg, UA_sock->msglen);
407 set_status(" Initializing ...");
409 bnet_fsend(UA_sock, "autodisplay on");
411 /* Read and display all initial messages */
412 while (bnet_recv(UA_sock) > 0) {
413 set_text(UA_sock->msg, UA_sock->msglen);
417 while (gtk_events_pending()) {
418 gtk_main_iteration();
421 /* Fill the run_dialog combo boxes */
422 job_list = get_and_fill_combo(run_dialog, "combo_job", ".jobs");
423 client_list = get_and_fill_combo(run_dialog, "combo_client", ".clients");
424 fileset_list = get_and_fill_combo(run_dialog, "combo_fileset", ".filesets");
425 messages_list = get_and_fill_combo(run_dialog, "combo_messages", ".msgs");
426 pool_list = get_and_fill_combo(run_dialog, "combo_pool", ".pools");
427 storage_list = get_and_fill_combo(run_dialog, "combo_storage", ".storage");
428 type_list = get_and_fill_combo(run_dialog, "combo_type", ".types");
429 level_list = get_and_fill_combo(run_dialog, "combo_level", ".levels");
431 fill_combo(label_dialog, "label_combo_storage", storage_list);
432 fill_combo(label_dialog, "label_combo_pool", pool_list);
434 set_status(" Connected");
438 void write_director(gchar *msg)
442 set_status(_(" Processing command ..."));
443 UA_sock->msglen = strlen(msg);
444 pm_strcpy(&UA_sock->msg, msg);
447 if (strcmp(msg, ".quit") == 0 || strcmp(msg, ".exit") == 0) {
448 disconnect_from_director((gpointer)NULL);
453 void read_director(gpointer data, gint fd, GdkInputCondition condition)
457 if (!UA_sock || UA_sock->fd != fd) {
460 stat = bnet_recv(UA_sock);
466 set_text(UA_sock->msg, UA_sock->msglen);
469 if (is_bnet_stop(UA_sock)) { /* error or term request */
473 /* Must be a signal -- either do something or ignore it */
474 if (UA_sock->msglen == BNET_PROMPT) {
476 set_status(_(" At prompt waiting for input ..."));
478 if (UA_sock->msglen == BNET_EOD) {
486 void start_director_reader(gpointer data)
489 if (director_reader_running || !UA_sock) {
492 director_reader_running = TRUE;
494 tag = gdk_input_add(UA_sock->fd, GDK_INPUT_READ, read_director, NULL);
497 void stop_director_reader(gpointer data)
499 if (!director_reader_running) {
502 gdk_input_remove(tag);
503 director_reader_running = FALSE;
508 /* Cleanup and then exit */
509 static void terminate_console(int sig)
511 static int already_here = FALSE;
513 if (already_here) /* avoid recursive temination problems */
516 disconnect_from_director((gpointer)NULL);
522 /* Buffer approx 2000 lines -- assume 60 chars/line */
523 #define MAX_TEXT_CHARS (2000 * 60)
524 static int text_chars = 0;
526 static void truncate_text_chars()
528 GtkTextBuffer *textbuf;
529 GtkTextIter iter, iter2;
531 int del_chars = MAX_TEXT_CHARS / 4;
533 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
534 len = gtk_text_buffer_get_char_count(textbuf);
535 gtk_text_buffer_get_iter_at_offset (textbuf, &iter, 0);
536 gtk_text_buffer_get_iter_at_offset (textbuf, &iter2, del_chars);
537 gtk_text_buffer_delete (textbuf, &iter, &iter2);
538 text_chars -= del_chars;
539 len = gtk_text_buffer_get_char_count(textbuf);
540 gtk_text_iter_set_offset(&iter, len);
543 void set_textf(char *fmt, ...)
548 va_start(arg_ptr, fmt);
549 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
554 void set_text(char *buf, int len)
556 GtkTextBuffer *textbuf;
560 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
561 buf_len = gtk_text_buffer_get_char_count(textbuf);
562 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
563 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
565 if (text_chars > MAX_TEXT_CHARS) {
566 truncate_text_chars();
568 set_scroll_bar_to_end();
571 void set_statusf(char *fmt, ...)
576 va_start(arg_ptr, fmt);
577 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
579 gtk_label_set_text(GTK_LABEL(status1), buf);
580 // set_scroll_bar_to_end();
584 void set_status_ready()
586 gtk_label_set_text(GTK_LABEL(status1), " Ready");
588 // set_scroll_bar_to_end();
591 void set_status(char *buf)
593 gtk_label_set_text(GTK_LABEL(status1), buf);
594 // set_scroll_bar_to_end();
598 static void set_scroll_bar_to_end(void)
600 GtkTextBuffer* textbuf = NULL;
604 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
605 buf_len = gtk_text_buffer_get_char_count(textbuf);
606 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
607 gtk_text_iter_set_offset(&iter, buf_len);
608 gtk_text_buffer_place_cursor(textbuf, &iter);
609 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text1),
610 gtk_text_buffer_get_mark(textbuf, "insert"),