3 * Interaction thread between director and the GUI
5 * Nicolas Boichat, April 2004
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2004-2006 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation and included
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
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
39 /* _("...") macro returns a wxChar*, so if we need a char*, we need to convert it with:
40 * wxString(_("...")).mb_str(*wxConvCurrent) */
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.
50 #include "console_conf.h"
52 #include "console_thread.h" // class's header file
54 #include <wx/wxprec.h>
56 #include <wx/thread.h>
62 char OK_msg[] = "2000 OK\n";
63 char TERM_msg[] = "2999 Terminate\n";
66 /* Imported functions */
67 int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
69 bool console_thread::inited = false;
70 bool console_thread::configloaded = false;
71 wxString console_thread::working_dir = wxT(".");
76 * Call-back for reading a passphrase for an encrypted PEM file
77 * This function uses getpass(), which uses a static buffer and is NOT thread-safe.
79 static int tls_pem_callback(char *buf, int size, const void *userdata)
81 #if defined(HAVE_TLS) && !defined(HAVE_WIN32)
82 const char *prompt = (const char *) userdata;
85 passwd = getpass(prompt);
86 bstrncpy(buf, passwd, size);
96 * Make a quick check to see that we have all the
99 static int check_resources()
107 foreach_res(director, R_DIRECTOR) {
109 /* tls_require implies tls_enable */
110 if (director->tls_require) {
112 director->tls_enable = true;
114 Jmsg(NULL, M_FATAL, 0, wxString(_("TLS required but not configured in Bacula.\n")).mb_str(*wxConvCurrent));
120 if ((!director->tls_ca_certfile && !director->tls_ca_certdir) && director->tls_enable) {
121 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),
128 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 /* Loop over Consoles */
134 foreach_res(cons, R_CONSOLE) {
135 /* tls_require implies tls_enable */
136 if (cons->tls_require) {
138 cons->tls_enable = true;
140 Jmsg(NULL, M_FATAL, 0, wxString(_("TLS required but not configured in Bacula.\n")).mb_str(*wxConvCurrent));
146 if ((!cons->tls_ca_certfile && !cons->tls_ca_certdir) && cons->tls_enable) {
147 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),
157 void console_thread::SetWorkingDirectory(wxString w_dir) {
158 if (IsPathSeparator(w_dir.Last())) {
159 console_thread::working_dir = w_dir.Mid(0, w_dir.Length()-1);
162 console_thread::working_dir = w_dir;
166 void console_thread::InitLib()
168 if (WSA_Init() != 0) {
169 csprint(_("Error while initializing windows sockets...\n"));
175 my_name_is(0, NULL, "bwx-console");
176 working_directory = (const char*) console_thread::working_dir.GetData();
181 void console_thread::FreeLib()
184 if (WSACleanup() != 0) {
185 csprint(_("Error while cleaning up windows sockets...\n"));
193 * Format a scanner error message
195 static void scan_err(const char *file, int line, LEX *lc, const char *msg, ...)
199 char more[MAXSTRING];
202 va_start(arg_ptr, msg);
203 bvsnprintf(buf, sizeof(buf), msg, arg_ptr);
206 if (lc->line_no > lc->begin_line_no) {
207 bsnprintf(more, sizeof(more),
208 wxString(_("Problem probably begins at line %d.\n")).mb_str(*wxConvCurrent), lc->begin_line_no);
213 err.Format(_("Config error: %s\n : line %d, col %d of file %s\n%s\n%s"),
214 buf, lc->line_no, lc->col_no, lc->fname, lc->line, more);
219 wxString console_thread::LoadConfig(wxString configfile)
224 return _("Error while initializing library.");
227 free_config_resources();
229 MSGS* msgs = (MSGS *)bmalloc(sizeof(MSGS));
230 memset(msgs, 0, sizeof(MSGS));
231 for (int i=1; i<=M_MAX; i++) {
232 add_msg_dest(msgs, MD_STDOUT, i, NULL, NULL);
233 // add_msg_dest(msgs, MD_SYSLOG, i, NULL, NULL);
234 add_msg_dest(msgs, MD_CONSOLE, i, NULL, NULL);
237 init_msg(NULL, msgs);
238 //init_console_msg(console_thread::working_dir.mb_str(*wxConvCurrent));
241 if (!parse_config(configfile.mb_str(*wxConvCurrent), &scan_err)) {
242 configloaded = false;
247 if (init_crypto() != 0) {
248 Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("Cryptographic library initialization failed.\n")).mb_str(*wxConvCurrent));
251 if (!check_resources()) {
252 Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("Please correct configuration file.\n")).mb_str(*wxConvCurrent));
256 wxRemoveFile(console_thread::working_dir + wxT("/bwx-console.conmsg"));
257 init_msg(NULL, NULL);
265 console_thread::console_thread() {
267 choosingdirector = false;
271 console_thread::~console_thread() {
273 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
282 void* console_thread::Entry() {
284 /* It seems we must redefine the locale on each thread on wxGTK.
285 * On Win32 it makes bwx-console crash. */
288 m_locale.AddCatalog(wxT("bacula"));
289 wxLocale::AddCatalogLookupPathPrefix(wxT(LOCALEDIR));
294 csprint(_("Error : Library not initialized\n"));
295 csprint(NULL, CS_END);
296 csprint(NULL, CS_DISCONNECTED);
297 csprint(NULL, CS_TERMINATED);
305 csprint(_("Error : No configuration file loaded\n"));
306 csprint(NULL, CS_END);
307 csprint(NULL, CS_DISCONNECTED);
308 csprint(NULL, CS_TERMINATED);
315 csprint(_("Connecting...\n"));
318 DIRRES* res[16]; /* Maximum 16 directors */
321 foreach_res(dir, R_DIRECTOR) {
331 csprint(_("Error : No director defined in config file.\n"));
332 csprint(NULL, CS_END);
333 csprint(NULL, CS_DISCONNECTED);
334 csprint(NULL, CS_TERMINATED);
339 } else if (count == 1) {
343 csprint(_("Multiple directors found in your config file.\n"));
344 for (int i = 0; i < count; i++) {
346 csprint(wxString(wxT(" ")) << (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
349 csprint(wxString(wxT(" ")) << (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
352 csprint(wxString::Format(_("Please choose a director (1-%d): "), count), CS_DATA);
353 csprint(NULL, CS_PROMPT);
354 choosingdirector = true;
355 directorchoosen = -1;
356 while(directorchoosen == -1) {
357 bmicrosleep(0, 2000);
360 choosingdirector = false;
361 if (directorchoosen != 0) {
366 dir = res[directorchoosen-1];
368 memset(&jcr, 0, sizeof(jcr));
370 jcr.dequeuing = 1; /* TODO: catch messages */
373 /* If cons==NULL, default console will be used */
374 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
378 /* Initialize Console TLS context */
379 if (cons && (cons->tls_enable || cons->tls_require)) {
380 /* Generate passphrase prompt */
381 bsnprintf(buf, sizeof(buf), wxString(_("Passphrase for Console \"%s\" TLS private key: ")).mb_str(*wxConvCurrent), cons->hdr.name);
383 /* Initialize TLS context:
384 * Args: CA certfile, CA certdir, Certfile, Keyfile,
385 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
386 cons->tls_ctx = new_tls_context(cons->tls_ca_certfile,
387 cons->tls_ca_certdir, cons->tls_certfile,
388 cons->tls_keyfile, tls_pem_callback, &buf, NULL, true);
390 if (!cons->tls_ctx) {
391 bsnprintf(buf, sizeof(buf), wxString(_("Failed to initialize TLS context for Console \"%s\".\n")).mb_str(*wxConvCurrent),
399 /* Initialize Director TLS context */
400 if (dir->tls_enable || dir->tls_require) {
401 /* Generate passphrase prompt */
402 bsnprintf(buf, sizeof(buf), wxString(_("Passphrase for Director \"%s\" TLS private key: ")).mb_str(*wxConvCurrent), dir->hdr.name);
404 /* Initialize TLS context:
405 * Args: CA certfile, CA certdir, Certfile, Keyfile,
406 * Keyfile PEM Callback, Keyfile CB Userdata, DHfile, Verify Peer */
407 dir->tls_ctx = new_tls_context(dir->tls_ca_certfile,
408 dir->tls_ca_certdir, dir->tls_certfile,
409 dir->tls_keyfile, tls_pem_callback, &buf, NULL, true);
412 bsnprintf(buf, sizeof(buf), wxString(_("Failed to initialize TLS context for Director \"%s\".\n")).mb_str(*wxConvCurrent),
420 UA_sock = bnet_connect(&jcr, 3, 3, 0, wxString(_("Director daemon")).mb_str(*wxConvCurrent),
421 dir->address, NULL, dir->DIRport, 0);
423 if (UA_sock == NULL) {
424 csprint(_("Failed to connect to the director\n"));
425 csprint(NULL, CS_END);
426 csprint(NULL, CS_DISCONNECTED);
427 csprint(NULL, CS_TERMINATED);
434 csprint(_("Connected\n"));
436 jcr.dir_bsock = UA_sock;
437 if (!authenticate_director(&jcr, dir, cons)) {
439 csprint(UA_sock->msg);
440 csprint(NULL, CS_END);
441 csprint(NULL, CS_DISCONNECTED);
442 csprint(NULL, CS_TERMINATED);
449 csprint(NULL, CS_CONNECTED);
451 Write("autodisplay on\n");
452 Write(".messages\n");
456 int last_is_eod = 0; /* Last packet received is BNET_EOD */
457 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) */
460 while(!TestDestroy()) { /* Tests if thread has been ended */
461 stat = bnet_wait_data(UA_sock, 10);
464 Write(".messages\n");
465 do_not_forward_eod = 1;
471 if ((stat = bnet_recv(UA_sock)) >= 0) {
472 if (do_not_forward_eod) { /* .messages got data: remove the prompt */
473 csprint(NULL, CS_REMOVEPROMPT);
475 csprint(UA_sock->msg);
477 else if (stat == BNET_SIGNAL) {
478 if (UA_sock->msglen == BNET_PROMPT) {
479 csprint(NULL, CS_PROMPT);
480 } else if (UA_sock->msglen == BNET_EOD) {
482 if (!do_not_forward_eod)
483 csprint(NULL, CS_END);
484 } else if (UA_sock->msglen == BNET_HEARTBEAT) {
485 bnet_sig(UA_sock, BNET_HB_RESPONSE);
486 csprint(_("<< Heartbeat signal received, answered. >>\n"), CS_DEBUG);
487 } else if (UA_sock->msglen == BNET_START_SELECT ||
488 UA_sock->msglen == BNET_END_SELECT) {
489 /* Ignore start/end selections for now */
491 csprint(_("<< Unexpected signal received : "), CS_DEBUG);
492 csprint(bnet_sig_to_ascii(UA_sock), CS_DEBUG);
493 csprint(">>\n", CS_DEBUG);
496 else { /* BNET_HARDEOF || BNET_ERROR */
497 csprint(NULL, CS_END);
501 if (is_bnet_stop(UA_sock)) {
502 csprint(NULL, CS_END);
503 break; /* error or term */
506 do_not_forward_eod = 0;
509 csprint(NULL, CS_DISCONNECTED);
511 csprint(_("Connection terminated\n"));
515 csprint(NULL, CS_TERMINATED);
524 void console_thread::Write(const char* str)
527 UA_sock->msglen = (int32_t)strlen(str);
528 pm_strcpy(&UA_sock->msg, str);
530 } else if (choosingdirector) {
531 // wxString number = str;
532 // number.RemoveLast(); /* Removes \n */
535 // if (number.ToLong(&val)) {
538 directorchoosen = (int)val;
545 void console_thread::Delete() {
548 bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
551 /*csprint(NULL, CS_END);
552 csprint(NULL, CS_DISCONNECTED);
553 csprint(NULL, CS_TERMINATED);*/