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