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 = NULL;
52 PangoFontDescription *text_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-2006 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 text_font_desc = pango_font_description_from_string(con_font->fontface);
306 if (text_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 if (!text_font_desc) {
319 text_font_desc = pango_font_description_from_string("Monospace 10");
321 if (!text_font_desc) {
322 text_font_desc = pango_font_description_from_string("monospace");
325 gtk_widget_modify_font(console, font_desc);
326 gtk_widget_modify_font(entry1, font_desc);
327 gtk_widget_modify_font(status1, font_desc);
328 if (text_font_desc) {
329 gtk_widget_modify_font(text1, text_font_desc);
330 pango_font_description_free(text_font_desc);
332 gtk_widget_modify_font(text1, font_desc);
334 pango_font_description_free(font_desc);
337 terminate_console(0);
341 initial = gtk_timeout_add(100, initial_connect_to_director, (gpointer)NULL);
345 disconnect_from_director((gpointer)NULL);
350 * Every 5 seconds, ask the Director for our
353 static gint message_handler(gpointer data)
355 if (ready && UA_sock) {
356 bnet_fsend(UA_sock, ".messages");
361 int disconnect_from_director(gpointer data)
364 set_status(_(" Not Connected"));
367 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
375 * Called just after the main loop is started to allow
376 * us to connect to the Director.
378 static int initial_connect_to_director(gpointer data)
380 gtk_timeout_remove(initial);
381 if (connect_to_director(data)) {
382 start_director_reader(data);
384 gtk_timeout_add(5000, message_handler, (gpointer)NULL);
388 static GList *get_list(char *cmd)
395 while (bnet_recv(UA_sock) > 0) {
396 strip_trailing_junk(UA_sock->msg);
397 msg = (char *)malloc(strlen(UA_sock->msg) + 1);
398 strcpy(msg, UA_sock->msg);
399 options = g_list_append(options, msg);
405 static GList *get_and_fill_combo(GtkWidget *dialog, const char *combo_name, const char *dircmd)
410 combo = lookup_widget(dialog, combo_name);
411 options = get_list((char *)dircmd);
412 if (combo && options) {
413 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
418 static void fill_combo(GtkWidget *dialog, const char *combo_name, GList *options)
422 combo = lookup_widget(dialog, combo_name);
423 if (combo && options) {
424 gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
431 * Connect to Director. If there are more than one, put up
432 * a modal dialog so that the user chooses one.
434 int connect_to_director(gpointer data)
447 foreach_res(dir, R_DIRECTOR) {
448 dirs = g_list_append(dirs, dir->hdr.name);
451 dir_dialog = create_SelectDirectorDialog();
452 combo = lookup_widget(dir_dialog, "combo1");
453 dir_select = lookup_widget(dir_dialog, "dirselect");
455 gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);
457 gtk_widget_show(dir_dialog);
461 gchar *ecmd = gtk_editable_get_chars((GtkEditable *)dir_select, 0, -1);
462 dir = (DIRRES *)GetResWithName(R_DIRECTOR, ecmd);
464 g_free(ecmd); /* release director name string */
470 gtk_widget_destroy(dir_dialog);
473 /* Just take the first Director */
475 dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
483 memset(&jcr, 0, sizeof(jcr));
485 set_statusf(_(" Connecting to Director %s:%d"), dir->address,dir->DIRport);
486 set_textf(_("Connecting to Director %s:%d\n\n"), dir->address,dir->DIRport);
488 while (gtk_events_pending()) { /* fully paint screen */
489 gtk_main_iteration();
493 /* If cons==NULL, default console will be used */
494 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
498 /* Initialize Console TLS context */
499 if (cons && (cons->tls_enable || cons->tls_require)) {
500 /* Generate passphrase prompt */
501 bsnprintf(buf, sizeof(buf), _("Passphrase for Console \"%s\" TLS private key: "), cons->hdr.name);
503 /* Initialize TLS context:
504 * Args: CA certfile, CA certdir, Certfile, Keyfile,
505 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
506 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
507 cons->tls_ca_certdir, cons->tls_certfile,
508 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
510 if (!cons->tls_ctx) {
511 bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Console \"%s\".\n"),
513 set_text(buf, strlen(buf));
514 terminate_console(0);
520 /* Initialize Director TLS context */
521 if (dir->tls_enable || dir->tls_require) {
522 /* Generate passphrase prompt */
523 bsnprintf(buf, sizeof(buf), _("Passphrase for Director \"%s\" TLS private key: "), dir->hdr.name);
525 /* Initialize TLS context:
526 * Args: CA certfile, CA certdir, Certfile, Keyfile,
527 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
528 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
529 dir->tls_ca_certdir, dir->tls_certfile,
530 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
533 bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Director \"%s\".\n"),
535 set_text(buf, strlen(buf));
536 terminate_console(0);
542 UA_sock = bnet_connect(NULL, 5, 15, _("Director daemon"), dir->address,
543 NULL, dir->DIRport, 0);
544 if (UA_sock == NULL) {
548 jcr.dir_bsock = UA_sock;
549 if (!authenticate_director(&jcr, dir, cons)) {
550 set_text(UA_sock->msg, UA_sock->msglen);
554 set_status(_(" Initializing ..."));
556 bnet_fsend(UA_sock, "autodisplay on");
558 /* Read and display all initial messages */
559 while (bnet_recv(UA_sock) > 0) {
560 set_text(UA_sock->msg, UA_sock->msglen);
564 while (gtk_events_pending()) {
565 gtk_main_iteration();
568 /* Fill the run_dialog combo boxes */
569 job_list = get_and_fill_combo(run_dialog, "combo_job", ".jobs");
570 client_list = get_and_fill_combo(run_dialog, "combo_client", ".clients");
571 fileset_list = get_and_fill_combo(run_dialog, "combo_fileset", ".filesets");
572 messages_list = get_and_fill_combo(run_dialog, "combo_messages", ".msgs");
573 pool_list = get_and_fill_combo(run_dialog, "combo_pool", ".pools");
574 storage_list = get_and_fill_combo(run_dialog, "combo_storage", ".storage");
575 type_list = get_and_fill_combo(run_dialog, "combo_type", ".types");
576 level_list = get_and_fill_combo(run_dialog, "combo_level", ".levels");
578 /* Fill the label dialog combo boxes */
579 fill_combo(label_dialog, "label_combo_storage", storage_list);
580 fill_combo(label_dialog, "label_combo_pool", pool_list);
583 /* Fill the restore_dialog combo boxes */
584 fill_combo(restore_dialog, "combo_restore_job", job_list);
585 fill_combo(restore_dialog, "combo_restore_client", client_list);
586 fill_combo(restore_dialog, "combo_restore_fileset", fileset_list);
587 fill_combo(restore_dialog, "combo_restore_pool", pool_list);
588 fill_combo(restore_dialog, "combo_restore_storage", storage_list);
590 set_status(_(" Connected"));
594 void write_director(const gchar *msg)
598 set_status(_(" Processing command ..."));
599 UA_sock->msglen = strlen(msg);
600 pm_strcpy(&UA_sock->msg, msg);
603 if (strcmp(msg, ".quit") == 0 || strcmp(msg, ".exit") == 0) {
604 disconnect_from_director((gpointer)NULL);
610 void read_director(gpointer data, gint fd, GdkInputCondition condition)
614 if (!UA_sock || UA_sock->fd != fd) {
617 stat = bnet_recv(UA_sock);
623 set_text(UA_sock->msg, UA_sock->msglen);
626 if (is_bnet_stop(UA_sock)) { /* error or term request */
630 /* Must be a signal -- either do something or ignore it */
631 if (UA_sock->msglen == BNET_PROMPT) {
633 set_status(_(" At prompt waiting for input ..."));
635 if (UA_sock->msglen == BNET_EOD) {
643 void start_director_reader(gpointer data)
646 if (director_reader_running || !UA_sock) {
649 tag = gdk_input_add(UA_sock->fd, GDK_INPUT_READ, read_director, NULL);
650 director_reader_running = true;
653 void stop_director_reader(gpointer data)
655 if (!director_reader_running) {
658 gdk_input_remove(tag);
659 gdk_input_remove(tag);
660 gdk_input_remove(tag);
661 gdk_input_remove(tag);
662 gdk_input_remove(tag);
663 gdk_input_remove(tag);
664 gdk_input_remove(tag);
665 gdk_input_remove(tag);
666 gdk_input_remove(tag);
667 gdk_input_remove(tag);
668 gdk_input_remove(tag);
669 director_reader_running = false;
674 /* Cleanup and then exit */
675 void terminate_console(int sig)
677 static bool already_here = false;
679 if (already_here) /* avoid recursive temination problems */
683 disconnect_from_director((gpointer)NULL);
689 /* Buffer approx 2000 lines -- assume 60 chars/line */
690 #define MAX_TEXT_CHARS (2000 * 60)
691 static int text_chars = 0;
693 static void truncate_text_chars()
695 GtkTextBuffer *textbuf;
696 GtkTextIter iter, iter2;
698 int del_chars = MAX_TEXT_CHARS / 4;
700 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
701 len = gtk_text_buffer_get_char_count(textbuf);
702 gtk_text_buffer_get_iter_at_offset (textbuf, &iter, 0);
703 gtk_text_buffer_get_iter_at_offset (textbuf, &iter2, del_chars);
704 gtk_text_buffer_delete (textbuf, &iter, &iter2);
705 text_chars -= del_chars;
706 len = gtk_text_buffer_get_char_count(textbuf);
707 gtk_text_iter_set_offset(&iter, len);
710 void set_textf(const char *fmt, ...)
715 va_start(arg_ptr, fmt);
716 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
721 void set_text(const char *buf, int len)
723 GtkTextBuffer *textbuf;
727 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
728 buf_len = gtk_text_buffer_get_char_count(textbuf);
729 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
730 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
732 if (text_chars > MAX_TEXT_CHARS) {
733 truncate_text_chars();
735 set_scroll_bar_to_end();
738 void set_statusf(const char *fmt, ...)
743 va_start(arg_ptr, fmt);
744 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
746 gtk_label_set_text(GTK_LABEL(status1), buf);
747 // set_scroll_bar_to_end();
751 void set_status_ready()
753 gtk_label_set_text(GTK_LABEL(status1), _(" Ready"));
755 // set_scroll_bar_to_end();
758 void set_status(const char *buf)
760 gtk_label_set_text(GTK_LABEL(status1), buf);
761 // set_scroll_bar_to_end();
765 static void set_scroll_bar_to_end(void)
767 GtkTextBuffer* textbuf = NULL;
771 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
772 buf_len = gtk_text_buffer_get_char_count(textbuf);
773 gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
774 gtk_text_iter_set_offset(&iter, buf_len);
775 gtk_text_buffer_place_cursor(textbuf, &iter);
776 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text1),
777 gtk_text_buffer_get_mark(textbuf, "insert"),