]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/wx-console/console_thread.cpp
eb54a5bc1e0554b2013e8111862dd6d83d7b17ee
[bacula/bacula] / bacula / src / wx-console / console_thread.cpp
1 /*
2  *
3  *    Interaction thread between director and the GUI
4  *
5  *    Nicolas Boichat, April 2004
6  *
7  *    Version $Id$
8  */
9 /*
10    Copyright (C) 2004-2005 Kern Sibbald
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License
14    version 2 as amended with additional clauses defined in the
15    file LICENSE in the main source directory.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
20    the file LICENSE for additional details.
21
22  */
23
24 // http://66.102.9.104/search?q=cache:Djc1mPF3hRoJ:cvs.sourceforge.net/viewcvs.py/audacity/audacity-src/src/AudioIO.cpp%3Frev%3D1.102+macos+x+wxthread&hl=fr
25
26 #include "console_thread.h" // class's header file
27
28 #include <wx/wxprec.h>
29
30 #include <wx/thread.h>
31 #include <wx/file.h>
32 #include <bacula.h>
33 #include <jcr.h>
34
35 #include "console_conf.h"
36
37 #include "csprint.h"
38
39 #ifdef HAVE_WIN32
40 #include <windows.h>
41 char OK_msg[]   = "2000 OK\n";
42 char TERM_msg[] = "2999 Terminate\n";
43 #endif
44
45 /* Imported functions */
46 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
47
48 bool console_thread::inited = false;
49 bool console_thread::configloaded = false;
50 wxString console_thread::working_dir = wxT(".");
51
52 int numdir = 0;
53
54 /*
55  * Call-back for reading a passphrase for an encrypted PEM file
56  * This function uses getpass(), which uses a static buffer and is NOT thread-safe.
57  */
58 static int tls_pem_callback(char *buf, int size, const void *userdata)
59 {
60 #ifdef HAVE_TLS
61    const char *prompt = (const char *) userdata;
62    char *passwd;
63
64    passwd = getpass(prompt);
65    bstrncpy(buf, passwd, size);
66    return (strlen(buf));
67 #else
68    buf[0] = 0;
69    return 0;
70 #endif
71 }
72
73
74 /*
75  * Make a quick check to see that we have all the
76  * resources needed.
77  */
78 static int check_resources()
79 {
80    int xOK = true;
81    DIRRES *director;
82
83    LockRes();
84
85    numdir = 0;
86    foreach_res(director, R_DIRECTOR) {
87       numdir++;
88       /* tls_require implies tls_enable */
89       if (director->tls_require) {
90          if (have_tls) {
91             director->tls_enable = true;
92          } else {
93             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
94             xOK = false;
95             continue;
96          }
97       }
98
99       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
100          Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
101                              " or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in config file.\n"
102                              " At least one CA certificate store is required.\n"),
103                              director->hdr.name);
104          xOK = false;
105       }
106    }
107    
108    if (numdir == 0) {
109       Jmsg(NULL, M_FATAL, 0, _("No Director resource defined in config file.\n"
110                           "Without that I don't how to speak to the Director :-(\n"));
111       xOK = false;
112    }
113
114    CONRES *cons;
115    /* Loop over Consoles */
116    foreach_res(cons, R_CONSOLE) {
117       /* tls_require implies tls_enable */
118       if (cons->tls_require) {
119          if (have_tls) {
120             cons->tls_enable = true;
121          } else {
122             Jmsg(NULL, M_FATAL, 0, _("TLS required but not configured in Bacula.\n"));
123             xOK = false;
124             continue;
125          }
126       }
127
128       if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
129          Jmsg(NULL, M_FATAL, 0, _("Neither \"TLS CA Certificate\""
130                              " or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in config file.\n"),
131                              cons->hdr.name);
132          xOK = false;
133       }
134    }
135    UnlockRes();
136    return xOK;
137 }
138
139
140 void console_thread::SetWorkingDirectory(wxString w_dir) {
141    if ((w_dir.Last() == '/') || (w_dir.Last() == '\\')) {
142       console_thread::working_dir = w_dir.Mid(0, w_dir.Length()-1);
143    }
144    else {
145       console_thread::working_dir = w_dir;
146    }
147 }
148
149 void console_thread::InitLib() {
150    if (WSA_Init() != 0) {
151       csprint(_("Error while initializing windows sockets...\n"));
152       inited = false;
153       return;
154    }
155    
156    init_stack_dump();
157    my_name_is(0, NULL, "wx-console");
158    working_directory = (const char*) console_thread::working_dir.GetData();
159    
160    inited = true;
161 }
162
163 void console_thread::FreeLib() {
164    if (inited) {
165       if (WSACleanup() != 0) {
166          csprint(_("Error while cleaning up windows sockets...\n"));
167       }
168    }
169 }
170
171 wxString errmsg;
172
173 /*
174  * Format a scanner error message
175  */
176 static void scan_err(const char *file, int line, LEX *lc, const char *msg, ...)
177 {
178    va_list arg_ptr;
179    char buf[MAXSTRING];
180    char more[MAXSTRING];
181    wxString err;
182    
183    va_start(arg_ptr, msg);
184    bvsnprintf(buf, sizeof(buf), msg, arg_ptr);
185    va_end(arg_ptr);
186
187    if (lc->line_no > lc->begin_line_no) {
188       bsnprintf(more, sizeof(more),
189                 _("Problem probably begins at line %d.\n"), lc->begin_line_no);
190    } else {
191       more[0] = 0;
192    }
193
194    err.Format(wxT(_("Config error: %s\n            : line %d, col %d of file %s\n%s\n%s")),
195       buf, lc->line_no, lc->col_no, lc->fname, lc->line, more);
196      
197    errmsg << err; 
198 }
199
200 wxString console_thread::LoadConfig(wxString configfile) {
201    if (!inited) {
202       InitLib();
203       if (!inited)
204          return wxT(_("Error while initializing library."));
205    }
206    
207    free_config_resources();
208    
209    MSGS* msgs = (MSGS *)malloc(sizeof(MSGS));
210    memset(msgs, 0, sizeof(MSGS));
211    for (int i=1; i<=M_MAX; i++) {
212 #ifndef WIN32
213       add_msg_dest(msgs, MD_STDOUT, i, NULL, NULL);
214 #endif
215 //    add_msg_dest(msgs, MD_SYSLOG, i, NULL, NULL);
216       add_msg_dest(msgs, MD_CONSOLE, i, NULL, NULL);
217    }
218    
219    init_msg(NULL, msgs);
220    init_console_msg(console_thread::working_dir.mb_str(*wxConvCurrent));
221
222    errmsg = wxT("");
223    if (!parse_config(configfile.mb_str(*wxConvCurrent), &scan_err)) {
224       configloaded = false;
225       term_msg();
226       return errmsg;
227    }
228    
229    if (init_tls() != 0) {
230       Jmsg(NULL, M_ERROR_TERM, 0, _("TLS library initialization failed.\n"));
231    }
232
233    if (!check_resources()) {
234       Jmsg(NULL, M_ERROR_TERM, 0, _("Please correct configuration file.\n"));
235    }
236
237    term_msg();
238    wxRemoveFile(console_thread::working_dir + wxT("/wx-console.conmsg"));
239    init_msg(NULL, NULL);
240    
241    configloaded = true;
242    
243    return wxT("");
244 }
245
246 // class constructor
247 console_thread::console_thread() {
248    UA_sock = NULL;
249    choosingdirector = false;
250 }
251
252 // class destructor
253 console_thread::~console_thread() {
254    if (UA_sock) {
255       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
256       bnet_close(UA_sock);
257       UA_sock = NULL;
258    }
259 }
260
261 /*
262  * Thread entry point
263  */
264 void* console_thread::Entry() {
265    DIRRES* dir;
266    if (!inited) {
267       csprint(_("Error : Library not initialized\n"));
268       csprint(NULL, CS_END);
269       csprint(NULL, CS_DISCONNECTED);
270       csprint(NULL, CS_TERMINATED);
271       #ifdef HAVE_WIN32
272          Exit();
273       #endif
274       return NULL;
275    }
276    
277    if (!configloaded) {
278       csprint(_("Error : No configuration file loaded\n"));
279       csprint(NULL, CS_END);
280       csprint(NULL, CS_DISCONNECTED);
281       csprint(NULL, CS_TERMINATED);
282       #ifdef HAVE_WIN32
283          Exit();
284       #endif
285       return NULL;
286    }
287    
288    csprint(_("Connecting...\n"));
289   
290    int count = 0;
291    DIRRES* res[16]; /* Maximum 16 directors */
292    
293    LockRes();
294    foreach_res(dir, R_DIRECTOR) {
295       res[count] = dir;
296       count++;
297       if (count == 16) {
298          break;
299       }
300    }
301    UnlockRes();
302    
303    if (count == 0) {
304       csprint(_("Error : No director defined in config file.\n"));
305       csprint(NULL, CS_END);
306       csprint(NULL, CS_DISCONNECTED);
307       csprint(NULL, CS_TERMINATED);
308       #ifdef HAVE_WIN32
309          Exit();
310       #endif
311       return NULL;
312    } else if (count == 1) {
313       directorchoosen = 1;
314    } else {
315       while (true) {
316          csprint(_("Multiple directors found in your config file.\n"));
317          for (int i = 0; i < count; i++) {
318             if (i < 9) {
319                csprint(wxString(wxT("    ")) << (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
320             }
321             else {
322                csprint(wxString(wxT("   ")) <<  (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
323             }
324          }
325          csprint(wxString::Format(wxT(_("Please choose a director (1-%d): ")), count), CS_DATA);
326          csprint(NULL, CS_PROMPT);
327          choosingdirector = true;
328          directorchoosen = -1;
329          while(directorchoosen == -1) {
330             bmicrosleep(0, 2000);
331             Yield();
332          }      
333          choosingdirector = false;
334          if (directorchoosen != 0) {
335             break;
336          }
337       }
338    }
339    dir = res[directorchoosen-1];
340
341    memset(&jcr, 0, sizeof(jcr));
342    
343    jcr.dequeuing = 1; /* TODO: catch messages */
344
345    LockRes();
346    /* If cons==NULL, default console will be used */
347    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
348    UnlockRes();
349
350    char buf[1024];
351    /* Initialize Console TLS context */
352    if (cons && (cons->tls_enable || cons->tls_require)) {
353       /* Generate passphrase prompt */
354       bsnprintf(buf, sizeof(buf), _("Passphrase for Console \"%s\" TLS private key: "), cons->hdr.name);
355
356       /* Initialize TLS context:
357        * Args: CA certfile, CA certdir, Certfile, Keyfile,
358        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
359       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
360          cons->tls_ca_certdir, cons->tls_certfile,
361          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
362
363       if (!cons->tls_ctx) {
364          bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Console \"%s\".\n"),
365             dir->hdr.name);
366          csprint(buf);
367          return NULL;
368       }
369
370    }
371
372    /* Initialize Director TLS context */
373    if (dir->tls_enable || dir->tls_require) {
374       /* Generate passphrase prompt */
375       bsnprintf(buf, sizeof(buf), _("Passphrase for Director \"%s\" TLS private key: "), dir->hdr.name);
376
377       /* Initialize TLS context:
378        * Args: CA certfile, CA certdir, Certfile, Keyfile,
379        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
380       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
381          dir->tls_ca_certdir, dir->tls_certfile,
382          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
383
384       if (!dir->tls_ctx) {
385          bsnprintf(buf, sizeof(buf), _("Failed to initialize TLS context for Director \"%s\".\n"),
386             dir->hdr.name);
387          csprint(buf);
388          return NULL;
389       }
390    }
391
392
393    UA_sock = bnet_connect(&jcr, 3, 3, _("Director daemon"),
394       dir->address, NULL, dir->DIRport, 0);
395       
396    if (UA_sock == NULL) {
397       csprint(_("Failed to connect to the director\n"));
398       csprint(NULL, CS_END);
399       csprint(NULL, CS_DISCONNECTED);
400       csprint(NULL, CS_TERMINATED);
401       #ifdef HAVE_WIN32
402          Exit();
403       #endif
404       return NULL;
405    }
406
407    csprint(_("Connected\n"));
408
409    jcr.dir_bsock = UA_sock;
410    if (!authenticate_director(&jcr, dir, cons)) {
411       csprint("ERR=");
412       csprint(UA_sock->msg);
413       csprint(NULL, CS_END);
414       csprint(NULL, CS_DISCONNECTED);
415       csprint(NULL, CS_TERMINATED);
416       #ifdef HAVE_WIN32
417          Exit();
418       #endif
419       return NULL;
420    }
421    
422    csprint(NULL, CS_CONNECTED);
423    
424    Write(".messages\n");
425
426    int stat;
427
428    /* main loop */
429    while(!TestDestroy()) {   /* Tests if thread has been ended */
430       if ((stat = bnet_recv(UA_sock)) >= 0) {
431          csprint(UA_sock->msg);
432       }
433       else if (stat == BNET_SIGNAL) {
434          if (UA_sock->msglen == BNET_PROMPT) {
435             csprint(NULL, CS_PROMPT);
436          }
437          else if (UA_sock->msglen == BNET_EOD) {
438             csprint(NULL, CS_END);
439          }
440          else if (UA_sock->msglen == BNET_HEARTBEAT) {
441             bnet_sig(UA_sock, BNET_HB_RESPONSE);
442             csprint(_("<< Heartbeat signal received, answered. >>\n"), CS_DEBUG);
443          }
444          else {
445             csprint(_("<< Unexpected signal received : "), CS_DEBUG);
446             csprint(bnet_sig_to_ascii(UA_sock), CS_DEBUG);
447             csprint(">>\n", CS_DEBUG);
448          }
449       }
450       else { /* BNET_HARDEOF || BNET_ERROR */
451          csprint(NULL, CS_END);
452          break;
453       }
454            
455       if (is_bnet_stop(UA_sock)) {
456          csprint(NULL, CS_END);
457          break;            /* error or term */
458       }
459    }
460    
461    csprint(NULL, CS_DISCONNECTED);
462
463    csprint(_("Connection terminated\n"));
464    
465    UA_sock = NULL;
466
467    csprint(NULL, CS_TERMINATED);
468
469    #ifdef HAVE_WIN32
470       Exit();
471    #endif
472    
473    return NULL;
474 }
475
476 void console_thread::Write(const char* str) 
477 {
478    if (UA_sock) {
479        UA_sock->msglen = strlen(str);
480        pm_strcpy(&UA_sock->msg, str);
481        bnet_send(UA_sock);
482    } else if (choosingdirector) {
483 //      wxString number = str;
484 //      number.RemoveLast(); /* Removes \n */
485       long val;
486       
487 //      if (number.ToLong(&val)) {
488       val = atol(str);
489       if (val) {
490          directorchoosen = (int)val;
491       } else {
492          directorchoosen = 0;
493       }
494    }
495 }
496
497 void console_thread::Delete() {
498    Write("quit\n");
499    if (UA_sock) {
500       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
501       bnet_close(UA_sock);
502       UA_sock = NULL;
503       /*csprint(NULL, CS_END);
504       csprint(NULL, CS_DISCONNECTED);
505       csprint(NULL, CS_TERMINATED);*/
506    }
507 }