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