3 * Bacula GNOME Console interface to the Director
5 * Kern Sibbald, March MMII
10 Copyright (C) 2002-2005 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as amended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
27 #include "interface.h"
30 /* Imported functions */
31 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
32 void select_restore_setup();
35 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
37 /* Exported variables */
38 GtkWidget *console; /* 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_file_selection;
48 GtkWidget *dir_select;
49 GtkWidget *about1; /* about box */
50 GtkWidget *label_dialog;
51 PangoFontDescription *font_desc;
52 PangoFontDescription *console_font_desc = NULL;
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 void terminate_console(int sig);
66 static gint message_handler(gpointer data);
67 static int initial_connect_to_director(gpointer data);
70 static void set_scroll_bar_to_end(void);
72 /* Static variables */
73 static char *configfile = NULL;
76 static bool director_reader_running = false;
77 static bool at_prompt = false;
78 static bool ready = false;
79 static bool quit = false;
81 static int numdir = 0;
83 #define CONFIG_FILE "./gnome-console.conf" /* default configuration file */
88 "Copyright (C) 2002-2005 Kern Sibbald\n"
89 "\nVersion: %s (%s) %s %s %s\n\n"
90 "Usage: gnome-console [-s] [-c config_file] [-d debug_level] [config_file]\n"
91 " -c <file> set configuration file to file\n"
92 " -dnn set debug level to nn\n"
94 " -t test - read configuration and exit\n"
95 " -? print this message.\n"
96 "\n"), VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
102 * Call-back for reading a passphrase for an encrypted PEM file
103 * This function uses getpass(), which uses a static buffer and is NOT thread-safe.
105 static int tls_pem_callback(char *buf, int size, const void *userdata)
108 const char *prompt = (const char *) userdata;
111 passwd = getpass(prompt);
112 bstrncpy(buf, passwd, size);
113 return (strlen(buf));
122 * Make a quick check to see that we have all the
125 static int check_resources()
133 foreach_res(director, R_DIRECTOR) {
135 /* tls_require implies tls_enable */
136 if (director->tls_require) {
138 director->tls_enable = true;
140 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
146 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
147 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
148 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
149 " At least one CA certificate store is required.\n"),
150 director->hdr.name, configfile);
156 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
157 "Without that I don't how to speak to the Director :-(\n"), configfile);
162 /* Loop over Consoles */
163 foreach_res(cons, R_CONSOLE) {
164 /* tls_require implies tls_enable */
165 if (cons->tls_require) {
167 cons->tls_enable = true;
169 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
175 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
176 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
177 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
178 cons->hdr.name, configfile);
189 /*********************************************************************
191 * Main Bacula GNOME Console -- User Interface Program
194 int main(int argc, char *argv[])
197 int no_signals = TRUE;
198 int test_config = FALSE;
200 const char *gargv[2] = {"gnome-console", NULL};
201 CONFONTRES *con_font;
203 setlocale(LC_ALL, "");
204 bindtextdomain("bacula", LOCALEDIR);
205 textdomain("bacula");
208 my_name_is(argc, argv, "gnome-console");
209 init_msg(NULL, NULL);
210 working_directory = "/tmp";
212 struct sigaction sigignore;
213 sigignore.sa_flags = 0;
214 sigignore.sa_handler = SIG_IGN;
215 sigfillset(&sigignore.sa_mask);
216 sigaction(SIGPIPE, &sigignore, NULL);
218 if ((stat=pthread_cond_init(&cmd_wait, NULL)) != 0) {
219 Emsg1(M_ABORT, 0, _("Pthread cond init error = %s\n"),
223 gnome_init("bacula", VERSION, gargc, (char **)&gargv);
225 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
227 case 'c': /* configuration file */
228 if (configfile != NULL)
230 configfile = bstrdup(optarg);
234 debug_level = atoi(optarg);
235 if (debug_level <= 0)
239 case 's': /* turn off signals */
257 init_signals(terminate_console);
264 if (configfile == NULL) {
265 configfile = bstrdup(CONFIG_FILE);
268 parse_config(configfile);
270 if (init_crypto() != 0) {
271 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
274 if (!check_resources()) {
275 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
278 console = create_console();
279 gtk_window_set_default_size(GTK_WINDOW(console), 800, 700);
280 run_dialog = create_RunDialog();
281 label_dialog = create_label_dialog();
282 restore_dialog = create_RestoreDialog();
283 about1 = create_about1();
285 text1 = lookup_widget(console, "text1");
286 entry1 = lookup_widget(console, "entry1");
287 status1 = lookup_widget(console, "status1");
288 scroll1 = lookup_widget(console, "scroll1");
290 select_restore_setup();
292 gtk_widget_show(console);
295 * Gtk2/pango have different font names. Gnome2 comes with "Monospace 10"
299 foreach_res(con_font, R_CONSOLE_FONT) {
300 if (!con_font->fontface) {
301 Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
304 Dmsg1(100, "Now loading: %s\n",con_font->fontface);
305 console_font_desc = pango_font_description_from_string(con_font->fontface);
306 if (console_font_desc == NULL) {
307 Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
308 con_font->hdr.name, con_font->fontface);
310 Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
311 con_font->hdr.name, con_font->fontface);
317 font_desc = pango_font_description_from_string("LucidaTypewriter 9");
318 gtk_widget_modify_font(console, font_desc);
319 gtk_widget_modify_font(entry1, font_desc);
320 gtk_widget_modify_font(status1, font_desc);
321 if (console_font_desc) {
322 gtk_widget_modify_font(text1, console_font_desc);
323 pango_font_description_free(console_font_desc);
325 gtk_widget_modify_font(text1, font_desc);
327 pango_font_description_free(font_desc);
330 terminate_console(0);
334 initial = gtk_timeout_add(100, initial_connect_to_director, (gpointer)NULL);
338 disconnect_from_director((gpointer)NULL);
343 * Every 5 seconds, ask the Director for our
346 static gint message_handler(gpointer data)
348 if (ready && UA_sock) {
349 bnet_fsend(UA_sock, ".messages");
354 int disconnect_from_director(gpointer data)
357 set_status(_(" Not Connected"));
360 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
368 * Called just after the main loop is started to allow
369 * us to connect to the Director.
371 static int initial_connect_to_director(gpointer data)
373 gtk_timeout_remove(initial);
374 if (connect_to_director(data)) {
375 start_director_reader(data);
377 gtk_timeout_add(5000, message_handler, (gpointer)NULL);
381 static GList *get_list(char *cmd)
388 while (bnet_recv(UA_sock) > 0) {
389 strip_trailing_junk(UA_sock->msg);
390 msg = (char *)malloc(strlen(UA_sock->msg) + 1);
391 strcpy(msg, UA_sock->msg);
392 options = g_list_append(options, msg);
398 static GList *get_and_fill_combo(GtkWidget *dialog, const char *combo_name, const char *dircmd)
403 combo = lookup_widget(dialog, combo_name);
404 options = get_list((char *)dircmd);
405 if (combo && options) {
406 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
411 static void fill_combo(GtkWidget *dialog, const char *combo_name, GList *options)
415 combo = lookup_widget(dialog, combo_name);
416 if (combo && options) {
417 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
424 * Connect to Director. If there are more than one, put up
425 * a modal dialog so that the user chooses one.
427 int connect_to_director(gpointer data)
440 foreach_res(dir, R_DIRECTOR) {
441 dirs = g_list_append(dirs, dir->hdr.name);
444 dir_dialog = create_SelectDirectorDialog();
445 combo = lookup_widget(dir_dialog, "combo1");
446 dir_select = lookup_widget(dir_dialog, "dirselect");
448 gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);
450 gtk_widget_show(dir_dialog);
454 gchar *ecmd = gtk_editable_get_chars((GtkEditable *)dir_select, 0, -1);
455 dir = (DIRRES *)GetResWithName(R_DIRECTOR, ecmd);
457 g_free(ecmd); /* release director name string */
463 gtk_widget_destroy(dir_dialog);
466 /* Just take the first Director */
468 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
476 memset(&jcr, 0, sizeof(jcr));
478 set_statusf(_(" Connecting to Director %s:%d"), dir->address,dir->DIRport);
479 set_textf(_("Connecting to Director %s:%d\n\n"), dir->address,dir->DIRport);
481 while (gtk_events_pending()) { /* fully paint screen */
482 gtk_main_iteration();
486 /* If cons==NULL, default console will be used */
487 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
491 /* Initialize Console TLS context */
492 if (cons && (cons->tls_enable || cons->tls_require)) {
493 /* Generate passphrase prompt */
494 bsnprintf(buf, sizeof(buf), _("Passphrase for Console \"%s\" TLS private key: "), cons->hdr.name);
496 /* Initialize TLS context:
497 * Args: CA certfile, CA certdir, Certfile, Keyfile,
498 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
499 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
500 cons->tls_ca_certdir, cons->tls_certfile,
501 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
503 if (!cons->tls_ctx) {
504 bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Console \"%s\".\n"),
506 set_text(buf, strlen(buf));
507 terminate_console(0);
513 /* Initialize Director TLS context */
514 if (dir->tls_enable || dir->tls_require) {
515 /* Generate passphrase prompt */
516 bsnprintf(buf, sizeof(buf), _("Passphrase for Director \"%s\" TLS private key: "), dir->hdr.name);
518 /* Initialize TLS context:
519 * Args: CA certfile, CA certdir, Certfile, Keyfile,
520 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
521 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
522 dir->tls_ca_certdir, dir->tls_certfile,
523 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
526 bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Director \"%s\".\n"),
528 set_text(buf, strlen(buf));
529 terminate_console(0);
535 UA_sock = bnet_connect(NULL, 5, 15, _("Director daemon"), dir->address,
536 NULL, dir->DIRport, 0);
537 if (UA_sock == NULL) {
541 jcr.dir_bsock = UA_sock;
542 if (!authenticate_director(&jcr, dir, cons)) {
543 set_text(UA_sock->msg, UA_sock->msglen);
547 set_status(_(" Initializing ..."));
549 bnet_fsend(UA_sock, "autodisplay on");
551 /* Read and display all initial messages */
552 while (bnet_recv(UA_sock) > 0) {
553 set_text(UA_sock->msg, UA_sock->msglen);
557 while (gtk_events_pending()) {
558 gtk_main_iteration();
561 /* Fill the run_dialog combo boxes */
562 job_list = get_and_fill_combo(run_dialog, "combo_job", ".jobs");
563 client_list = get_and_fill_combo(run_dialog, "combo_client", ".clients");
564 fileset_list = get_and_fill_combo(run_dialog, "combo_fileset", ".filesets");
565 messages_list = get_and_fill_combo(run_dialog, "combo_messages", ".msgs");
566 pool_list = get_and_fill_combo(run_dialog, "combo_pool", ".pools");
567 storage_list = get_and_fill_combo(run_dialog, "combo_storage", ".storage");
568 type_list = get_and_fill_combo(run_dialog, "combo_type", ".types");
569 level_list = get_and_fill_combo(run_dialog, "combo_level", ".levels");
571 /* Fill the label dialog combo boxes */
572 fill_combo(label_dialog, "label_combo_storage", storage_list);
573 fill_combo(label_dialog, "label_combo_pool", pool_list);
576 /* Fill the restore_dialog combo boxes */
577 fill_combo(restore_dialog, "combo_restore_job", job_list);
578 fill_combo(restore_dialog, "combo_restore_client", client_list);
579 fill_combo(restore_dialog, "combo_restore_fileset", fileset_list);
580 fill_combo(restore_dialog, "combo_restore_pool", pool_list);
581 fill_combo(restore_dialog, "combo_restore_storage", storage_list);
583 set_status(_(" Connected"));
587 void write_director(const gchar *msg)
591 set_status(_(" Processing command ..."));
592 UA_sock->msglen = strlen(msg);
593 pm_strcpy(&UA_sock->msg, msg);
596 if (strcmp(msg, ".quit") == 0 || strcmp(msg, ".exit") == 0) {
597 disconnect_from_director((gpointer)NULL);
603 void read_director(gpointer data, gint fd, GdkInputCondition condition)
607 if (!UA_sock || UA_sock->fd != fd) {
610 stat = bnet_recv(UA_sock);
616 set_text(UA_sock->msg, UA_sock->msglen);
619 if (is_bnet_stop(UA_sock)) { /* error or term request */
623 /* Must be a signal -- either do something or ignore it */
624 if (UA_sock->msglen == BNET_PROMPT) {
626 set_status(_(" At prompt waiting for input ..."));
628 if (UA_sock->msglen == BNET_EOD) {
636 void start_director_reader(gpointer data)
639 if (director_reader_running || !UA_sock) {
642 tag = gdk_input_add(UA_sock->fd, GDK_INPUT_READ, read_director, NULL);
643 director_reader_running = true;
646 void stop_director_reader(gpointer data)
648 if (!director_reader_running) {
651 gdk_input_remove(tag);
652 gdk_input_remove(tag);
653 gdk_input_remove(tag);
654 gdk_input_remove(tag);
655 gdk_input_remove(tag);
656 gdk_input_remove(tag);
657 gdk_input_remove(tag);
658 gdk_input_remove(tag);
659 gdk_input_remove(tag);
660 gdk_input_remove(tag);
661 gdk_input_remove(tag);
662 director_reader_running = false;
667 /* Cleanup and then exit */
668 void terminate_console(int sig)
670 static bool already_here = false;
672 if (already_here) /* avoid recursive temination problems */
676 disconnect_from_director((gpointer)NULL);
682 /* Buffer approx 2000 lines -- assume 60 chars/line */
683 #define MAX_TEXT_CHARS (2000 * 60)
684 static int text_chars = 0;
686 static void truncate_text_chars()
688 GtkTextBuffer *textbuf;
689 GtkTextIter iter, iter2;
691 int del_chars = MAX_TEXT_CHARS / 4;
693 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
694 len = gtk_text_buffer_get_char_count(textbuf);
695 gtk_text_buffer_get_iter_at_offset (textbuf, &iter, 0);
696 gtk_text_buffer_get_iter_at_offset (textbuf, &iter2, del_chars);
697 gtk_text_buffer_delete (textbuf, &iter, &iter2);
698 text_chars -= del_chars;
699 len = gtk_text_buffer_get_char_count(textbuf);
700 gtk_text_iter_set_offset(&iter, len);
703 void set_textf(const char *fmt, ...)
708 va_start(arg_ptr, fmt);
709 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
714 void set_text(const char *buf, int len)
716 GtkTextBuffer *textbuf;
720 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
721 buf_len = gtk_text_buffer_get_char_count(textbuf);
722 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
723 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
725 if (text_chars > MAX_TEXT_CHARS) {
726 truncate_text_chars();
728 set_scroll_bar_to_end();
731 void set_statusf(const char *fmt, ...)
736 va_start(arg_ptr, fmt);
737 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
739 gtk_label_set_text(GTK_LABEL(status1), buf);
740 // set_scroll_bar_to_end();
744 void set_status_ready()
746 gtk_label_set_text(GTK_LABEL(status1), _(" Ready"));
748 // set_scroll_bar_to_end();
751 void set_status(const char *buf)
753 gtk_label_set_text(GTK_LABEL(status1), buf);
754 // set_scroll_bar_to_end();
758 static void set_scroll_bar_to_end(void)
760 GtkTextBuffer* textbuf = NULL;
764 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
765 buf_len = gtk_text_buffer_get_char_count(textbuf);
766 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
767 gtk_text_iter_set_offset(&iter, buf_len);
768 gtk_text_buffer_place_cursor(textbuf, &iter);
769 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text1),
770 gtk_text_buffer_get_mark(textbuf, "insert"),