]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/gnome2-console/console.c
- Modify the depend section of each Makefile.in to reference
[bacula/bacula] / bacula / src / gnome2-console / console.c
1 /*
2  *
3  *   Bacula GNOME Console interface to the Director
4  *
5  *     Kern Sibbald, March MMII
6  *     
7  *     Version $Id$
8  */
9
10 /*
11    Copyright (C) 2002-2004 Kern Sibbald and John Walker
12
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.
17
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.
22
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.
26  */
27
28 #include "bacula.h"
29 #include "console.h"
30
31 #include "interface.h"
32 #include "support.h"
33
34 /* Imported functions */
35 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
36 void select_restore_setup();
37        
38 /* Exported variables */
39 GtkWidget *console;   /* application window */
40 GtkWidget *text1;            /* text window */
41 GtkWidget *entry1;           /* entry box */
42 GtkWidget *status1;          /* status bar */
43 GtkWidget *combo1;           /* director combo */
44 GtkWidget *scroll1;          /* main scroll bar */
45 GtkWidget *run_dialog;       /* run dialog */
46 GtkWidget *dir_dialog;       /* director selection dialog */
47 GtkWidget *restore_dialog;   /* restore dialog */
48 GtkWidget *restore_file_selection;
49 GtkWidget *dir_select;
50 GtkWidget *about1;           /* about box */
51 GtkWidget *label_dialog;
52 GdkFont   *text_font = NULL;
53 PangoFontDescription *font_desc;
54 pthread_mutex_t cmd_mutex = PTHREAD_MUTEX_INITIALIZER;
55 pthread_cond_t  cmd_wait;
56 char cmd[1000];
57 int reply;
58 BSOCK *UA_sock = NULL;
59 GList *job_list, *client_list, *fileset_list;
60 GList *messages_list, *pool_list, *storage_list;
61 GList *type_list, *level_list;
62
63 /* Forward referenced functions */
64 void terminate_console(int sig);
65
66 extern "C" {
67     static gint message_handler(gpointer data);
68     static int initial_connect_to_director(gpointer data);
69 }
70
71 static void set_scroll_bar_to_end(void);
72
73 /* Static variables */
74 static char *configfile = NULL;
75 static DIRRES *dir; 
76 static int ndir;
77 static bool director_reader_running = false;
78 static bool at_prompt = false;
79 static bool ready = false;
80 static bool quit = false;
81 static guint initial;
82
83 #define CONFIG_FILE "./gnome-console.conf"   /* default configuration file */
84
85 static void usage()
86 {
87    fprintf(stderr, _(
88 "Copyright (C) 2002-2004 Kern Sibbald and John Walker\n"
89 "\nVersion: " VERSION " (" BDATE ") %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"
93 "       -s          no signals\n"
94 "       -t          test - read configuration and exit\n"
95 "       -?          print this message.\n"
96 "\n"), HOST_OS, DISTNAME, DISTVER);
97
98    exit(1);
99 }
100
101
102 /*********************************************************************
103  *
104  *         Main Bacula GNOME Console -- User Interface Program
105  *
106  */
107 int main(int argc, char *argv[])
108 {
109    int ch, stat;
110    int no_signals = TRUE;
111    int test_config = FALSE;
112    int gargc = 1;
113    const char *gargv[2] = {"gnome-console", NULL};
114    CONFONTRES *con_font;
115
116    init_stack_dump();
117    my_name_is(argc, argv, "gnome-console");
118    init_msg(NULL, NULL);
119    working_directory  = "/tmp";
120
121    struct sigaction sigignore;
122    sigignore.sa_flags = 0;
123    sigignore.sa_handler = SIG_IGN;       
124    sigfillset(&sigignore.sa_mask);
125    sigaction(SIGPIPE, &sigignore, NULL);
126
127    if ((stat=pthread_cond_init(&cmd_wait, NULL)) != 0) {
128       Emsg1(M_ABORT, 0, _("Pthread cond init error = %s\n"),
129          strerror(stat));
130    }
131
132    gnome_init("bacula", VERSION, gargc, (char **)&gargv);
133
134    while ((ch = getopt(argc, argv, "bc:d:r:st?")) != -1) {
135       switch (ch) {
136       case 'c':                    /* configuration file */
137          if (configfile != NULL)
138             free(configfile);
139          configfile = bstrdup(optarg);
140          break;
141
142       case 'd':
143          debug_level = atoi(optarg);
144          if (debug_level <= 0)
145             debug_level = 1;
146          break;
147
148       case 's':                    /* turn off signals */
149          no_signals = TRUE;
150          break;
151
152       case 't':
153          test_config = TRUE;
154          break;
155
156       case '?':
157       default:
158          usage();
159       }  
160    }
161    argc -= optind;
162    argv += optind;
163
164
165    if (!no_signals) {
166       init_signals(terminate_console);
167    }
168
169    if (argc) {
170       usage();
171    }
172
173    if (configfile == NULL) {
174       configfile = bstrdup(CONFIG_FILE);
175    }
176
177    parse_config(configfile);
178
179    LockRes();
180    ndir = 0;
181    foreach_res(dir, R_DIRECTOR) {
182       ndir++;
183    }
184    UnlockRes();
185    if (ndir == 0) {
186       Emsg1(M_ERROR_TERM, 0, _("No director resource defined in %s\n\
187 Without that I don't how to speak to the Director :-(\n"), configfile);
188    }
189
190
191
192    console = create_console();
193    gtk_window_set_default_size(GTK_WINDOW(console), 800, 700);
194    run_dialog = create_RunDialog();
195    label_dialog = create_label_dialog();
196    restore_dialog = create_RestoreDialog();
197    about1 = create_about1();
198
199    text1 = lookup_widget(console, "text1");
200    entry1 = lookup_widget(console, "entry1");
201    status1 = lookup_widget(console, "status1");
202    scroll1 = lookup_widget(console, "scroll1");
203
204    select_restore_setup();
205
206    gtk_widget_show(console);
207
208 /*
209  * Thanks to Phil Stracchino for providing the font configuration code.
210  * original default:
211    text_font = gdk_font_load("-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-koi8-r");
212  * this works for me:
213    text_font = gdk_font_load("-Bigelow & Holmes-lucida console-medium-r-semi condensed-*-12-0-100-100-m-0-iso8859-1");
214  * and, new automagic:font specification!
215  */
216
217    LockRes();
218    foreach_res(con_font, R_CONSOLE_FONT) {
219        if (!con_font->fontface) {
220           Dmsg1(400, "No fontface for %s\n", con_font->hdr.name);
221           continue;
222        }
223        text_font = gdk_font_load(con_font->fontface);
224        if (text_font == NULL) {
225            Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
226                   con_font->hdr.name, con_font->fontface);
227        } else {
228            Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
229                   con_font->hdr.name, con_font->fontface);
230            break;
231        }           
232    }
233    UnlockRes();
234
235    if (text_font == NULL) {
236        Dmsg1(400, "Attempting to load fallback font %s\n",
237               "-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
238        text_font = gdk_font_load("-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
239    }
240    font_desc = pango_font_description_from_string("LucidaTypewriter 9");
241    gtk_widget_modify_font(console, font_desc);
242    gtk_widget_modify_font(text1, font_desc);
243    gtk_widget_modify_font(entry1, font_desc);
244    gtk_widget_modify_font(status1, font_desc);
245    pango_font_description_free(font_desc);
246
247    if (test_config) {
248       terminate_console(0);
249       exit(0);
250    }
251
252    initial = gtk_timeout_add(100, initial_connect_to_director, (gpointer)NULL);
253
254    gtk_main();
255    quit = true;
256    disconnect_from_director((gpointer)NULL);
257    return 0;
258 }
259
260 /*
261  * Every 5 seconds, ask the Director for our
262  *  messages.
263  */
264 static gint message_handler(gpointer data)
265 {
266    if (ready && UA_sock) {
267       bnet_fsend(UA_sock, ".messages");
268    }
269    return TRUE;
270 }
271
272 int disconnect_from_director(gpointer data)
273 {
274    if (!quit) {
275       set_status(_(" Not Connected"));
276    }
277    if (UA_sock) {
278       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
279       bnet_close(UA_sock);
280       UA_sock = NULL;
281    }
282    return 1;
283 }
284
285 /*
286  * Called just after the main loop is started to allow
287  *  us to connect to the Director.
288  */
289 static int initial_connect_to_director(gpointer data)
290 {
291    gtk_timeout_remove(initial);
292    if (connect_to_director(data)) {
293       start_director_reader(data);
294    }
295    gtk_timeout_add(5000, message_handler, (gpointer)NULL);
296    return TRUE;
297 }
298
299 static GList *get_list(char *cmd)
300 {
301    GList *options;
302    char *msg;
303
304    options = NULL;
305    write_director(cmd);
306    while (bnet_recv(UA_sock) > 0) {
307       strip_trailing_junk(UA_sock->msg);
308       msg = (char *)malloc(strlen(UA_sock->msg) + 1);
309       strcpy(msg, UA_sock->msg);
310       options = g_list_append(options, msg);
311    }
312    return options;
313    
314 }
315
316 static GList *get_and_fill_combo(GtkWidget *dialog, const char *combo_name, const char *cm)
317 {
318    GtkWidget *combo;
319    GList *options;
320
321    combo = lookup_widget(dialog, combo_name);
322    options = get_list(cmd);
323    if (combo && options) {
324       gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
325    }
326    return options;
327 }
328
329 static void fill_combo(GtkWidget *dialog, const char *combo_name, GList *options)
330 {
331    GtkWidget *combo;
332
333    combo = lookup_widget(dialog, combo_name);
334    if (combo && options) {
335       gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
336    }
337    return;
338 }
339
340
341 /*
342  * Connect to Director. If there are more than one, put up
343  * a modal dialog so that the user chooses one.
344  */
345 int connect_to_director(gpointer data)
346 {
347    GList *dirs = NULL;
348    GtkWidget *combo;
349    JCR jcr;
350
351
352    if (UA_sock) {
353       return 0;
354    }
355
356    if (ndir > 1) {
357       LockRes();
358       foreach_res(dir, R_DIRECTOR) {
359          dirs = g_list_append(dirs, dir->hdr.name);
360       }
361       UnlockRes();
362       dir_dialog = create_SelectDirectorDialog();
363       combo = lookup_widget(dir_dialog, "combo1");
364       dir_select = lookup_widget(dir_dialog, "dirselect");
365       if (dirs) {
366          gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);   
367       }
368       gtk_widget_show(dir_dialog);
369       gtk_main();
370
371       if (reply == OK) {
372          gchar *ecmd = gtk_editable_get_chars((GtkEditable *)dir_select, 0, -1);
373          dir = (DIRRES *)GetResWithName(R_DIRECTOR, ecmd);
374          if (ecmd) {
375             g_free(ecmd);             /* release director name string */
376          }
377       }
378       if (dirs) {
379          g_free(dirs);
380       }
381       gtk_widget_destroy(dir_dialog);
382       dir_dialog = NULL;
383    } else {
384       /* Just take the first Director */
385       LockRes();
386       dir = (DIRRES *)GetNextRes(R_DIRECTOR, NULL);
387       UnlockRes();
388    }
389
390    if (!dir) {
391       return 0;
392    }
393
394    memset(&jcr, 0, sizeof(jcr));
395    
396    set_statusf(_(" Connecting to Director %s:%d"), dir->address,dir->DIRport);
397    set_textf(_("Connecting to Director %s:%d\n\n"), dir->address,dir->DIRport);
398
399    while (gtk_events_pending()) {     /* fully paint screen */
400       gtk_main_iteration();
401    }
402    UA_sock = bnet_connect(NULL, 5, 15, "Director daemon", dir->address, 
403                           NULL, dir->DIRport, 0);
404    if (UA_sock == NULL) {
405       return 0;
406    }
407    
408    jcr.dir_bsock = UA_sock;
409    LockRes();
410    /* If cons==NULL, default console will be used */
411    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
412    UnlockRes();
413    if (!authenticate_director(&jcr, dir, cons)) {
414       set_text(UA_sock->msg, UA_sock->msglen);
415       return 0;
416    }
417
418    set_status(" Initializing ...");
419
420    bnet_fsend(UA_sock, "autodisplay on");
421
422    /* Read and display all initial messages */
423    while (bnet_recv(UA_sock) > 0) {
424       set_text(UA_sock->msg, UA_sock->msglen);
425    }
426
427    /* Paint changes */
428    while (gtk_events_pending()) {
429       gtk_main_iteration();
430    }
431
432    /* Fill the run_dialog combo boxes */
433    job_list      = get_and_fill_combo(run_dialog, "combo_job", ".jobs");
434    client_list   = get_and_fill_combo(run_dialog, "combo_client", ".clients");
435    fileset_list  = get_and_fill_combo(run_dialog, "combo_fileset", ".filesets");
436    messages_list = get_and_fill_combo(run_dialog, "combo_messages", ".msgs");
437    pool_list     = get_and_fill_combo(run_dialog, "combo_pool", ".pools");
438    storage_list  = get_and_fill_combo(run_dialog, "combo_storage", ".storage");
439    type_list     = get_and_fill_combo(run_dialog, "combo_type", ".types");
440    level_list    = get_and_fill_combo(run_dialog, "combo_level", ".levels");
441
442    /* Fill the label dialog combo boxes */
443    fill_combo(label_dialog, "label_combo_storage", storage_list);
444    fill_combo(label_dialog, "label_combo_pool", pool_list);
445
446
447    /* Fill the restore_dialog combo boxes */
448    fill_combo(restore_dialog, "combo_restore_job", job_list);
449    fill_combo(restore_dialog, "combo_restore_client", client_list);
450    fill_combo(restore_dialog, "combo_restore_fileset", fileset_list);
451    fill_combo(restore_dialog, "combo_restore_pool", pool_list);
452    fill_combo(restore_dialog, "combo_restore_storage", storage_list);
453
454    set_status(" Connected");
455    return 1;
456 }
457
458 void write_director(const gchar *msg)
459 {
460    if (UA_sock) {
461       at_prompt = false;
462       set_status(_(" Processing command ..."));
463       UA_sock->msglen = strlen(msg);
464       pm_strcpy(&UA_sock->msg, msg);
465       bnet_send(UA_sock);
466    }
467    if (strcmp(msg, ".quit") == 0 || strcmp(msg, ".exit") == 0) {
468       disconnect_from_director((gpointer)NULL);
469       gtk_main_quit();
470    }
471 }
472
473 extern "C"
474 void read_director(gpointer data, gint fd, GdkInputCondition condition)
475 {
476    int stat;
477
478    if (!UA_sock || UA_sock->fd != fd) {
479       return;
480    }
481    stat = bnet_recv(UA_sock);
482    if (stat >= 0) {
483       if (at_prompt) {
484          set_text("\n", 1);
485          at_prompt = false;
486       }
487       set_text(UA_sock->msg, UA_sock->msglen);
488       return;
489    }
490    if (is_bnet_stop(UA_sock)) {         /* error or term request */
491       gtk_main_quit();
492       return;
493    }
494    /* Must be a signal -- either do something or ignore it */
495    if (UA_sock->msglen == BNET_PROMPT) {
496       at_prompt = true;
497       set_status(_(" At prompt waiting for input ..."));
498    }
499    if (UA_sock->msglen == BNET_EOD) {
500       set_status_ready();
501    }
502    return;
503 }
504
505 static gint tag;
506
507 void start_director_reader(gpointer data)
508 {
509
510    if (director_reader_running || !UA_sock) {
511       return;
512    }
513    tag = gdk_input_add(UA_sock->fd, GDK_INPUT_READ, read_director, NULL);
514    director_reader_running = true;
515 }
516
517 void stop_director_reader(gpointer data)
518 {
519    if (!director_reader_running) {
520       return;
521    }
522    gdk_input_remove(tag);
523    gdk_input_remove(tag);
524    gdk_input_remove(tag);
525    gdk_input_remove(tag);
526    gdk_input_remove(tag);
527    gdk_input_remove(tag);
528    gdk_input_remove(tag);
529    gdk_input_remove(tag);
530    gdk_input_remove(tag);
531    gdk_input_remove(tag);
532    gdk_input_remove(tag);
533    director_reader_running = false;
534 }
535
536
537
538 /* Cleanup and then exit */
539 void terminate_console(int sig)
540 {
541    static int already_here = FALSE;
542
543    if (already_here)                  /* avoid recursive temination problems */
544       exit(1);
545    already_here = TRUE;
546    disconnect_from_director((gpointer)NULL);
547    gtk_main_quit();
548    exit(0);
549 }
550
551
552 /* Buffer approx 2000 lines -- assume 60 chars/line */
553 #define MAX_TEXT_CHARS   (2000 * 60)
554 static int text_chars = 0;
555
556 static void truncate_text_chars()
557 {
558    GtkTextBuffer *textbuf;
559    GtkTextIter iter, iter2;
560    guint len;
561    int del_chars = MAX_TEXT_CHARS / 4;
562
563    textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
564    len = gtk_text_buffer_get_char_count(textbuf);
565    gtk_text_buffer_get_iter_at_offset (textbuf, &iter, 0);
566    gtk_text_buffer_get_iter_at_offset (textbuf, &iter2, del_chars);
567    gtk_text_buffer_delete (textbuf, &iter, &iter2);
568    text_chars -= del_chars;
569    len = gtk_text_buffer_get_char_count(textbuf);
570    gtk_text_iter_set_offset(&iter, len);
571 }
572
573 void set_textf(const char *fmt, ...)
574 {
575    va_list arg_ptr;
576    char buf[1000];
577    int len;
578    va_start(arg_ptr, fmt);
579    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
580    va_end(arg_ptr);
581    set_text(buf, len);
582 }
583
584 void set_text(const char *buf, int len)
585 {
586    GtkTextBuffer *textbuf;
587    GtkTextIter iter;
588    guint buf_len;
589
590    textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
591    buf_len = gtk_text_buffer_get_char_count(textbuf);
592    gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
593    gtk_text_buffer_insert(textbuf, &iter, buf, -1);
594    text_chars += len;
595    if (text_chars > MAX_TEXT_CHARS) {
596       truncate_text_chars();
597    }
598    set_scroll_bar_to_end();
599 }
600
601 void set_statusf(const char *fmt, ...)
602 {
603    va_list arg_ptr;
604    char buf[1000];
605    int len;
606    va_start(arg_ptr, fmt);
607    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
608    va_end(arg_ptr);
609    gtk_label_set_text(GTK_LABEL(status1), buf);
610 // set_scroll_bar_to_end();
611    ready = false;
612 }
613
614 void set_status_ready()    
615 {
616    gtk_label_set_text(GTK_LABEL(status1), " Ready");
617    ready = true;
618 // set_scroll_bar_to_end();
619 }
620
621 void set_status(const char *buf)
622 {
623    gtk_label_set_text(GTK_LABEL(status1), buf);
624 // set_scroll_bar_to_end();
625    ready = false;
626 }
627
628 static void set_scroll_bar_to_end(void)
629 {
630    GtkTextBuffer* textbuf = NULL;
631    GtkTextIter iter;
632    guint buf_len;
633
634    textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1));
635    buf_len = gtk_text_buffer_get_char_count(textbuf);
636    gtk_text_buffer_get_iter_at_offset(textbuf, &iter, buf_len - 1);
637    gtk_text_iter_set_offset(&iter, buf_len);
638    gtk_text_buffer_place_cursor(textbuf, &iter);
639    gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(text1),
640               gtk_text_buffer_get_mark(textbuf, "insert"), 
641               0, TRUE, 0.0, 1.0);
642 }