2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula GNOME Console interface to the Director
32 * Kern Sibbald, March MMII
40 #include "interface.h"
43 /* Imported functions */
44 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
45 void select_restore_setup();
46 extern bool parse_gcons_config(CONFIG *config, const char *configfile, int exit_code);
50 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
52 /* Exported variables */
53 GtkWidget *console; /* application window */
54 GtkWidget *text1; /* text window */
55 GtkWidget *entry1; /* entry box */
56 GtkWidget *status1; /* status bar */
57 GtkWidget *combo1; /* director combo */
58 GtkWidget *scroll1; /* main scroll bar */
59 GtkWidget *run_dialog; /* run dialog */
60 GtkWidget *dir_dialog; /* director selection dialog */
61 GtkWidget *restore_dialog; /* restore dialog */
62 GtkWidget *restore_file_selection;
63 GtkWidget *dir_select;
64 GtkWidget *about1; /* about box */
65 GtkWidget *label_dialog;
66 PangoFontDescription *font_desc = NULL;
67 PangoFontDescription *text_font_desc = NULL;
68 pthread_mutex_t cmd_mutex = PTHREAD_MUTEX_INITIALIZER;
69 pthread_cond_t cmd_wait;
72 BSOCK *UA_sock = NULL;
73 GList *job_list, *client_list, *fileset_list;
74 GList *messages_list, *pool_list, *storage_list;
75 GList *type_list, *level_list;
77 /* Forward referenced functions */
78 void terminate_console(int sig);
81 static gint message_handler(gpointer data);
82 static int initial_connect_to_director(gpointer data);
85 static void set_scroll_bar_to_end(void);
87 /* Static variables */
88 static char *configfile = NULL;
91 static bool director_reader_running = false;
92 static bool at_prompt = false;
93 static bool ready = false;
94 static bool quit = false;
96 static int numdir = 0;
97 static CONFIG *config;
99 #define CONFIG_FILE "./bgnome-console.conf" /* default configuration file */
105 "\nVersion: %s (%s) %s %s %s\n\n"
106 "Usage: bgnome-console [-s] [-c config_file] [-d debug_level] [config_file]\n"
107 " -c <file> set configuration file to file\n"
108 " -dnn set debug level to nn\n"
110 " -t test - read configuration and exit\n"
111 " -? print this message.\n"
112 "\n"), 2002, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
118 * Call-back for reading a passphrase for an encrypted PEM file
119 * This function uses getpass(), which uses a static buffer and is NOT thread-safe.
121 static int tls_pem_callback(char *buf, int size, const void *userdata)
124 const char *prompt = (const char *) userdata;
127 passwd = getpass(prompt);
128 bstrncpy(buf, passwd, size);
129 return (strlen(buf));
138 * Make a quick check to see that we have all the
141 static int check_resources()
149 foreach_res(director, R_DIRECTOR) {
151 /* tls_require implies tls_enable */
152 if (director->tls_require) {
154 director->tls_enable = true;
156 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
162 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
163 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
164 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
165 " At least one CA certificate store is required.\n"),
166 director->hdr.name, configfile);
172 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
173 "Without that I don't how to speak to the Director :-(\n"), configfile);
178 /* Loop over Consoles */
179 foreach_res(cons, R_CONSOLE) {
180 /* tls_require implies tls_enable */
181 if (cons->tls_require) {
183 cons->tls_enable = true;
185 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
191 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
192 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
193 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
194 cons->hdr.name, configfile);
205 /*********************************************************************
207 * Main Bacula GNOME Console -- User Interface Program
210 int main(int argc, char *argv[])
213 int no_signals = TRUE;
214 int test_config = FALSE;
216 const char *gargv[2] = {"bgnome-console", NULL};
217 CONFONTRES *con_font;
220 setlocale(LC_ALL, "");
221 bindtextdomain("bacula", LOCALEDIR);
222 textdomain("bacula");
226 my_name_is(argc, argv, "bgnome-console");
227 init_msg(NULL, NULL);
228 working_directory = "/tmp";
230 struct sigaction sigignore;
231 sigignore.sa_flags = 0;
232 sigignore.sa_handler = SIG_IGN;
233 sigfillset(&sigignore.sa_mask);
234 sigaction(SIGPIPE, &sigignore, NULL);
236 if ((stat=pthread_cond_init(&cmd_wait, NULL)) != 0) {
237 Emsg1(M_ABORT, 0, _("Pthread cond init error = %s\n"),
241 gnome_init("bacula", VERSION, gargc, (char **)&gargv);
243 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
245 case 'c': /* configuration file */
246 if (configfile != NULL)
248 configfile = bstrdup(optarg);
252 debug_level = atoi(optarg);
253 if (debug_level <= 0)
257 case 's': /* turn off signals */
275 init_signals(terminate_console);
282 if (configfile == NULL) {
283 configfile = bstrdup(CONFIG_FILE);
286 config = new_config_parser();
287 parse_gcons_config(config, configfile, M_ERROR_TERM);
289 if (init_crypto() != 0) {
290 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
293 if (!check_resources()) {
294 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
297 console = create_console();
298 gtk_window_set_default_size(GTK_WINDOW(console), 800, 700);
299 run_dialog = create_RunDialog();
300 label_dialog = create_label_dialog();
301 restore_dialog = create_RestoreDialog();
302 about1 = create_about1();
304 text1 = lookup_widget(console, "text1");
305 entry1 = lookup_widget(console, "entry1");
306 status1 = lookup_widget(console, "status1");
307 scroll1 = lookup_widget(console, "scroll1");
309 select_restore_setup();
311 gtk_widget_show(console);
314 * Gtk2/pango have different font names. Gnome2 comes with "Monospace 10"
318 foreach_res(con_font, R_CONSOLE_FONT) {
319 if (!con_font->fontface) {
320 Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
323 Dmsg1(100, "Now loading: %s\n",con_font->fontface);
324 text_font_desc = pango_font_description_from_string(con_font->fontface);
325 if (text_font_desc == NULL) {
326 Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
327 con_font->hdr.name, con_font->fontface);
329 Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
330 con_font->hdr.name, con_font->fontface);
336 font_desc = pango_font_description_from_string("LucidaTypewriter 9");
337 if (!text_font_desc) {
338 text_font_desc = pango_font_description_from_string("Monospace 10");
340 if (!text_font_desc) {
341 text_font_desc = pango_font_description_from_string("monospace");
344 gtk_widget_modify_font(console, font_desc);
345 gtk_widget_modify_font(entry1, font_desc);
346 gtk_widget_modify_font(status1, font_desc);
347 if (text_font_desc) {
348 gtk_widget_modify_font(text1, text_font_desc);
349 pango_font_description_free(text_font_desc);
351 gtk_widget_modify_font(text1, font_desc);
353 pango_font_description_free(font_desc);
356 terminate_console(0);
360 initial = gtk_timeout_add(100, initial_connect_to_director, (gpointer)NULL);
364 disconnect_from_director((gpointer)NULL);
369 * Every 5 seconds, ask the Director for our
372 static gint message_handler(gpointer data)
374 if (ready && UA_sock) {
375 bnet_fsend(UA_sock, ".messages");
380 int disconnect_from_director(gpointer data)
383 set_status(_(" Not Connected"));
386 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
394 * Called just after the main loop is started to allow
395 * us to connect to the Director.
397 static int initial_connect_to_director(gpointer data)
399 gtk_timeout_remove(initial);
400 if (connect_to_director(data)) {
401 start_director_reader(data);
403 gtk_timeout_add(5000, message_handler, (gpointer)NULL);
407 static GList *get_list(char *cmd)
414 while (bnet_recv(UA_sock) > 0) {
415 strip_trailing_junk(UA_sock->msg);
416 msg = (char *)malloc(strlen(UA_sock->msg) + 1);
417 strcpy(msg, UA_sock->msg);
418 options = g_list_append(options, msg);
424 static GList *get_and_fill_combo(GtkWidget *dialog, const char *combo_name, const char *dircmd)
429 combo = lookup_widget(dialog, combo_name);
430 options = get_list((char *)dircmd);
431 if (combo && options) {
432 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
437 static void fill_combo(GtkWidget *dialog, const char *combo_name, GList *options)
441 combo = lookup_widget(dialog, combo_name);
442 if (combo && options) {
443 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
450 * Connect to Director. If there are more than one, put up
451 * a modal dialog so that the user chooses one.
453 int connect_to_director(gpointer data)
466 foreach_res(dir, R_DIRECTOR) {
467 dirs = g_list_append(dirs, dir->hdr.name);
470 dir_dialog = create_SelectDirectorDialog();
471 combo = lookup_widget(dir_dialog, "combo1");
472 dir_select = lookup_widget(dir_dialog, "dirselect");
474 gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);
476 gtk_widget_show(dir_dialog);
480 gchar *ecmd = gtk_editable_get_chars((GtkEditable *)dir_select, 0, -1);
481 dir = (DIRRES *)GetResWithName(R_DIRECTOR, ecmd);
483 g_free(ecmd); /* release director name string */
489 gtk_widget_destroy(dir_dialog);
492 /* Just take the first Director */
494 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
502 memset(&jcr, 0, sizeof(jcr));
504 set_statusf(_(" Connecting to Director %s:%d"), dir->address,dir->DIRport);
505 set_textf(_("Connecting to Director %s:%d\n\n"), dir->address,dir->DIRport);
507 while (gtk_events_pending()) { /* fully paint screen */
508 gtk_main_iteration();
512 /* If cons==NULL, default console will be used */
513 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
517 /* Initialize Console TLS context */
518 if (cons && (cons->tls_enable || cons->tls_require)) {
519 /* Generate passphrase prompt */
520 bsnprintf(buf, sizeof(buf), _("Passphrase for Console \"%s\" TLS private key: "), cons->hdr.name);
522 /* Initialize TLS context:
523 * Args: CA certfile, CA certdir, Certfile, Keyfile,
524 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
525 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
526 cons->tls_ca_certdir, cons->tls_certfile,
527 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
529 if (!cons->tls_ctx) {
530 bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Console \"%s\".\n"),
532 set_text(buf, strlen(buf));
533 terminate_console(0);
539 /* Initialize Director TLS context */
540 if (dir->tls_enable || dir->tls_require) {
541 /* Generate passphrase prompt */
542 bsnprintf(buf, sizeof(buf), _("Passphrase for Director \"%s\" TLS private key: "), dir->hdr.name);
544 /* Initialize TLS context:
545 * Args: CA certfile, CA certdir, Certfile, Keyfile,
546 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
547 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
548 dir->tls_ca_certdir, dir->tls_certfile,
549 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
552 bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Director \"%s\".\n"),
554 set_text(buf, strlen(buf));
555 terminate_console(0);
561 UA_sock = bnet_connect(NULL, 5, 15, 0, _("Director daemon"), dir->address,
562 NULL, dir->DIRport, 0);
563 if (UA_sock == NULL) {
567 jcr.dir_bsock = UA_sock;
568 if (!authenticate_director(&jcr, dir, cons)) {
569 set_text(UA_sock->msg, UA_sock->msglen);
573 set_status(_(" Initializing ..."));
575 bnet_fsend(UA_sock, "autodisplay on");
577 /* Read and display all initial messages */
578 while (bnet_recv(UA_sock) > 0) {
579 set_text(UA_sock->msg, UA_sock->msglen);
583 while (gtk_events_pending()) {
584 gtk_main_iteration();
587 /* Fill the run_dialog combo boxes */
588 job_list = get_and_fill_combo(run_dialog, "combo_job", ".jobs");
589 client_list = get_and_fill_combo(run_dialog, "combo_client", ".clients");
590 fileset_list = get_and_fill_combo(run_dialog, "combo_fileset", ".filesets");
591 messages_list = get_and_fill_combo(run_dialog, "combo_messages", ".msgs");
592 pool_list = get_and_fill_combo(run_dialog, "combo_pool", ".pools");
593 storage_list = get_and_fill_combo(run_dialog, "combo_storage", ".storage");
594 type_list = get_and_fill_combo(run_dialog, "combo_type", ".types");
595 level_list = get_and_fill_combo(run_dialog, "combo_level", ".levels");
597 /* Fill the label dialog combo boxes */
598 fill_combo(label_dialog, "label_combo_storage", storage_list);
599 fill_combo(label_dialog, "label_combo_pool", pool_list);
602 /* Fill the restore_dialog combo boxes */
603 fill_combo(restore_dialog, "combo_restore_job", job_list);
604 fill_combo(restore_dialog, "combo_restore_client", client_list);
605 fill_combo(restore_dialog, "combo_restore_fileset", fileset_list);
606 fill_combo(restore_dialog, "combo_restore_pool", pool_list);
607 fill_combo(restore_dialog, "combo_restore_storage", storage_list);
609 set_status(_(" Connected"));
613 void write_director(const gchar *msg)
617 set_status(_(" Processing command ..."));
618 UA_sock->msglen = strlen(msg);
619 pm_strcpy(&UA_sock->msg, msg);
622 if (strcmp(msg, ".quit") == 0 || strcmp(msg, ".exit") == 0) {
623 disconnect_from_director((gpointer)NULL);
629 void read_director(gpointer data, gint fd, GdkInputCondition condition)
633 if (!UA_sock || UA_sock->m_fd != fd) {
636 stat = UA_sock->recv();
642 set_text(UA_sock->msg, UA_sock->msglen);
645 if (is_bnet_stop(UA_sock)) { /* error or term request */
649 /* Must be a signal -- either do something or ignore it */
650 if (UA_sock->msglen == BNET_PROMPT) {
652 set_status(_(" At prompt waiting for input ..."));
654 if (UA_sock->msglen == BNET_EOD) {
662 void start_director_reader(gpointer data)
665 if (director_reader_running || !UA_sock) {
668 tag = gdk_input_add(UA_sock->m_fd, GDK_INPUT_READ, read_director, NULL);
669 director_reader_running = true;
672 void stop_director_reader(gpointer data)
674 if (!director_reader_running) {
677 gdk_input_remove(tag);
678 gdk_input_remove(tag);
679 gdk_input_remove(tag);
680 gdk_input_remove(tag);
681 gdk_input_remove(tag);
682 gdk_input_remove(tag);
683 gdk_input_remove(tag);
684 gdk_input_remove(tag);
685 gdk_input_remove(tag);
686 gdk_input_remove(tag);
687 gdk_input_remove(tag);
688 director_reader_running = false;
693 /* Cleanup and then exit */
694 void terminate_console(int sig)
696 static bool already_here = false;
698 if (already_here) /* avoid recursive temination problems */
701 config->free_resources();
705 disconnect_from_director((gpointer)NULL);
711 /* Buffer approx 2000 lines -- assume 60 chars/line */
712 #define MAX_TEXT_CHARS (2000 * 60)
713 static int text_chars = 0;
715 static void truncate_text_chars()
717 GtkTextBuffer *textbuf;
718 GtkTextIter iter, iter2;
720 int del_chars = MAX_TEXT_CHARS / 4;
722 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
723 len = gtk_text_buffer_get_char_count(textbuf);
724 gtk_text_buffer_get_iter_at_offset (textbuf, &iter, 0);
725 gtk_text_buffer_get_iter_at_offset (textbuf, &iter2, del_chars);
726 gtk_text_buffer_delete (textbuf, &iter, &iter2);
727 text_chars -= del_chars;
728 len = gtk_text_buffer_get_char_count(textbuf);
729 gtk_text_iter_set_offset(&iter, len);
732 void set_textf(const char *fmt, ...)
737 va_start(arg_ptr, fmt);
738 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
743 void set_text(const char *buf, int len)
745 GtkTextBuffer *textbuf;
749 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
750 buf_len = gtk_text_buffer_get_char_count(textbuf);
751 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
752 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
754 if (text_chars > MAX_TEXT_CHARS) {
755 truncate_text_chars();
757 set_scroll_bar_to_end();
760 void set_statusf(const char *fmt, ...)
765 va_start(arg_ptr, fmt);
766 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
768 gtk_label_set_text(GTK_LABEL(status1), buf);
769 // set_scroll_bar_to_end();
773 void set_status_ready()
775 gtk_label_set_text(GTK_LABEL(status1), _(" Ready"));
777 // set_scroll_bar_to_end();
780 void set_status(const char *buf)
782 gtk_label_set_text(GTK_LABEL(status1), buf);
783 // set_scroll_bar_to_end();
787 static void set_scroll_bar_to_end(void)
789 GtkTextBuffer* textbuf = NULL;
793 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
794 buf_len = gtk_text_buffer_get_char_count(textbuf);
795 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
796 gtk_text_iter_set_offset(&iter, buf_len);
797 gtk_text_buffer_place_cursor(textbuf, &iter);
798 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text1),
799 gtk_text_buffer_get_mark(textbuf, "insert"),