]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/wx-console/console_thread.cpp
Send M_ERROR_TERM and M_ABORT messages to stdout on Windows.
[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       add_msg_dest(msgs, MD_STDOUT, i, NULL, NULL);
220 //    add_msg_dest(msgs, MD_SYSLOG, i, NULL, NULL);
221       add_msg_dest(msgs, MD_CONSOLE, i, NULL, NULL);
222    }
223    
224    init_msg(NULL, msgs);
225    //init_console_msg(console_thread::working_dir.mb_str(*wxConvCurrent));
226
227    errmsg = wxT("");
228    if (!parse_config(configfile.mb_str(*wxConvCurrent), &scan_err)) {
229       configloaded = false;
230       term_msg();
231       return errmsg;
232    }
233    
234    if (init_crypto() != 0) {
235       Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("Cryptographic library initialization failed.\n")).mb_str(*wxConvCurrent));
236    }
237
238    if (!check_resources()) {
239       Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("Please correct configuration file.\n")).mb_str(*wxConvCurrent));
240    }
241
242    term_msg();
243    wxRemoveFile(console_thread::working_dir + wxT("/wx-console.conmsg"));
244    init_msg(NULL, NULL);
245    
246    configloaded = true;
247    
248    return wxT("");
249 }
250
251 // class constructor
252 console_thread::console_thread() {
253    UA_sock = NULL;
254    choosingdirector = false;
255 }
256
257 // class destructor
258 console_thread::~console_thread() {
259    if (UA_sock) {
260       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
261       bnet_close(UA_sock);
262       UA_sock = NULL;
263    }
264 }
265
266 /*
267  * Thread entry point
268  */
269 void* console_thread::Entry() {
270 #ifndef HAVE_WIN32
271    /* It seems we must redefine the locale on each thread on wxGTK. 
272     * On Win32 it makes wx-console crash. */
273    wxLocale m_locale;
274    m_locale.Init();
275    m_locale.AddCatalog(wxT("bacula"));
276    wxLocale::AddCatalogLookupPathPrefix(wxT(LOCALEDIR));
277 #endif
278
279    DIRRES* dir;
280    if (!inited) {
281       csprint(_("Error : Library not initialized\n"));
282       csprint(NULL, CS_END);
283       csprint(NULL, CS_DISCONNECTED);
284       csprint(NULL, CS_TERMINATED);
285 #ifdef HAVE_WIN32
286       Exit();
287 #endif
288       return NULL;
289    }
290    
291    if (!configloaded) {
292       csprint(_("Error : No configuration file loaded\n"));
293       csprint(NULL, CS_END);
294       csprint(NULL, CS_DISCONNECTED);
295       csprint(NULL, CS_TERMINATED);
296 #ifdef HAVE_WIN32
297       Exit();
298 #endif
299       return NULL;
300    }
301    
302    csprint(_("Connecting...\n"));
303   
304    int count = 0;
305    DIRRES* res[16]; /* Maximum 16 directors */
306    
307    LockRes();
308    foreach_res(dir, R_DIRECTOR) {
309       res[count] = dir;
310       count++;
311       if (count == 16) {
312          break;
313       }
314    }
315    UnlockRes();
316    
317    if (count == 0) {
318       csprint(_("Error : No director defined in config file.\n"));
319       csprint(NULL, CS_END);
320       csprint(NULL, CS_DISCONNECTED);
321       csprint(NULL, CS_TERMINATED);
322 #ifdef HAVE_WIN32
323       Exit();
324 #endif
325       return NULL;
326    } else if (count == 1) {
327       directorchoosen = 1;
328    } else {
329       while (true) {
330          csprint(_("Multiple directors found in your config file.\n"));
331          for (int i = 0; i < count; i++) {
332             if (i < 9) {
333                csprint(wxString(wxT("    ")) << (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
334             }
335             else {
336                csprint(wxString(wxT("   ")) <<  (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
337             }
338          }
339          csprint(wxString::Format(_("Please choose a director (1-%d): "), count), CS_DATA);
340          csprint(NULL, CS_PROMPT);
341          choosingdirector = true;
342          directorchoosen = -1;
343          while(directorchoosen == -1) {
344             bmicrosleep(0, 2000);
345             Yield();
346          }      
347          choosingdirector = false;
348          if (directorchoosen != 0) {
349             break;
350          }
351       }
352    }
353    dir = res[directorchoosen-1];
354
355    memset(&jcr, 0, sizeof(jcr));
356    
357    jcr.dequeuing = 1; /* TODO: catch messages */
358
359    LockRes();
360    /* If cons==NULL, default console will be used */
361    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
362    UnlockRes();
363
364    char buf[1024];
365    /* Initialize Console TLS context */
366    if (cons && (cons->tls_enable || cons->tls_require)) {
367       /* Generate passphrase prompt */
368       bsnprintf(buf, sizeof(buf), wxString(_("Passphrase for Console \"%s\" TLS private key: ")).mb_str(*wxConvCurrent), cons->hdr.name);
369
370       /* Initialize TLS context:
371        * Args: CA certfile, CA certdir, Certfile, Keyfile,
372        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
373       cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
374          cons->tls_ca_certdir, cons->tls_certfile,
375          cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
376
377       if (!cons->tls_ctx) {
378          bsnprintf(buf, sizeof(buf), wxString(_("Failed to initialize TLS context for Console \"%s\".\n")).mb_str(*wxConvCurrent),
379             dir->hdr.name);
380          csprint(buf);
381          return NULL;
382       }
383
384    }
385
386    /* Initialize Director TLS context */
387    if (dir->tls_enable || dir->tls_require) {
388       /* Generate passphrase prompt */
389       bsnprintf(buf, sizeof(buf), wxString(_("Passphrase for Director \"%s\" TLS private key: ")).mb_str(*wxConvCurrent), dir->hdr.name);
390
391       /* Initialize TLS context:
392        * Args: CA certfile, CA certdir, Certfile, Keyfile,
393        * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
394       dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
395          dir->tls_ca_certdir, dir->tls_certfile,
396          dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
397
398       if (!dir->tls_ctx) {
399          bsnprintf(buf, sizeof(buf), wxString(_("Failed to initialize TLS context for Director \"%s\".\n")).mb_str(*wxConvCurrent),
400             dir->hdr.name);
401          csprint(buf);
402          return NULL;
403       }
404    }
405
406
407    UA_sock = bnet_connect(&jcr, 3, 3, wxString(_("Director daemon")).mb_str(*wxConvCurrent),
408       dir->address, NULL, dir->DIRport, 0);
409       
410    if (UA_sock == NULL) {
411       csprint(_("Failed to connect to the director\n"));
412       csprint(NULL, CS_END);
413       csprint(NULL, CS_DISCONNECTED);
414       csprint(NULL, CS_TERMINATED);
415 #ifdef HAVE_WIN32
416       Exit();
417 #endif
418       return NULL;
419    }
420
421    csprint(_("Connected\n"));
422
423    jcr.dir_bsock = UA_sock;
424    if (!authenticate_director(&jcr, dir, cons)) {
425       csprint("ERR=");
426       csprint(UA_sock->msg);
427       csprint(NULL, CS_END);
428       csprint(NULL, CS_DISCONNECTED);
429       csprint(NULL, CS_TERMINATED);
430 #ifdef HAVE_WIN32
431       Exit();
432 #endif
433       return NULL;
434    }
435    
436    csprint(NULL, CS_CONNECTED);
437    
438    Write("autodisplay on\n");
439    Write(".messages\n");
440
441    int stat;
442
443    int last_is_eod = 0; /* Last packet received is BNET_EOD */
444    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) */
445
446    /* main loop */
447    while(!TestDestroy()) {   /* Tests if thread has been ended */
448       stat = bnet_wait_data(UA_sock, 10);
449       if (stat == 0) {
450          if (last_is_eod) {
451             Write(".messages\n");
452             do_not_forward_eod = 1;
453          }
454          continue;
455       }
456       
457       last_is_eod = 0;
458       if ((stat = bnet_recv(UA_sock)) >= 0) {
459          if (do_not_forward_eod) { /* .messages got data: remove the prompt */
460             csprint(NULL, CS_REMOVEPROMPT);
461          }
462          csprint(UA_sock->msg);
463       }
464       else if (stat == BNET_SIGNAL) {
465          if (UA_sock->msglen == BNET_PROMPT) {
466             csprint(NULL, CS_PROMPT);
467          } else if (UA_sock->msglen == BNET_EOD) {
468             last_is_eod = 1;
469             if (!do_not_forward_eod)
470                csprint(NULL, CS_END);
471          } else if (UA_sock->msglen == BNET_HEARTBEAT) {
472             bnet_sig(UA_sock, BNET_HB_RESPONSE);
473             csprint(_("<< Heartbeat signal received, answered. >>\n"), CS_DEBUG);
474          } else if (UA_sock->msglen == BNET_START_SELECT ||
475                     UA_sock->msglen == BNET_END_SELECT) {
476            /* Ignore start/end selections for now */
477          } else {
478             csprint(_("<< Unexpected signal received : "), CS_DEBUG);
479             csprint(bnet_sig_to_ascii(UA_sock), CS_DEBUG);
480             csprint(">>\n", CS_DEBUG);
481          }
482       }
483       else { /* BNET_HARDEOF || BNET_ERROR */
484          csprint(NULL, CS_END);
485          break;
486       }
487            
488       if (is_bnet_stop(UA_sock)) {
489          csprint(NULL, CS_END);
490          break;            /* error or term */
491       }
492       
493       do_not_forward_eod = 0;
494    }
495    
496    csprint(NULL, CS_DISCONNECTED);
497
498    csprint(_("Connection terminated\n"));
499    
500    UA_sock = NULL;
501
502    csprint(NULL, CS_TERMINATED);
503
504 #ifdef HAVE_WIN32
505    Exit();
506 #endif
507    
508    return NULL;
509 }
510
511 void console_thread::Write(const char* str) 
512 {
513    if (UA_sock) {
514       UA_sock->msglen = (int32_t)strlen(str);
515       pm_strcpy(&UA_sock->msg, str);
516       bnet_send(UA_sock);
517    } else if (choosingdirector) {
518 //      wxString number = str;
519 //      number.RemoveLast(); /* Removes \n */
520       long val;
521       
522 //      if (number.ToLong(&val)) {
523       val = atol(str);
524       if (val) {
525          directorchoosen = (int)val;
526       } else {
527          directorchoosen = 0;
528       }
529    }
530 }
531
532 void console_thread::Delete() {
533    Write("quit\n");
534    if (UA_sock) {
535       bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
536       bnet_close(UA_sock);
537       UA_sock = NULL;
538       /*csprint(NULL, CS_END);
539       csprint(NULL, CS_DISCONNECTED);
540       csprint(NULL, CS_TERMINATED);*/
541    }
542 }