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