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 John Walker.
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();
48 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
50 /* Exported variables */
51 GtkWidget *console; /* application window */
52 GtkWidget *text1; /* text window */
53 GtkWidget *entry1; /* entry box */
54 GtkWidget *status1; /* status bar */
55 GtkWidget *combo1; /* director combo */
56 GtkWidget *scroll1; /* main scroll bar */
57 GtkWidget *run_dialog; /* run dialog */
58 GtkWidget *dir_dialog; /* director selection dialog */
59 GtkWidget *restore_dialog; /* restore dialog */
60 GtkWidget *restore_file_selection;
61 GtkWidget *dir_select;
62 GtkWidget *about1; /* about box */
63 GtkWidget *label_dialog;
64 PangoFontDescription *font_desc = NULL;
65 PangoFontDescription *text_font_desc = NULL;
66 pthread_mutex_t cmd_mutex = PTHREAD_MUTEX_INITIALIZER;
67 pthread_cond_t cmd_wait;
70 BSOCK *UA_sock = NULL;
71 GList *job_list, *client_list, *fileset_list;
72 GList *messages_list, *pool_list, *storage_list;
73 GList *type_list, *level_list;
75 /* Forward referenced functions */
76 void terminate_console(int sig);
79 static gint message_handler(gpointer data);
80 static int initial_connect_to_director(gpointer data);
83 static void set_scroll_bar_to_end(void);
85 /* Static variables */
86 static char *configfile = NULL;
89 static bool director_reader_running = false;
90 static bool at_prompt = false;
91 static bool ready = false;
92 static bool quit = false;
94 static int numdir = 0;
96 #define CONFIG_FILE "./bgnome-console.conf" /* default configuration file */
102 "\nVersion: %s (%s) %s %s %s\n\n"
103 "Usage: bgnome-console [-s] [-c config_file] [-d debug_level] [config_file]\n"
104 " -c <file> set configuration file to file\n"
105 " -dnn set debug level to nn\n"
107 " -t test - read configuration and exit\n"
108 " -? print this message.\n"
109 "\n"), 2002, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER);
115 * Call-back for reading a passphrase for an encrypted PEM file
116 * This function uses getpass(), which uses a static buffer and is NOT thread-safe.
118 static int tls_pem_callback(char *buf, int size, const void *userdata)
121 const char *prompt = (const char *) userdata;
124 passwd = getpass(prompt);
125 bstrncpy(buf, passwd, size);
126 return (strlen(buf));
135 * Make a quick check to see that we have all the
138 static int check_resources()
146 foreach_res(director, R_DIRECTOR) {
148 /* tls_require implies tls_enable */
149 if (director->tls_require) {
151 director->tls_enable = true;
153 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
159 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
160 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
161 " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in %s."
162 " At least one CA certificate store is required.\n"),
163 director->hdr.name, configfile);
169 Emsg1(M_FATAL, 0, _("No Director resource defined in %s\n"
170 "Without that I don't how to speak to the Director :-(\n"), configfile);
175 /* Loop over Consoles */
176 foreach_res(cons, R_CONSOLE) {
177 /* tls_require implies tls_enable */
178 if (cons->tls_require) {
180 cons->tls_enable = true;
182 Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
188 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
189 Emsg2(M_FATAL, 0, _("Neither \"TLS CA Certificate\""
190 " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in %s.\n"),
191 cons->hdr.name, configfile);
202 /*********************************************************************
204 * Main Bacula GNOME Console -- User Interface Program
207 int main(int argc, char *argv[])
210 int no_signals = TRUE;
211 int test_config = FALSE;
213 const char *gargv[2] = {"bgnome-console", NULL};
214 CONFONTRES *con_font;
217 setlocale(LC_ALL, "");
218 bindtextdomain("bacula", LOCALEDIR);
219 textdomain("bacula");
223 my_name_is(argc, argv, "bgnome-console");
224 init_msg(NULL, NULL);
225 working_directory = "/tmp";
227 struct sigaction sigignore;
228 sigignore.sa_flags = 0;
229 sigignore.sa_handler = SIG_IGN;
230 sigfillset(&sigignore.sa_mask);
231 sigaction(SIGPIPE, &sigignore, NULL);
233 if ((stat=pthread_cond_init(&cmd_wait, NULL)) != 0) {
234 Emsg1(M_ABORT, 0, _("Pthread cond init error = %s\n"),
238 gnome_init("bacula", VERSION, gargc, (char **)&gargv);
240 while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
242 case 'c': /* configuration file */
243 if (configfile != NULL)
245 configfile = bstrdup(optarg);
249 debug_level = atoi(optarg);
250 if (debug_level <= 0)
254 case 's': /* turn off signals */
272 init_signals(terminate_console);
279 if (configfile == NULL) {
280 configfile = bstrdup(CONFIG_FILE);
283 parse_config(configfile);
285 if (init_crypto() != 0) {
286 Emsg0(M_ERROR_TERM, 0, _("Cryptography library initialization failed.\n"));
289 if (!check_resources()) {
290 Emsg1(M_ERROR_TERM, 0, _("Please correct configuration file: %s\n"), configfile);
293 console = create_console();
294 gtk_window_set_default_size(GTK_WINDOW(console), 800, 700);
295 run_dialog = create_RunDialog();
296 label_dialog = create_label_dialog();
297 restore_dialog = create_RestoreDialog();
298 about1 = create_about1();
300 text1 = lookup_widget(console, "text1");
301 entry1 = lookup_widget(console, "entry1");
302 status1 = lookup_widget(console, "status1");
303 scroll1 = lookup_widget(console, "scroll1");
305 select_restore_setup();
307 gtk_widget_show(console);
310 * Gtk2/pango have different font names. Gnome2 comes with "Monospace 10"
314 foreach_res(con_font, R_CONSOLE_FONT) {
315 if (!con_font->fontface) {
316 Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
319 Dmsg1(100, "Now loading: %s\n",con_font->fontface);
320 text_font_desc = pango_font_description_from_string(con_font->fontface);
321 if (text_font_desc == NULL) {
322 Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
323 con_font->hdr.name, con_font->fontface);
325 Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
326 con_font->hdr.name, con_font->fontface);
332 font_desc = pango_font_description_from_string("LucidaTypewriter 9");
333 if (!text_font_desc) {
334 text_font_desc = pango_font_description_from_string("Monospace 10");
336 if (!text_font_desc) {
337 text_font_desc = pango_font_description_from_string("monospace");
340 gtk_widget_modify_font(console, font_desc);
341 gtk_widget_modify_font(entry1, font_desc);
342 gtk_widget_modify_font(status1, font_desc);
343 if (text_font_desc) {
344 gtk_widget_modify_font(text1, text_font_desc);
345 pango_font_description_free(text_font_desc);
347 gtk_widget_modify_font(text1, font_desc);
349 pango_font_description_free(font_desc);
352 terminate_console(0);
356 initial = gtk_timeout_add(100, initial_connect_to_director, (gpointer)NULL);
360 disconnect_from_director((gpointer)NULL);
365 * Every 5 seconds, ask the Director for our
368 static gint message_handler(gpointer data)
370 if (ready && UA_sock) {
371 bnet_fsend(UA_sock, ".messages");
376 int disconnect_from_director(gpointer data)
379 set_status(_(" Not Connected"));
382 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
390 * Called just after the main loop is started to allow
391 * us to connect to the Director.
393 static int initial_connect_to_director(gpointer data)
395 gtk_timeout_remove(initial);
396 if (connect_to_director(data)) {
397 start_director_reader(data);
399 gtk_timeout_add(5000, message_handler, (gpointer)NULL);
403 static GList *get_list(char *cmd)
410 while (bnet_recv(UA_sock) > 0) {
411 strip_trailing_junk(UA_sock->msg);
412 msg = (char *)malloc(strlen(UA_sock->msg) + 1);
413 strcpy(msg, UA_sock->msg);
414 options = g_list_append(options, msg);
420 static GList *get_and_fill_combo(GtkWidget *dialog, const char *combo_name, const char *dircmd)
425 combo = lookup_widget(dialog, combo_name);
426 options = get_list((char *)dircmd);
427 if (combo && options) {
428 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
433 static void fill_combo(GtkWidget *dialog, const char *combo_name, GList *options)
437 combo = lookup_widget(dialog, combo_name);
438 if (combo && options) {
439 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
446 * Connect to Director. If there are more than one, put up
447 * a modal dialog so that the user chooses one.
449 int connect_to_director(gpointer data)
462 foreach_res(dir, R_DIRECTOR) {
463 dirs = g_list_append(dirs, dir->hdr.name);
466 dir_dialog = create_SelectDirectorDialog();
467 combo = lookup_widget(dir_dialog, "combo1");
468 dir_select = lookup_widget(dir_dialog, "dirselect");
470 gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);
472 gtk_widget_show(dir_dialog);
476 gchar *ecmd = gtk_editable_get_chars((GtkEditable *)dir_select, 0, -1);
477 dir = (DIRRES *)GetResWithName(R_DIRECTOR, ecmd);
479 g_free(ecmd); /* release director name string */
485 gtk_widget_destroy(dir_dialog);
488 /* Just take the first Director */
490 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
498 memset(&jcr, 0, sizeof(jcr));
500 set_statusf(_(" Connecting to Director %s:%d"), dir->address,dir->DIRport);
501 set_textf(_("Connecting to Director %s:%d\n\n"), dir->address,dir->DIRport);
503 while (gtk_events_pending()) { /* fully paint screen */
504 gtk_main_iteration();
508 /* If cons==NULL, default console will be used */
509 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
513 /* Initialize Console TLS context */
514 if (cons && (cons->tls_enable || cons->tls_require)) {
515 /* Generate passphrase prompt */
516 bsnprintf(buf, sizeof(buf), _("Passphrase for Console \"%s\" TLS private key: "), cons->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 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
522 cons->tls_ca_certdir, cons->tls_certfile,
523 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
525 if (!cons->tls_ctx) {
526 bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Console \"%s\".\n"),
528 set_text(buf, strlen(buf));
529 terminate_console(0);
535 /* Initialize Director TLS context */
536 if (dir->tls_enable || dir->tls_require) {
537 /* Generate passphrase prompt */
538 bsnprintf(buf, sizeof(buf), _("Passphrase for Director \"%s\" TLS private key: "), dir->hdr.name);
540 /* Initialize TLS context:
541 * Args: CA certfile, CA certdir, Certfile, Keyfile,
542 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
543 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
544 dir->tls_ca_certdir, dir->tls_certfile,
545 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
548 bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Director \"%s\".\n"),
550 set_text(buf, strlen(buf));
551 terminate_console(0);
557 UA_sock = bnet_connect(NULL, 5, 15, 0, _("Director daemon"), dir->address,
558 NULL, dir->DIRport, 0);
559 if (UA_sock == NULL) {
563 jcr.dir_bsock = UA_sock;
564 if (!authenticate_director(&jcr, dir, cons)) {
565 set_text(UA_sock->msg, UA_sock->msglen);
569 set_status(_(" Initializing ..."));
571 bnet_fsend(UA_sock, "autodisplay on");
573 /* Read and display all initial messages */
574 while (bnet_recv(UA_sock) > 0) {
575 set_text(UA_sock->msg, UA_sock->msglen);
579 while (gtk_events_pending()) {
580 gtk_main_iteration();
583 /* Fill the run_dialog combo boxes */
584 job_list = get_and_fill_combo(run_dialog, "combo_job", ".jobs");
585 client_list = get_and_fill_combo(run_dialog, "combo_client", ".clients");
586 fileset_list = get_and_fill_combo(run_dialog, "combo_fileset", ".filesets");
587 messages_list = get_and_fill_combo(run_dialog, "combo_messages", ".msgs");
588 pool_list = get_and_fill_combo(run_dialog, "combo_pool", ".pools");
589 storage_list = get_and_fill_combo(run_dialog, "combo_storage", ".storage");
590 type_list = get_and_fill_combo(run_dialog, "combo_type", ".types");
591 level_list = get_and_fill_combo(run_dialog, "combo_level", ".levels");
593 /* Fill the label dialog combo boxes */
594 fill_combo(label_dialog, "label_combo_storage", storage_list);
595 fill_combo(label_dialog, "label_combo_pool", pool_list);
598 /* Fill the restore_dialog combo boxes */
599 fill_combo(restore_dialog, "combo_restore_job", job_list);
600 fill_combo(restore_dialog, "combo_restore_client", client_list);
601 fill_combo(restore_dialog, "combo_restore_fileset", fileset_list);
602 fill_combo(restore_dialog, "combo_restore_pool", pool_list);
603 fill_combo(restore_dialog, "combo_restore_storage", storage_list);
605 set_status(_(" Connected"));
609 void write_director(const gchar *msg)
613 set_status(_(" Processing command ..."));
614 UA_sock->msglen = strlen(msg);
615 pm_strcpy(&UA_sock->msg, msg);
618 if (strcmp(msg, ".quit") == 0 || strcmp(msg, ".exit") == 0) {
619 disconnect_from_director((gpointer)NULL);
625 void read_director(gpointer data, gint fd, GdkInputCondition condition)
629 if (!UA_sock || UA_sock->m_fd != fd) {
632 stat = UA_sock->recv();
638 set_text(UA_sock->msg, UA_sock->msglen);
641 if (is_bnet_stop(UA_sock)) { /* error or term request */
645 /* Must be a signal -- either do something or ignore it */
646 if (UA_sock->msglen == BNET_PROMPT) {
648 set_status(_(" At prompt waiting for input ..."));
650 if (UA_sock->msglen == BNET_EOD) {
658 void start_director_reader(gpointer data)
661 if (director_reader_running || !UA_sock) {
664 tag = gdk_input_add(UA_sock->m_fd, GDK_INPUT_READ, read_director, NULL);
665 director_reader_running = true;
668 void stop_director_reader(gpointer data)
670 if (!director_reader_running) {
673 gdk_input_remove(tag);
674 gdk_input_remove(tag);
675 gdk_input_remove(tag);
676 gdk_input_remove(tag);
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 director_reader_running = false;
689 /* Cleanup and then exit */
690 void terminate_console(int sig)
692 static bool already_here = false;
694 if (already_here) /* avoid recursive temination problems */
698 disconnect_from_director((gpointer)NULL);
704 /* Buffer approx 2000 lines -- assume 60 chars/line */
705 #define MAX_TEXT_CHARS (2000 * 60)
706 static int text_chars = 0;
708 static void truncate_text_chars()
710 GtkTextBuffer *textbuf;
711 GtkTextIter iter, iter2;
713 int del_chars = MAX_TEXT_CHARS / 4;
715 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
716 len = gtk_text_buffer_get_char_count(textbuf);
717 gtk_text_buffer_get_iter_at_offset (textbuf, &iter, 0);
718 gtk_text_buffer_get_iter_at_offset (textbuf, &iter2, del_chars);
719 gtk_text_buffer_delete (textbuf, &iter, &iter2);
720 text_chars -= del_chars;
721 len = gtk_text_buffer_get_char_count(textbuf);
722 gtk_text_iter_set_offset(&iter, len);
725 void set_textf(const char *fmt, ...)
730 va_start(arg_ptr, fmt);
731 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
736 void set_text(const char *buf, int len)
738 GtkTextBuffer *textbuf;
742 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
743 buf_len = gtk_text_buffer_get_char_count(textbuf);
744 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
745 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
747 if (text_chars > MAX_TEXT_CHARS) {
748 truncate_text_chars();
750 set_scroll_bar_to_end();
753 void set_statusf(const char *fmt, ...)
758 va_start(arg_ptr, fmt);
759 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
761 gtk_label_set_text(GTK_LABEL(status1), buf);
762 // set_scroll_bar_to_end();
766 void set_status_ready()
768 gtk_label_set_text(GTK_LABEL(status1), _(" Ready"));
770 // set_scroll_bar_to_end();
773 void set_status(const char *buf)
775 gtk_label_set_text(GTK_LABEL(status1), buf);
776 // set_scroll_bar_to_end();
780 static void set_scroll_bar_to_end(void)
782 GtkTextBuffer* textbuf = NULL;
786 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
787 buf_len = gtk_text_buffer_get_char_count(textbuf);
788 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
789 gtk_text_iter_set_offset(&iter, buf_len);
790 gtk_text_buffer_place_cursor(textbuf, &iter);
791 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text1),
792 gtk_text_buffer_get_mark(textbuf, "insert"),