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