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