]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/wx-console/console_thread.cpp
b580b9ee0969d09455c4ac6711553e9d2c502b77
[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-2006 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 /* _("...") macro returns a wxChar*, so if we need a char*, we need to convert it with:
27  * wxString(_("...")).mb_str(*wxConvCurrent) */
28
29 #undef _DEBUG
30
31 #include "bacula.h"
32 #include "console_conf.h"
33
34 #include "console_thread.h" // class's header file
35
36 #include <wx/wxprec.h>
37
38 #include <wx/thread.h>
39 #include <wx/file.h>
40
41 #include "csprint.h"
42
43 #ifdef HAVE_WIN32
44 char OK_msg[]   = "2000 OK\n";
45 char TERM_msg[] = "2999 Terminate\n";
46 #endif
47
48 /* Imported functions */
49 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
50
51 bool console_thread::inited = false;
52 bool console_thread::configloaded = false;
53 wxString console_thread::working_dir = wxT(".");
54
55 int numdir = 0;
56
57 /*
58  * Call-back for reading a passphrase for an encrypted PEM file
59  * This function uses getpass(), which uses a static buffer and is NOT thread-safe.
60  */
61 static int tls_pem_callback(char *buf, int size, const void *userdata)
62 {
63 #if defined(HAVE_TLS) && !defined(HAVE_WIN32)
64    const char *prompt = (const char *) userdata;
65    char *passwd;
66
67    passwd = getpass(prompt);
68    bstrncpy(buf, passwd, size);
69    return (strlen(buf));
70 #else
71    buf[0] = 0;
72    return 0;
73 #endif
74 }
75
76
77 /*
78  * Make a quick check to see that we have all the
79  * resources needed.
80  */
81 static int check_resources()
82 {
83    int xOK = true;
84    DIRRES *director;
85
86    LockRes();
87
88    numdir = 0;
89    foreach_res(director, R_DIRECTOR) {
90       numdir++;
91       /* tls_require implies tls_enable */
92       if (director->tls_require) {
93          if (have_tls) {
94             director->tls_enable = true;
95          } else {
96             Jmsg(NULL, M_FATAL, 0, wxString(_("TLS required but not configured in Bacula.\n")).mb_str(*wxConvCurrent));
97             xOK = false;
98             continue;
99          }
100       }
101
102       if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
103          Jmsg(NULL, M_FATAL, 0, wxString(_("Neither \"TLS CA Certificate\" or \"TLS CA Certificate Dir\" are defined for Director \"%s\" in config file.\nAt least one CA certificate store is required.\n")).mb_str(*wxConvCurrent),
104                              director->hdr.name);
105          xOK = false;
106       }
107    }
108    
109    if (numdir == 0) {
110       Jmsg(NULL, M_FATAL, 0, wxString(_("No Director resource defined in config file.\nWithout that I don't how to speak to the Director :-(\n")).mb_str(*wxConvCurrent));
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, wxString(_("TLS required but not configured in Bacula.\n")).mb_str(*wxConvCurrent));
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, wxString(_("Neither \"TLS CA Certificate\" or \"TLS CA Certificate Dir\" are defined for Console \"%s\" in config file.\n")).mb_str(*wxConvCurrent),
130                              cons->hdr.name);
131          xOK = false;
132       }
133    }
134    UnlockRes();
135    return xOK;
136 }
137
138
139 void console_thread::SetWorkingDirectory(wxString w_dir) {
140    if ((w_dir.Last() == '/') || (w_dir.Last() == '\\')) {
141       console_thread::working_dir = w_dir.Mid(0, w_dir.Length()-1);
142    }
143    else {
144       console_thread::working_dir = w_dir;
145    }
146 }
147
148 void console_thread::InitLib() 
149 {
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 {
165    if (inited) {
166       if (WSACleanup() != 0) {
167          csprint(_("Error while cleaning up windows sockets...\n"));
168       }
169    }
170 }
171
172 wxString errmsg;
173
174 /*
175  * Format a scanner error message
176  */
177 static void scan_err(const char *file, int line, LEX *lc, const char *msg, ...)
178 {
179    va_list arg_ptr;
180    char buf[MAXSTRING];
181    char more[MAXSTRING];
182    wxString err;
183    
184    va_start(arg_ptr, msg);
185    bvsnprintf(buf, sizeof(buf), msg, arg_ptr);
186    va_end(arg_ptr);
187
188    if (lc->line_no > lc->begin_line_no) {
189       bsnprintf(more, sizeof(more),
190                 wxString(_("Problem probably begins at line %d.\n")).mb_str(*wxConvCurrent), lc->begin_line_no);
191    } else {
192       more[0] = 0;
193    }
194
195    err.Format(_("Config error: %s\n            : line %d, col %d of file %s\n%s\n%s"),
196       buf, lc->line_no, lc->col_no, lc->fname, lc->line, more);
197      
198    errmsg << err; 
199 }
200
201 wxString console_thread::LoadConfig(wxString configfile) 
202 {
203    if (!inited) {
204       InitLib();
205       if (!inited)
206          return _("Error while initializing library.");
207    }
208    
209    free_config_resources();
210    
211    MSGS* msgs = (MSGS *)bmalloc(sizeof(MSGS));
212    memset(msgs, 0, sizeof(MSGS));
213    for (int i=1; i<=M_MAX; i++) {
214 #ifndef HAVE_WIN32
215       add_msg_dest(msgs, MD_STDOUT, i, NULL, NULL);
216 #endif
217 //    add_msg_dest(msgs, MD_SYSLOG, i, NULL, NULL);
218       add_msg_dest(msgs, MD_CONSOLE, i, NULL, NULL);
219    }
220    
221    init_msg(NULL, msgs);
222    //init_console_msg(console_thread::working_dir.mb_str(*wxConvCurrent));
223
224    errmsg = wxT("");
225    if (!parse_config(configfile.mb_str(*wxConvCurrent), &scan_err)) {
226       configloaded = false;
227       term_msg();
228       return errmsg;
229    }
230    
231    if (init_crypto() != 0) {
232       Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("Cryptographic library initialization failed.\n")).mb_str(*wxConvCurrent));
233    }
234
235    if (!check_resources()) {
236       Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("Please correct configuration file.\n")).mb_str(*wxConvCurrent));
237    }
238
239    term_msg();
240    wxRemoveFile(console_thread::working_dir + wxT("/wx-console.conmsg"));
241    init_msg(NULL, NULL);
242    
243    configloaded = true;
244    
245    return wxT("");
246 }
247
248 // class constructor
249 console_thread::console_thread() {
250    UA_sock = NULL;
251    choosingdirector = false;
252 }
253
254 // class destructor
255 console_thread::~console_thread() {
256    if (UA_sock) {
257       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
258       bnet_close(UA_sock);
259       UA_sock = NULL;
260    }
261 }
262
263 /*
264  * Thread entry point
265  */
266 void* console_thread::Entry() {
267 #ifndef HAVE_WIN32
268    /* It seems we must redefine the locale on each thread on wxGTK. 
269     * On Win32 it makes wx-console crash. */
270    wxLocale m_locale;
271    m_locale.Init();
272    m_locale.AddCatalog(wxT("bacula"));
273    wxLocale::AddCatalogLookupPathPrefix(wxT(LOCALEDIR));
274 #endif
275
276    DIRRES* dir;
277    if (!inited) {
278       csprint(_("Error : Library not initialized\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    if (!configloaded) {
289       csprint(_("Error : No configuration file loaded\n"));
290       csprint(NULL, CS_END);
291       csprint(NULL, CS_DISCONNECTED);
292       csprint(NULL, CS_TERMINATED);
293 #ifdef HAVE_WIN32
294       Exit();
295 #endif
296       return NULL;
297    }
298    
299    csprint(_("Connecting...\n"));
300   
301    int count = 0;
302    DIRRES* res[16]; /* Maximum 16 directors */
303    
304    LockRes();
305    foreach_res(dir, R_DIRECTOR) {
306       res[count] = dir;
307       count++;
308       if (count == 16) {
309          break;
310       }
311    }
312    UnlockRes();
313    
314    if (count == 0) {
315       csprint(_("Error : No director defined in config file.\n"));
316       csprint(NULL, CS_END);
317       csprint(NULL, CS_DISCONNECTED);
318       csprint(NULL, CS_TERMINATED);
319 #ifdef HAVE_WIN32
320       Exit();
321 #endif
322       return NULL;
323    } else if (count == 1) {
324       directorchoosen = 1;
325    } else {
326       while (true) {
327          csprint(_("Multiple directors found in your config file.\n"));
328          for (int i = 0; i < count; i++) {
329             if (i < 9) {
330                csprint(wxString(wxT("    ")) << (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
331             }
332             else {
333                csprint(wxString(wxT("   ")) <<  (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
334             }
335          }
336          csprint(wxString::Format(_("Please choose a director (1-%d): "), count), CS_DATA);
337          csprint(NULL, CS_PROMPT);
338          choosingdirector = true;
339          directorchoosen = -1;
340          while(directorchoosen == -1) {
341             bmicrosleep(0, 2000);
342             Yield();
343          }      
344          choosingdirector = false;
345          if (directorchoosen != 0) {
346             break;
347          }
348       }
349    }
350    dir = res[directorchoosen-1];
351
352    memset(&jcr, 0, sizeof(jcr));
353    
354    jcr.dequeuing = 1; /* TODO: catch messages */
355
356    LockRes();
357    /* If cons==NULL, default console will be used */
358    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
359    UnlockRes();
360
361    char buf[1024];
362    /* Initialize Console TLS context */
363    if (cons && (cons->tls_enable || cons->tls_require)) {
364       /* Generate passphrase prompt */
365       bsnprintf(buf, sizeof(buf), wxString(_("Passphrase for Console \"%s\" TLS private key: ")).mb_str(*wxConvCurrent), cons->hdr.name);
366
367       /* Initialize TLS context:
368        * Args: CA certfile, CA certdir, Certfile, Keyfile,
369        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
370       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
371          cons->tls_ca_certdir, cons->tls_certfile,
372          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
373
374       if (!cons->tls_ctx) {
375          bsnprintf(buf, sizeof(buf), wxString(_("Failed to initialize TLS context for Console \"%s\".\n")).mb_str(*wxConvCurrent),
376             dir->hdr.name);
377          csprint(buf);
378          return NULL;
379       }
380
381    }
382
383    /* Initialize Director TLS context */
384    if (dir->tls_enable || dir->tls_require) {
385       /* Generate passphrase prompt */
386       bsnprintf(buf, sizeof(buf), wxString(_("Passphrase for Director \"%s\" TLS private key: ")).mb_str(*wxConvCurrent), dir->hdr.name);
387
388       /* Initialize TLS context:
389        * Args: CA certfile, CA certdir, Certfile, Keyfile,
390        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
391       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
392          dir->tls_ca_certdir, dir->tls_certfile,
393          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
394
395       if (!dir->tls_ctx) {
396          bsnprintf(buf, sizeof(buf), wxString(_("Failed to initialize TLS context for Director \"%s\".\n")).mb_str(*wxConvCurrent),
397             dir->hdr.name);
398          csprint(buf);
399          return NULL;
400       }
401    }
402
403
404    UA_sock = bnet_connect(&jcr, 3, 3, wxString(_("Director daemon")).mb_str(*wxConvCurrent),
405       dir->address, NULL, dir->DIRport, 0);
406       
407    if (UA_sock == NULL) {
408       csprint(_("Failed to connect to the director\n"));
409       csprint(NULL, CS_END);
410       csprint(NULL, CS_DISCONNECTED);
411       csprint(NULL, CS_TERMINATED);
412 #ifdef HAVE_WIN32
413       Exit();
414 #endif
415       return NULL;
416    }
417
418    csprint(_("Connected\n"));
419
420    jcr.dir_bsock = UA_sock;
421    if (!authenticate_director(&jcr, dir, cons)) {
422       csprint("ERR=");
423       csprint(UA_sock->msg);
424       csprint(NULL, CS_END);
425       csprint(NULL, CS_DISCONNECTED);
426       csprint(NULL, CS_TERMINATED);
427 #ifdef HAVE_WIN32
428       Exit();
429 #endif
430       return NULL;
431    }
432    
433    csprint(NULL, CS_CONNECTED);
434    
435    Write("autodisplay on\n");
436    Write(".messages\n");
437
438    int stat;
439
440    int last_is_eod = 0; /* Last packet received is BNET_EOD */
441    int do_not_forward_eod = 0; /* Last packet received/sent is .messages, so don't forward EOD. (so wx-console don't show the prompt again) */
442
443    /* main loop */
444    while(!TestDestroy()) {   /* Tests if thread has been ended */
445       stat = bnet_wait_data(UA_sock, 10);
446       if (stat == 0) {
447          if (last_is_eod) {
448             Write(".messages\n");
449             do_not_forward_eod = 1;
450          }
451          continue;
452       }
453       
454       last_is_eod = 0;
455       if ((stat = bnet_recv(UA_sock)) >= 0) {
456          if (do_not_forward_eod) { /* .messages got data: remove the prompt */
457             csprint(NULL, CS_REMOVEPROMPT);
458          }
459          csprint(UA_sock->msg);
460       }
461       else if (stat == BNET_SIGNAL) {
462          if (UA_sock->msglen == BNET_PROMPT) {
463             csprint(NULL, CS_PROMPT);
464          } else if (UA_sock->msglen == BNET_EOD) {
465             last_is_eod = 1;
466             if (!do_not_forward_eod)
467                csprint(NULL, CS_END);
468          } else if (UA_sock->msglen == BNET_HEARTBEAT) {
469             bnet_sig(UA_sock, BNET_HB_RESPONSE);
470             csprint(_("<< Heartbeat signal received, answered. >>\n"), CS_DEBUG);
471          } else if (UA_sock->msglen == BNET_START_SELECT ||
472                     UA_sock->msglen == BNET_END_SELECT) {
473            /* Ignore start/end selections for now */
474          } else {
475             csprint(_("<< Unexpected signal received : "), CS_DEBUG);
476             csprint(bnet_sig_to_ascii(UA_sock), CS_DEBUG);
477             csprint(">>\n", CS_DEBUG);
478          }
479       }
480       else { /* BNET_HARDEOF || BNET_ERROR */
481          csprint(NULL, CS_END);
482          break;
483       }
484            
485       if (is_bnet_stop(UA_sock)) {
486          csprint(NULL, CS_END);
487          break;            /* error or term */
488       }
489       
490       do_not_forward_eod = 0;
491    }
492    
493    csprint(NULL, CS_DISCONNECTED);
494
495    csprint(_("Connection terminated\n"));
496    
497    UA_sock = NULL;
498
499    csprint(NULL, CS_TERMINATED);
500
501 #ifdef HAVE_WIN32
502    Exit();
503 #endif
504    
505    return NULL;
506 }
507
508 void console_thread::Write(const char* str) 
509 {
510    if (UA_sock) {
511       UA_sock->msglen = (int32_t)strlen(str);
512       pm_strcpy(&UA_sock->msg, str);
513       bnet_send(UA_sock);
514    } else if (choosingdirector) {
515 //      wxString number = str;
516 //      number.RemoveLast(); /* Removes \n */
517       long val;
518       
519 //      if (number.ToLong(&val)) {
520       val = atol(str);
521       if (val) {
522          directorchoosen = (int)val;
523       } else {
524          directorchoosen = 0;
525       }
526    }
527 }
528
529 void console_thread::Delete() {
530    Write("quit\n");
531    if (UA_sock) {
532       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
533       bnet_close(UA_sock);
534       UA_sock = NULL;
535       /*csprint(NULL, CS_END);
536       csprint(NULL, CS_DISCONNECTED);
537       csprint(NULL, CS_TERMINATED);*/
538    }
539 }