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