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