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