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