2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2008 Free Software Foundation Europe e.V.
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
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.
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
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.
30 * Interaction thread between director and the GUI
32 * Nicolas Boichat, April 2004
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);
68 bool parse_wxcons_config(CONFIG *config, const char *cfgfile, LEX_ERROR_HANDLER *scan_error);
70 bool console_thread::inited = false;
71 bool console_thread::configloaded = false;
72 wxString console_thread::working_dir = wxT(".");
75 static CONFIG *config = NULL;
76 static void scan_err(const char *file, int line, LEX *lc, const char *msg, ...);
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.
83 static int tls_pem_callback(char *buf, int size, const void *userdata)
85 #if defined(HAVE_TLS) && !defined(HAVE_WIN32)
86 const char *prompt = (const char *) userdata;
89 passwd = getpass(prompt);
90 bstrncpy(buf, passwd, size);
100 * Make a quick check to see that we have all the
103 static int check_resources()
111 foreach_res(director, R_DIRECTOR) {
113 /* tls_require implies tls_enable */
114 if (director->tls_require) {
116 director->tls_enable = true;
118 Jmsg(NULL, M_FATAL, 0, wxString(_("TLS required but not configured in Bacula.\n")).mb_str(*wxConvCurrent));
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),
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));
137 /* Loop over Consoles */
138 foreach_res(cons, R_CONSOLE) {
139 /* tls_require implies tls_enable */
140 if (cons->tls_require) {
142 cons->tls_enable = true;
144 Jmsg(NULL, M_FATAL, 0, wxString(_("TLS required but not configured in Bacula.\n")).mb_str(*wxConvCurrent));
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),
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);
166 console_thread::working_dir = w_dir;
170 void console_thread::InitLib()
172 if (WSA_Init() != 0) {
173 csprint(_("Error while initializing windows sockets...\n"));
179 my_name_is(0, NULL, "bwx-console");
180 working_directory = (const char*) console_thread::working_dir.GetData();
185 void console_thread::FreeLib()
188 if (WSACleanup() != 0) {
189 csprint(_("Error while cleaning up windows sockets...\n"));
197 * Format a scanner error message
199 static void scan_err(const char *file, int line, LEX *lc, const char *msg, ...)
203 char more[MAXSTRING];
206 va_start(arg_ptr, msg);
207 bvsnprintf(buf, sizeof(buf), msg, arg_ptr);
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);
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);
223 wxString console_thread::LoadConfig(wxString configfile)
228 return _("Error while initializing library.");
232 config->free_resources();
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);
244 init_msg(NULL, msgs);
245 //init_console_msg(console_thread::working_dir.mb_str(*wxConvCurrent));
248 config = new_config_parser();
249 if (!parse_wxcons_config(config, configfile.mb_str(*wxConvCurrent), &scan_err)) {
250 configloaded = false;
255 if (init_crypto() != 0) {
256 Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("Cryptographic library initialization failed.\n")).mb_str(*wxConvCurrent));
259 if (!check_resources()) {
260 Jmsg(NULL, M_ERROR_TERM, 0, wxString(_("Please correct configuration file.\n")).mb_str(*wxConvCurrent));
264 wxRemoveFile(console_thread::working_dir + wxT("/bwx-console.conmsg"));
265 init_msg(NULL, NULL);
273 console_thread::console_thread() {
275 choosingdirector = false;
279 console_thread::~console_thread() {
281 UA_sock->signal(BNET_TERMINATE); /* send EOF */
290 void* console_thread::Entry() {
292 /* It seems we must redefine the locale on each thread on wxGTK.
293 * On Win32 it makes bwx-console crash. */
296 m_locale.AddCatalog(wxT("bacula"));
297 wxLocale::AddCatalogLookupPathPrefix(wxT(LOCALEDIR));
302 csprint(_("Error : Library not initialized\n"));
303 csprint(NULL, CS_END);
304 csprint(NULL, CS_DISCONNECTED);
305 csprint(NULL, CS_TERMINATED);
313 csprint(_("Error : No configuration file loaded\n"));
314 csprint(NULL, CS_END);
315 csprint(NULL, CS_DISCONNECTED);
316 csprint(NULL, CS_TERMINATED);
323 csprint(_("Connecting...\n"));
326 DIRRES* res[16]; /* Maximum 16 directors */
329 foreach_res(dir, R_DIRECTOR) {
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);
347 } else if (count == 1) {
351 csprint(_("Multiple directors found in your config file.\n"));
352 for (int i = 0; i < count; i++) {
354 csprint(wxString(wxT(" ")) << (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
357 csprint(wxString(wxT(" ")) << (i+1) << wxT(": ") << wxString(res[i]->hdr.name,*wxConvCurrent) << wxT("\n"));
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);
368 choosingdirector = false;
369 if (directorchoosen != 0) {
374 dir = res[directorchoosen-1];
376 memset(&jcr, 0, sizeof(jcr));
378 jcr.dequeuing_msgs = 1; /* TODO: catch messages */
381 /* If cons==NULL, default console will be used */
382 CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
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);
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);
398 if (!cons->tls_ctx) {
399 bsnprintf(buf, sizeof(buf), wxString(_("Failed to initialize TLS context for Console \"%s\".\n")).mb_str(*wxConvCurrent),
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);
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);
420 bsnprintf(buf, sizeof(buf), wxString(_("Failed to initialize TLS context for Director \"%s\".\n")).mb_str(*wxConvCurrent),
428 UA_sock = bnet_connect(&jcr, 3, 3, 0, wxString(_("Director daemon")).mb_str(*wxConvCurrent),
429 dir->address, NULL, dir->DIRport, 0);
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);
442 csprint(_("Connected\n"));
444 jcr.dir_bsock = UA_sock;
445 if (!authenticate_director(&jcr, dir, cons)) {
447 csprint(UA_sock->msg);
448 csprint(NULL, CS_END);
449 csprint(NULL, CS_DISCONNECTED);
450 csprint(NULL, CS_TERMINATED);
457 csprint(NULL, CS_CONNECTED);
459 Write("autodisplay on\n");
460 Write(".messages\n");
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) */
468 while(!TestDestroy()) { /* Tests if thread has been ended */
469 stat = UA_sock->wait_data(10);
472 Write(".messages\n");
473 do_not_forward_eod = 1;
479 if ((stat = UA_sock->recv()) >= 0) {
480 if (do_not_forward_eod) { /* .messages got data: remove the prompt */
481 csprint(NULL, CS_REMOVEPROMPT);
483 csprint(UA_sock->msg);
485 else if (stat == BNET_SIGNAL) {
486 if (UA_sock->msglen == BNET_SUB_PROMPT) {
487 csprint(NULL, CS_PROMPT);
488 } else if (UA_sock->msglen == BNET_EOD) {
490 if (!do_not_forward_eod)
491 csprint(NULL, CS_END);
492 } else if (UA_sock->msglen == BNET_HEARTBEAT) {
493 UA_sock->signal(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 */
499 csprint(_("<< Unexpected signal received : "), CS_DEBUG);
500 csprint(bnet_sig_to_ascii(UA_sock), CS_DEBUG);
501 csprint(">>\n", CS_DEBUG);
504 else { /* BNET_HARDEOF || BNET_ERROR */
505 csprint(NULL, CS_END);
509 if (UA_sock->is_stop()) {
510 csprint(NULL, CS_END);
511 break; /* error or term */
514 do_not_forward_eod = 0;
517 csprint(NULL, CS_DISCONNECTED);
519 csprint(_("Connection terminated\n"));
523 csprint(NULL, CS_TERMINATED);
532 void console_thread::Write(const char* str)
535 UA_sock->msglen = (int32_t)strlen(str);
536 pm_strcpy(&UA_sock->msg, str);
538 } else if (choosingdirector) {
539 // wxString number = str;
540 // number.RemoveLast(); /* Removes \n */
543 // if (number.ToLong(&val)) {
546 directorchoosen = (int)val;
553 void console_thread::Delete() {
556 UA_sock->signal(BNET_TERMINATE); /* send EOF */
559 /*csprint(NULL, CS_END);
560 csprint(NULL, CS_DISCONNECTED);
561 csprint(NULL, CS_TERMINATED);*/