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;
54 pthread_mutex_t cmd_mutex = PTHREAD_MUTEX_INITIALIZER;
55 pthread_cond_t cmd_wait;
57 int cmd_ready = FALSE;
59 BSOCK *UA_sock = NULL;
60 GList *job_list, *client_list, *fileset_list;
61 GList *messages_list, *pool_list, *storage_list;
62 GList *type_list, *level_list;
64 /* Forward referenced functions */
65 static void terminate_console(int sig);
66 static gint message_handler(gpointer data);
67 static int initial_connect_to_director(gpointer data);
69 /* Static variables */
70 static char *configfile = NULL;
74 static int director_reader_running = FALSE;
75 static int at_prompt = FALSE;
76 static int ready = FALSE;
77 static int quit = FALSE;
80 #define CONFIG_FILE "./gnome-console.conf" /* default configuration file */
85 "\nVersion: " VERSION " (" BDATE ") %s %s %s\n\n"
86 "Usage: gnome-console [-s] [-c config_file] [-d debug_level] [config_file]\n"
87 " -c <file> set configuration file to file\n"
88 " -dnn set debug level to nn\n"
90 " -t test - read configuration and exit\n"
91 " -? print this message.\n"
92 "\n"), HOST_OS, DISTNAME, DISTVER);
98 /*********************************************************************
100 * Main Bacula GNOME Console -- User Interface Program
103 int main(int argc, char *argv[])
106 int no_signals = TRUE;
107 int test_config = FALSE;
109 char *gargv[2] = {"gnome-console", NULL};
112 my_name_is(argc, argv, "gnome-console");
113 init_msg(NULL, NULL);
114 working_directory = "/tmp";
116 struct sigaction sigignore;
117 sigignore.sa_flags = 0;
118 sigignore.sa_handler = SIG_IGN;
119 sigfillset(&sigignore.sa_mask);
120 sigaction(SIGPIPE, &sigignore, NULL);
122 if ((stat=pthread_cond_init(&cmd_wait, NULL)) != 0) {
123 Emsg1(M_ABORT, 0, _("Pthread cond init error = %s\n"),
127 gnome_init("bacula", VERSION, gargc, (char **)&gargv);
129 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
131 case 'c': /* configuration file */
132 if (configfile != NULL)
134 configfile = bstrdup(optarg);
138 debug_level = atoi(optarg);
139 if (debug_level <= 0)
143 case 's': /* turn off signals */
162 init_signals(terminate_console);
169 if (configfile == NULL) {
170 configfile = bstrdup(CONFIG_FILE);
173 parse_config(configfile);
177 for (dir=NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
182 Emsg1(M_ERROR_TERM, 0, _("No director resource defined in %s\n\
183 Without that I don't how to speak to the Director :-(\n"), configfile);
187 terminate_console(0);
192 app1 = create_app1();
193 gtk_window_set_default_size(GTK_WINDOW(app1), 800, 700);
194 run_dialog = create_RunDialog();
195 label_dialog = create_label_dialog();
196 restore_dialog = create_restore_dialog();
197 restore_files = create_restore_files();
198 about1 = create_about1();
200 gtk_widget_show(app1);
202 text1 = lookup_widget(app1, "text1");
203 entry1 = lookup_widget(app1, "entry1");
204 status1 = lookup_widget(app1, "status1");
205 scroll1 = lookup_widget(app1, "scroll1");
208 * Thanks to Phil Stracchino for providing the font configuration code.
210 text_font = gdk_font_load("-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-koi8-r");
212 text_font = gdk_font_load("-Bigelow & Holmes-lucida console-medium-r-semi condensed-*-12-0-100-100-m-0-iso8859-1");
213 * and, new automagic:font specification!
217 for (con = NULL; (con = (CONRES *)GetNextRes(R_CONSOLE, (RES *)con)); ) {
218 text_font = gdk_font_load(con->fontface);
219 if (text_font == NULL) {
220 Dmsg2(404, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
221 con->hdr.name, con->fontface);
223 Dmsg2(404, "ConsoleFont \"%s\" (%s) loaded.\n",
224 con->hdr.name, con->fontface);
230 if (text_font == NULL) {
231 Dmsg1(100, "Attempting to load fallback font %s\n",
232 "-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
233 text_font = gdk_font_load("-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
235 font_desc = pango_font_description_from_string("LucidaTypewriter 9");
236 gtk_widget_modify_font (app1, font_desc);
237 gtk_widget_modify_font (text1, font_desc);
238 gtk_widget_modify_font (entry1, font_desc);
239 gtk_widget_modify_font (status1, font_desc);
240 pango_font_description_free (font_desc);
243 initial = gtk_timeout_add(100, initial_connect_to_director, (gpointer)NULL);
247 disconnect_from_director((gpointer)NULL);
252 * Every 5 seconds, ask the Director for our
255 static gint message_handler(gpointer data)
257 if (ready && UA_sock) {
258 bnet_fsend(UA_sock, ".messages");
263 int disconnect_from_director(gpointer data)
266 set_status(_(" Not Connected"));
268 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
276 * Called just after the main loop is started to allow
277 * us to connect to the Director.
279 static int initial_connect_to_director(gpointer data)
281 gtk_timeout_remove(initial);
282 if (connect_to_director(data)) {
283 start_director_reader(data);
285 gtk_timeout_add(5000, message_handler, (gpointer)NULL);
289 static GList *get_list(char *cmd)
296 while (bnet_recv(UA_sock) > 0) {
297 strip_trailing_junk(UA_sock->msg);
298 msg = (char *)malloc(strlen(UA_sock->msg) + 1);
299 strcpy(msg, UA_sock->msg);
300 options = g_list_append(options, msg);
306 static GList *get_and_fill_combo(GtkWidget *dialog, char *combo_name, char *cmd)
311 combo = lookup_widget(dialog, combo_name);
312 options = get_list(cmd);
313 if (combo && options) {
314 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
319 static void fill_combo(GtkWidget *dialog, char *combo_name, GList *options)
323 combo = lookup_widget(dialog, combo_name);
325 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
332 * Connect to Director. If there are more than one, put up
333 * a modal dialog so that the user chooses one.
335 int connect_to_director(gpointer data)
349 for (dir = NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
350 sprintf(buf, "%s at %s:%d", dir->hdr.name, dir->address,
353 dirs = g_list_append(dirs, dir->hdr.name);
356 dir_dialog = create_SelectDirectorDialog();
357 combo = lookup_widget(dir_dialog, "combo1");
358 dir_select = lookup_widget(dir_dialog, "dirselect");
359 gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);
360 printf("dialog run\n");
361 gtk_widget_show(dir_dialog);
365 gchar *ecmd = gtk_editable_get_chars((GtkEditable *)dir_select, 0, -1);
366 dir = (DIRRES *)GetResWithName(R_DIRECTOR, ecmd);
368 g_free(ecmd); /* release director name string */
374 gtk_widget_destroy(dir_dialog);
377 /* Just take the first Director */
379 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
384 printf("dir is NULL\n");
388 memset(&jcr, 0, sizeof(jcr));
390 set_statusf(_(" Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
391 set_textf(_("Connecting to Director %s:%d\n"), dir->address,dir->DIRport);
393 // while (gtk_events_pending()) { /* fully paint screen */
394 // gtk_main_iteration();
396 UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address,
397 NULL, dir->DIRport, 0);
398 if (UA_sock == NULL) {
402 jcr.dir_bsock = UA_sock;
403 if (!authenticate_director(&jcr, dir)) {
404 set_text(UA_sock->msg, UA_sock->msglen);
408 set_status(" Initializing ...");
410 bnet_fsend(UA_sock, "autodisplay on");
412 /* Read and display all initial messages */
413 while (bnet_recv(UA_sock) > 0) {
414 set_text(UA_sock->msg, UA_sock->msglen);
418 while (gtk_events_pending()) {
419 gtk_main_iteration();
422 /* Fill the run_dialog combo boxes */
423 job_list = get_and_fill_combo(run_dialog, "combo_job", ".jobs");
424 client_list = get_and_fill_combo(run_dialog, "combo_client", ".clients");
425 fileset_list = get_and_fill_combo(run_dialog, "combo_fileset", ".filesets");
426 messages_list = get_and_fill_combo(run_dialog, "combo_messages", ".msgs");
427 pool_list = get_and_fill_combo(run_dialog, "combo_pool", ".pools");
428 storage_list = get_and_fill_combo(run_dialog, "combo_storage", ".storage");
429 type_list = get_and_fill_combo(run_dialog, "combo_type", ".types");
430 level_list = get_and_fill_combo(run_dialog, "combo_level", ".levels");
432 fill_combo(label_dialog, "label_combo_storage", storage_list);
433 fill_combo(label_dialog, "label_combo_pool", pool_list);
435 set_status(" Connected");
439 void write_director(gchar *msg)
443 set_status(_(" Processing command ..."));
444 UA_sock->msglen = strlen(msg);
445 pm_strcpy(&UA_sock->msg, msg);
448 if (strcmp(msg, ".quit") == 0 || strcmp(msg, ".exit") == 0) {
449 disconnect_from_director((gpointer)NULL);
454 void read_director(gpointer data, gint fd, GdkInputCondition condition)
458 if (!UA_sock || UA_sock->fd != fd) {
461 stat = bnet_recv(UA_sock);
467 set_text(UA_sock->msg, UA_sock->msglen);
470 if (is_bnet_stop(UA_sock)) { /* error or term request */
474 /* Must be a signal -- either do something or ignore it */
475 if (UA_sock->msglen == BNET_PROMPT) {
477 set_status(_(" At prompt waiting for input ..."));
479 if (UA_sock->msglen == BNET_EOD) {
487 void start_director_reader(gpointer data)
490 if (director_reader_running || !UA_sock) {
493 director_reader_running = TRUE;
495 tag = gdk_input_add(UA_sock->fd, GDK_INPUT_READ, read_director, NULL);
498 void stop_director_reader(gpointer data)
500 if (!director_reader_running) {
503 gdk_input_remove(tag);
504 director_reader_running = FALSE;
509 /* Cleanup and then exit */
510 static void terminate_console(int sig)
512 static int already_here = FALSE;
514 if (already_here) /* avoid recursive temination problems */
517 disconnect_from_director((gpointer)NULL);
523 /* Buffer approx 2000 lines -- assume 60 chars/line */
524 #define MAX_TEXT_CHARS (2000 * 60)
525 static int text_chars = 0;
527 static void truncate_text_chars()
529 GtkTextBuffer *textbuf;
530 GtkTextIter iter, iter2;
532 int del_chars = MAX_TEXT_CHARS / 4;
534 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
535 len = gtk_text_buffer_get_char_count(textbuf);
536 gtk_text_buffer_get_iter_at_offset (textbuf, &iter, 0);
537 gtk_text_buffer_get_iter_at_offset (textbuf, &iter2, del_chars);
538 gtk_text_buffer_delete (textbuf, &iter, &iter2);
539 text_chars -= del_chars;
540 len = gtk_text_buffer_get_char_count(textbuf);
541 gtk_text_iter_set_offset(&iter, len);
544 gtk_text_set_point(GTK_TEXT(text1), del_chars);
545 gtk_text_backward_delete(GTK_TEXT(text1), del_chars);
546 text_chars -= del_chars;
547 gtk_text_set_point(GTK_TEXT(text1), text_chars);
551 void set_textf(char *fmt, ...)
556 va_start(arg_ptr, fmt);
557 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
561 void set_text(char *buf, int len)
563 GtkTextBuffer *textbuf;
567 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
568 buf_len = gtk_text_buffer_get_char_count(textbuf);
569 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
570 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
572 gtk_text_insert(GTK_TEXT(text1), text_font, NULL, NULL, buf, -1);
575 if (text_chars > MAX_TEXT_CHARS) {
576 truncate_text_chars();
578 buf_len = gtk_text_buffer_get_char_count(textbuf);
579 gtk_text_iter_set_offset(&iter, buf_len);
581 * Force the scroll bars to the bottom so that most
582 * recent text is on screen.
584 vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll1));
585 gtk_adjustment_set_value(vadj, vadj->upper);
588 void set_statusf(char *fmt, ...)
593 va_start(arg_ptr, fmt);
594 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
595 gtk_label_set_text(GTK_LABEL(status1), buf);
599 void set_status_ready()
601 gtk_label_set_text(GTK_LABEL(status1), " Ready");
605 void set_status(char *buf)
607 gtk_label_set_text(GTK_LABEL(status1), buf);