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