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