1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2018 Bareos GmbH & Co. KG
7
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 Affero 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 /*
24 * Kern Sibbald, October MM
25 */
26 /**
27 * fd_cmds.c -- send commands to File daemon
28 *
29 * This routine is run as a separate thread. There may be more
30 * work to be done to make it totally reentrant!!!!
31 *
32 * Utility functions for sending info to File Daemon.
33 * These functions are used by both backup and verify.
34 */
35
36 #include <algorithm>
37 #include "include/bareos.h"
38 #include "dird.h"
39 #include "dird/dird_globals.h"
40 #include "findlib/find.h"
41 #include "dird/authenticate.h"
42 #include "dird/fd_cmds.h"
43 #include "dird/getmsg.h"
44 #include "dird/msgchan.h"
45 #include "lib/bnet.h"
46 #include "lib/edit.h"
47
48
49 namespace directordaemon {
50
51 const int debuglevel = 400;
52
53 /* Commands sent to File daemon */
54 static char filesetcmd[] =
55 "fileset%s\n"; /* set full fileset */
56 static char jobcmd[] =
57 "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
58 static char jobcmdssl[] =
59 "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s ssl=%d\n";
60 /* Note, mtime_only is not used here -- implemented as file option */
61 static char levelcmd[] =
62 "level = %s%s%s mtime_only=%d %s%s\n";
63 static char runscriptcmd[] =
64 "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
65 static char runbeforenowcmd[] =
66 "RunBeforeNow\n";
67 static char restoreobjectendcmd[] =
68 "restoreobject end\n";
69 static char bandwidthcmd[] =
70 "setbandwidth=%lld Job=%s\n";
71 static char pluginoptionscmd[] =
72 "pluginoptions %s\n";
73 static char getSecureEraseCmd[] =
74 "getSecureEraseCmd\n";
75
76 /* Responses received from File daemon */
77 static char OKinc[] =
78 "2000 OK include\n";
79 static char OKjob[] =
80 "2000 OK Job";
81 static char OKlevel[] =
82 "2000 OK level\n";
83 static char OKRunScript[] =
84 "2000 OK RunScript\n";
85 static char OKRunBeforeNow[] =
86 "2000 OK RunBeforeNow\n";
87 static char OKRestoreObject[] =
88 "2000 OK ObjectRestored\n";
89 static char OKBandwidth[] =
90 "2000 OK Bandwidth\n";
91 static char OKPluginOptions[] =
92 "2000 OK PluginOptions\n";
93 static char OKgetSecureEraseCmd[] =
94 "2000 OK FDSecureEraseCmd %s\n";
95
96 /* Forward referenced functions */
97 static bool SendListItem(JobControlRecord *jcr, const char *code, char *item, BareosSocket *fd);
98
99 /* External functions */
100 extern DirectorResource *director;
101
102 #define INC_LIST 0
103 #define EXC_LIST 1
104
get_heartbeat_interval(ClientResource * res)105 static utime_t get_heartbeat_interval(ClientResource *res)
106 {
107 utime_t heartbeat;
108
109 if (res->heartbeat_interval) {
110 heartbeat = res->heartbeat_interval;
111 } else {
112 heartbeat = me->heartbeat_interval;
113 }
114
115 return heartbeat;
116 }
117
118 /**
119 * Open connection to File daemon.
120 *
121 * Try connecting every retry_interval (default 10 sec), and
122 * give up after max_retry_time (default 30 mins).
123 */
connect_outbound_to_file_daemon(JobControlRecord * jcr,int retry_interval,int max_retry_time,bool verbose)124 static bool connect_outbound_to_file_daemon(JobControlRecord *jcr, int retry_interval,
125 int max_retry_time, bool verbose)
126 {
127 bool result = false;
128 BareosSocket *fd = NULL;
129 utime_t heart_beat;
130
131 if (!IsConnectingToClientAllowed(jcr)) {
132 Dmsg1(120, "connecting to client \"%s\" is not allowed.\n", jcr->res.client->name());
133 return false;
134 }
135
136 fd = New(BareosSocketTCP);
137 if (me->nokeepalive) {
138 fd->ClearKeepalive();
139 }
140 heart_beat = get_heartbeat_interval(jcr->res.client);
141
142 char name[MAX_NAME_LENGTH + 100];
143 bstrncpy(name, _("Client: "), sizeof(name));
144 bstrncat(name, jcr->res.client->name(), sizeof(name));
145
146 fd->SetSourceAddress(me->DIRsrc_addr);
147 if (!fd->connect(jcr,retry_interval,max_retry_time,
148 heart_beat, name,
149 jcr->res.client->address, NULL,
150 jcr->res.client->FDport, verbose)) {
151 delete fd;
152 fd = NULL;
153 jcr->setJobStatus(JS_ErrorTerminated);
154 } else {
155 jcr->file_bsock = fd;
156 jcr->authenticated = false;
157 Dmsg0(10, "Opened connection with File daemon\n");
158 result = true;
159 }
160
161 return result;
162 }
163
OutputMessageForConnectionTry(JobControlRecord * jcr,UaContext * ua)164 static void OutputMessageForConnectionTry(JobControlRecord *jcr, UaContext *ua)
165 {
166 std::string m;
167
168 if (jcr->res.client->connection_successful_handshake_ == ClientConnectionHandshakeMode::kUndefined
169 || jcr->res.client->connection_successful_handshake_ == ClientConnectionHandshakeMode::kFailed) {
170 m = "Probing client protocol... (result will be saved until config reload)";
171 } else {
172 return;
173 }
174
175 if (jcr && jcr->JobId != 0) {
176 std::string m1 = m + "\n";
177 Jmsg(jcr, M_INFO, 0, m1.c_str());
178 }
179 if (ua) {
180 ua->SendMsg(m.c_str());
181 }
182 }
183
SendInfoChosenCipher(JobControlRecord * jcr,UaContext * ua)184 static void SendInfoChosenCipher(JobControlRecord *jcr, UaContext *ua)
185 {
186 std::string str;
187 jcr->file_bsock->GetCipherMessageString(str);
188 str += '\n';
189 if (jcr->JobId != 0) {
190 Jmsg(jcr, M_INFO, 0, str.c_str());
191 }
192 if (ua) { /* only whith console connection */
193 ua->SendRawMsg(str.c_str());
194 }
195 }
196
SendInfoSuccess(JobControlRecord * jcr,UaContext * ua)197 static void SendInfoSuccess(JobControlRecord *jcr, UaContext *ua)
198 {
199 std::string m;
200 if (jcr->res.client->connection_successful_handshake_ == ClientConnectionHandshakeMode::kUndefined) {
201 m += "\r\v";
202 }
203 bool add_newline_in_joblog = false;
204 switch (jcr->connection_handshake_try_) {
205 case ClientConnectionHandshakeMode::kTlsFirst:
206 m += " Handshake: Immediate TLS,";
207 break;
208 case ClientConnectionHandshakeMode::kCleartextFirst:
209 m += " Handshake: Cleartext,";
210 add_newline_in_joblog = true;
211 break;
212 default:
213 m += " unknown mode\n";
214 break;
215 }
216
217 if (jcr && jcr->JobId != 0) {
218 std::string m1 = m;
219 std::replace(m1.begin(), m1.end(), '\r', ' ');
220 std::replace(m1.begin(), m1.end(), '\v', ' ');
221 std::replace(m1.begin(), m1.end(), ',' , ' ');
222 if (add_newline_in_joblog) {
223 m1 += std::string("\n");
224 }
225 Jmsg(jcr, M_INFO, 0, m1.c_str());
226 }
227 if (ua) { /* only whith console connection */
228 ua->SendRawMsg(m.c_str());
229 }
230 }
231
ConnectToFileDaemon(JobControlRecord * jcr,int retry_interval,int max_retry_time,bool verbose,UaContext * ua)232 bool ConnectToFileDaemon(JobControlRecord *jcr, int retry_interval, int max_retry_time, bool verbose,
233 UaContext *ua)
234 {
235 bool success = false;
236 bool tcp_connect_failed = false;
237 int connect_tries = 3; /* as a finish-hook for the UseWaitingClient mechanism */
238
239 /* try the connection modes starting with tls directly,
240 * in case there is a client that cannot do Tls immediately then
241 * fall back to cleartext md5-handshake */
242 OutputMessageForConnectionTry(jcr, ua);
243 if (jcr->res.client->connection_successful_handshake_ == ClientConnectionHandshakeMode::kUndefined
244 || jcr->res.client->connection_successful_handshake_ == ClientConnectionHandshakeMode::kFailed) {
245 if (jcr->res.client->IsTlsConfigured()) {
246 jcr->connection_handshake_try_ = ClientConnectionHandshakeMode::kTlsFirst;
247 } else {
248 jcr->connection_handshake_try_ = ClientConnectionHandshakeMode::kCleartextFirst;
249 }
250 jcr->is_passive_client_connection_probing = true;
251 } else {
252 /* if there is a stored mode from a previous connection then use this */
253 jcr->connection_handshake_try_ = jcr->res.client->connection_successful_handshake_;
254 jcr->is_passive_client_connection_probing = false;
255 }
256
257 do { /* while (tcp_connect_failed ...) */
258 /* connect the tcp socket */
259 if (!jcr->file_bsock) {
260 if (!UseWaitingClient(jcr, 0)) {
261 if (!connect_outbound_to_file_daemon(jcr, retry_interval, max_retry_time, verbose)) {
262 if (!UseWaitingClient(jcr, max_retry_time)) { /* will set jcr->file_bsock accordingly */
263 tcp_connect_failed = true;
264 }
265 }
266 }
267 }
268
269 if (jcr->file_bsock) {
270 jcr->setJobStatus(JS_Running);
271 if (AuthenticateWithFileDaemon(jcr)) {
272 success = true;
273 SendInfoSuccess(jcr, ua);
274 SendInfoChosenCipher(jcr, ua);
275 jcr->is_passive_client_connection_probing = false;
276 jcr->res.client->connection_successful_handshake_ = jcr->connection_handshake_try_;
277 } else {
278 /* authentication failed due to
279 * - tls mismatch or
280 * - if an old client cannot do tls- before md5-handshake
281 * */
282 switch(jcr->connection_handshake_try_) {
283 case ClientConnectionHandshakeMode::kTlsFirst:
284 if (jcr->file_bsock) {
285 jcr->file_bsock->close();
286 delete jcr->file_bsock;
287 jcr->file_bsock = nullptr;
288 }
289 jcr->resetJobStatus(JS_Running);
290 jcr->connection_handshake_try_ = ClientConnectionHandshakeMode::kCleartextFirst;
291 break;
292 case ClientConnectionHandshakeMode::kCleartextFirst:
293 jcr->connection_handshake_try_ = ClientConnectionHandshakeMode::kFailed;
294 break;
295 case ClientConnectionHandshakeMode::kFailed:
296 default: /* should bei one of class ClientConnectionHandshakeMode */
297 ASSERT(false);
298 break;
299 }
300 }
301 } else {
302 Jmsg(jcr, M_FATAL, 0, "\nFailed to connect to client \"%s\".\n", jcr->res.client->name());
303 }
304 connect_tries--;
305 } while (!tcp_connect_failed && connect_tries
306 && !success
307 && jcr->connection_handshake_try_ != ClientConnectionHandshakeMode::kFailed);
308
309 if (!success) {
310 jcr->setJobStatus(JS_ErrorTerminated);
311 }
312
313 return success;
314 }
315
SendJobInfoToFileDaemon(JobControlRecord * jcr)316 int SendJobInfoToFileDaemon(JobControlRecord *jcr)
317 {
318 BareosSocket *fd = jcr->file_bsock;
319
320 if (jcr->sd_auth_key == NULL) {
321 jcr->sd_auth_key = bstrdup("dummy");
322 }
323
324 if (jcr->res.client->connection_successful_handshake_ == ClientConnectionHandshakeMode::kTlsFirst) {
325 /* client protocol onwards Bareos 18.2 */
326 TlsPolicy tls_policy = kBnetTlsUnknown;
327 int JobLevel = jcr->getJobLevel();
328 switch (JobLevel) {
329 case L_VERIFY_INIT:
330 case L_VERIFY_CATALOG:
331 case L_VERIFY_DISK_TO_CATALOG:
332 tls_policy = kBnetTlsUnknown;
333 break;
334 default:
335 StorageResource *storage = nullptr;
336 if (jcr->res.write_storage) {
337 storage = jcr->res.write_storage;
338 } else if (jcr->res.read_storage) {
339 storage = jcr->res.read_storage;
340 } else {
341 Jmsg(jcr, M_FATAL, 0, _("No read or write storage defined\n"));
342 jcr->setJobStatus(JS_ErrorTerminated);
343 return 0;
344 }
345 if (storage) {
346 tls_policy = storage->IsTlsConfigured() ? TlsPolicy::kBnetTlsAuto : TlsPolicy::kBnetTlsNone;
347 }
348 break;
349 }
350 char ed1[30];
351 fd->fsend(jobcmdssl, edit_int64(jcr->JobId, ed1), jcr->Job, jcr->VolSessionId,
352 jcr->VolSessionTime, jcr->sd_auth_key, tls_policy);
353 } else { /* jcr->res.client->connection_successful_handshake_ != ClientConnectionHandshakeMode::kTlsFirst */
354 /* client protocol before Bareos 18.2 */
355 char ed1[30];
356 fd->fsend(jobcmd, edit_int64(jcr->JobId, ed1), jcr->Job, jcr->VolSessionId,
357 jcr->VolSessionTime, jcr->sd_auth_key);
358 } /* if (jcr->res.client->connection_successful_handshake_ == ClientConnectionHandshakeMode::kTlsFirst) */
359
360 if (!jcr->keep_sd_auth_key && !bstrcmp(jcr->sd_auth_key, "dummy")) {
361 memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
362 }
363
364 Dmsg1(100, ">filed: %s", fd->msg);
365 if (BgetDirmsg(fd) > 0) {
366 Dmsg1(110, "<filed: %s", fd->msg);
367 if (!bstrncmp(fd->msg, OKjob, strlen(OKjob))) {
368 Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"),
369 jcr->res.client->hdr.name, fd->msg);
370 jcr->setJobStatus(JS_ErrorTerminated);
371 return 0;
372 } else if (jcr->db) {
373 ClientDbRecord cr;
374
375 memset(&cr, 0, sizeof(cr));
376 bstrncpy(cr.Name, jcr->res.client->hdr.name, sizeof(cr.Name));
377 cr.AutoPrune = jcr->res.client->AutoPrune;
378 cr.FileRetention = jcr->res.client->FileRetention;
379 cr.JobRetention = jcr->res.client->JobRetention;
380 bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
381 if (!jcr->db->UpdateClientRecord(jcr, &cr)) {
382 Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"), jcr->db->strerror());
383 }
384 }
385 } else {
386 Jmsg(jcr, M_FATAL, 0, _("FD gave bad response to JobId command: %s\n"), BnetStrerror(fd));
387 jcr->setJobStatus(JS_ErrorTerminated);
388 return 0;
389 }
390
391 return 1;
392 }
393
SendPreviousRestoreObjects(JobControlRecord * jcr)394 bool SendPreviousRestoreObjects(JobControlRecord *jcr)
395 {
396 int JobLevel;
397
398 JobLevel = jcr->getJobLevel();
399 switch (JobLevel) {
400 case L_DIFFERENTIAL:
401 case L_INCREMENTAL:
402 if (jcr->previous_jr.JobId > 0) {
403 if (!SendRestoreObjects(jcr, jcr->previous_jr.JobId, false)) {
404 return false;
405 }
406 }
407 break;
408 default:
409 break;
410 }
411
412 return true;
413 }
414
SendBwlimitToFd(JobControlRecord * jcr,const char * Job)415 bool SendBwlimitToFd(JobControlRecord *jcr, const char *Job)
416 {
417 BareosSocket *fd = jcr->file_bsock;
418
419 if (jcr->FDVersion >= FD_VERSION_4) {
420 fd->fsend(bandwidthcmd, jcr->max_bandwidth, Job);
421 if (!response(jcr, fd, OKBandwidth, "Bandwidth", DISPLAY_ERROR)) {
422 jcr->max_bandwidth = 0; /* can't set bandwidth limit */
423 return false;
424 }
425 }
426
427 return true;
428 }
429
SendSecureEraseReqToFd(JobControlRecord * jcr)430 bool SendSecureEraseReqToFd(JobControlRecord *jcr)
431 {
432 int32_t n;
433 BareosSocket *fd = jcr->file_bsock;
434
435 if (!jcr->FDSecureEraseCmd) {
436 jcr->FDSecureEraseCmd = GetPoolMemory(PM_NAME);
437 }
438
439 if (jcr->FDVersion > FD_VERSION_53) {
440 fd->fsend(getSecureEraseCmd);
441 while ((n = BgetDirmsg(fd)) >= 0) {
442 jcr->FDSecureEraseCmd = CheckPoolMemorySize(jcr->FDSecureEraseCmd, fd->message_length);
443 if (sscanf(fd->msg, OKgetSecureEraseCmd, jcr->FDSecureEraseCmd) == 1) {
444 Dmsg1(400, "Got FD Secure Erase Cmd: %s\n", jcr->FDSecureEraseCmd);
445 break;
446 } else {
447 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Secure Erase Cmd: %s\n"), fd->msg);
448 PmStrcpy(jcr->FDSecureEraseCmd, "*None*");
449 return false;
450 }
451 }
452 } else {
453 PmStrcpy(jcr->FDSecureEraseCmd, "*None*");
454 }
455
456 return true;
457 }
458
SendSinceTime(JobControlRecord * jcr)459 static void SendSinceTime(JobControlRecord *jcr)
460 {
461 char ed1[50];
462 utime_t stime;
463 BareosSocket *fd = jcr->file_bsock;
464
465 stime = StrToUtime(jcr->stime);
466 fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0,
467 NT_("prev_job="), jcr->PrevJob);
468
469 while (BgetDirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */
470 Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
471 }
472 }
473
474 /**
475 * Send level command to FD.
476 * Used for backup jobs and estimate command.
477 */
SendLevelCommand(JobControlRecord * jcr)478 bool SendLevelCommand(JobControlRecord *jcr)
479 {
480 int JobLevel;
481 BareosSocket *fd = jcr->file_bsock;
482 const char *accurate = jcr->accurate ? "accurate_" : "";
483 const char *not_accurate = "";
484 const char *rerunning = jcr->rerunning ? " rerunning " : " ";
485
486 /*
487 * Send Level command to File daemon
488 */
489 JobLevel = jcr->getJobLevel();
490 switch (JobLevel) {
491 case L_BASE:
492 fd->fsend(levelcmd, not_accurate, "base", rerunning, 0, "", "");
493 break;
494 case L_NONE: /* L_NONE is the console, sending something off to the FD */
495 case L_FULL:
496 fd->fsend(levelcmd, not_accurate, "full", rerunning, 0, "", "");
497 break;
498 case L_DIFFERENTIAL:
499 fd->fsend(levelcmd, accurate, "differential", rerunning, 0, "", "");
500 SendSinceTime(jcr);
501 break;
502 case L_INCREMENTAL:
503 fd->fsend(levelcmd, accurate, "incremental", rerunning, 0, "", "");
504 SendSinceTime(jcr);
505 break;
506 case L_SINCE:
507 Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"), JobLevel, JobLevel);
508 break;
509 default:
510 Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"), JobLevel, JobLevel);
511 return 0;
512 }
513
514 Dmsg1(120, ">filed: %s", fd->msg);
515
516 if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
517 return false;
518 }
519
520 return true;
521 }
522
523 /**
524 * Send either an Included or an Excluded list to FD
525 */
SendFileset(JobControlRecord * jcr)526 static bool SendFileset(JobControlRecord *jcr)
527 {
528 FilesetResource *fileset = jcr->res.fileset;
529 BareosSocket *fd = jcr->file_bsock;
530 StorageResource *store = jcr->res.write_storage;
531 int num;
532 bool include = true;
533
534 while (1) {
535 if (include) {
536 num = fileset->num_includes;
537 } else {
538 num = fileset->num_excludes;
539 }
540 for (int i = 0; i < num; i++) {
541 char *item;
542 IncludeExcludeItem *ie;
543
544 if (include) {
545 ie = fileset->include_items[i];
546 fd->fsend("I\n");
547 } else {
548 ie = fileset->exclude_items[i];
549 fd->fsend("E\n");
550 }
551
552 for (int j = 0; j < ie->ignoredir.size(); j++) {
553 fd->fsend("Z %s\n", ie->ignoredir.get(j));
554 }
555
556 for (int j = 0; j < ie->num_opts; j++) {
557 FileOptions *fo = ie->opts_list[j];
558 bool enhanced_wild = false;
559
560 for (int k = 0; fo->opts[k] != '\0'; k++) {
561 if (fo->opts[k] == 'W') {
562 enhanced_wild = true;
563 break;
564 }
565 }
566
567 /*
568 * Strip out compression option Zn if disallowed for this Storage
569 */
570 if (store && !store->AllowCompress) {
571 char newopts[MAX_FOPTS];
572 bool done = false; /* print warning only if compression enabled in FS */
573 int l = 0;
574
575 for (int k = 0; fo->opts[k] != '\0'; k++) {
576 /*
577 * Z compress option is followed by the single-digit compress level or 'o'
578 * For fastlz its Zf with a single char selecting the actual compression algo.
579 */
580 if (fo->opts[k] == 'Z' && fo->opts[k + 1] == 'f') {
581 done = true;
582 k += 2; /* skip option */
583 } else if (fo->opts[k] == 'Z') {
584 done = true;
585 k++; /* skip option and level */
586 } else {
587 newopts[l] = fo->opts[k];
588 l++;
589 }
590 }
591 newopts[l] = '\0';
592
593 if (done) {
594 Jmsg(jcr, M_INFO, 0,
595 _("FD compression disabled for this Job because AllowCompress=No in Storage resource.\n") );
596 }
597
598 /*
599 * Send the new trimmed option set without overwriting fo->opts
600 */
601 fd->fsend("O %s\n", newopts);
602 } else {
603 /*
604 * Send the original options
605 */
606 fd->fsend("O %s\n", fo->opts);
607 }
608
609 for (int k = 0; k < fo->regex.size(); k++) {
610 fd->fsend("R %s\n", fo->regex.get(k));
611 }
612
613 for (int k = 0; k < fo->regexdir.size(); k++) {
614 fd->fsend("RD %s\n", fo->regexdir.get(k));
615 }
616
617 for (int k = 0; k < fo->regexfile.size(); k++) {
618 fd->fsend("RF %s\n", fo->regexfile.get(k));
619 }
620
621 for (int k = 0; k<fo->wild.size(); k++) {
622 fd->fsend("W %s\n", fo->wild.get(k));
623 }
624
625 for (int k = 0; k < fo->wilddir.size(); k++) {
626 fd->fsend("WD %s\n", fo->wilddir.get(k));
627 }
628
629 for (int k = 0; k < fo->wildfile.size(); k++) {
630 fd->fsend("WF %s\n", fo->wildfile.get(k));
631 }
632
633 for (int k = 0; k < fo->wildbase.size(); k++) {
634 fd->fsend("W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
635 }
636
637 for (int k = 0; k < fo->base.size(); k++) {
638 fd->fsend("B %s\n", fo->base.get(k));
639 }
640
641 for (int k = 0; k < fo->fstype.size(); k++) {
642 fd->fsend("X %s\n", fo->fstype.get(k));
643 }
644
645 for (int k = 0; k < fo->Drivetype.size(); k++) {
646 fd->fsend("XD %s\n", fo->Drivetype.get(k));
647 }
648
649 if (fo->plugin) {
650 fd->fsend("G %s\n", fo->plugin);
651 }
652
653 if (fo->reader) {
654 fd->fsend("D %s\n", fo->reader);
655 }
656
657 if (fo->writer) {
658 fd->fsend("T %s\n", fo->writer);
659 }
660
661 fd->fsend("N\n");
662 }
663
664 for (int j = 0; j < ie->name_list.size(); j++) {
665 item = (char *)ie->name_list.get(j);
666 if (!SendListItem(jcr, "F ", item, fd)) {
667 goto bail_out;
668 }
669 }
670 fd->fsend("N\n");
671
672 for (int j = 0; j < ie->plugin_list.size(); j++) {
673 item = (char *)ie->plugin_list.get(j);
674 if (!SendListItem(jcr, "P ", item, fd)) {
675 goto bail_out;
676 }
677 }
678 fd->fsend("N\n");
679 }
680
681 if (!include) { /* If we just did excludes */
682 break; /* all done */
683 }
684
685 include = false; /* Now do excludes */
686 }
687
688 fd->signal(BNET_EOD); /* end of data */
689 if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
690 goto bail_out;
691 }
692 return true;
693
694 bail_out:
695 jcr->setJobStatus(JS_ErrorTerminated);
696 return false;
697
698 }
699
SendListItem(JobControlRecord * jcr,const char * code,char * item,BareosSocket * fd)700 static bool SendListItem(JobControlRecord *jcr, const char *code, char *item, BareosSocket *fd)
701 {
702 Bpipe *bpipe;
703 FILE *ffd;
704 char buf[2000];
705 int optlen, status;
706 char *p = item;
707
708 switch (*p) {
709 case '|':
710 p++; /* skip over the | */
711 fd->msg = edit_job_codes(jcr, fd->msg, p, "");
712 bpipe = OpenBpipe(fd->msg, 0, "r");
713 if (!bpipe) {
714 BErrNo be;
715 Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
716 p, be.bstrerror());
717 return false;
718 }
719 bstrncpy(buf, code, sizeof(buf));
720 Dmsg1(500, "code=%s\n", buf);
721 optlen = strlen(buf);
722 while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
723 fd->message_length = Mmsg(fd->msg, "%s", buf);
724 Dmsg2(500, "Inc/exc len=%d: %s", fd->message_length, fd->msg);
725 if (!BnetSend(fd)) {
726 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
727 return false;
728 }
729 }
730 if ((status = CloseBpipe(bpipe)) != 0) {
731 BErrNo be;
732 Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
733 p, be.bstrerror(status));
734 return false;
735 }
736 break;
737 case '<':
738 p++; /* skip over < */
739 if ((ffd = fopen(p, "rb")) == NULL) {
740 BErrNo be;
741 Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
742 p, be.bstrerror());
743 return false;
744 }
745 bstrncpy(buf, code, sizeof(buf));
746 Dmsg1(500, "code=%s\n", buf);
747 optlen = strlen(buf);
748 while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
749 fd->message_length = Mmsg(fd->msg, "%s", buf);
750 if (!BnetSend(fd)) {
751 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
752 fclose(ffd);
753 return false;
754 }
755 }
756 fclose(ffd);
757 break;
758 case '\\':
759 p++; /* skip over \ */
760 /* Note, fall through wanted */
761 default:
762 PmStrcpy(fd->msg, code);
763 fd->message_length = PmStrcat(fd->msg, p);
764 Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
765 if (!fd->send()) {
766 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
767 return false;
768 }
769 break;
770 }
771 return true;
772 }
773
774 /**
775 * Send include list to File daemon
776 */
SendIncludeList(JobControlRecord * jcr)777 bool SendIncludeList(JobControlRecord *jcr)
778 {
779 BareosSocket *fd = jcr->file_bsock;
780 if (jcr->res.fileset->new_include) {
781 fd->fsend(filesetcmd, jcr->res.fileset->enable_vss ? " vss=1" : "");
782 return SendFileset(jcr);
783 }
784 return true;
785 }
786
787 /**
788 * Send exclude list to File daemon
789 * Under the new scheme, the Exclude list
790 * is part of the FileSet sent with the
791 * "include_list" above.
792 */
SendExcludeList(JobControlRecord * jcr)793 bool SendExcludeList(JobControlRecord *jcr)
794 {
795 return true;
796 }
797
798 /*
799 * This checks to see if there are any non local runscripts for this job.
800 */
HaveClientRunscripts(alist * RunScripts)801 static bool HaveClientRunscripts(alist *RunScripts)
802 {
803 RunScript *cmd = nullptr;
804 bool retval = false;
805
806 if (RunScripts->empty()) {
807 return false;
808 }
809
810 foreach_alist(cmd, RunScripts) {
811 if (!cmd->IsLocal()) {
812 retval = true;
813 }
814 }
815
816 return retval;
817 }
818
819 /**
820 * Send RunScripts to File daemon
821 * 1) We send all runscript to FD, they can be executed Before, After, or twice
822 * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
823 * first run_script() call. (ie ClientRunBeforeJob)
824 */
SendRunscriptsCommands(JobControlRecord * jcr)825 int SendRunscriptsCommands(JobControlRecord *jcr)
826 {
827 int result;
828 RunScript *cmd = nullptr;
829 POOLMEM *msg, *ehost;
830 BareosSocket *fd = jcr->file_bsock;
831 bool has_before_jobs = false;
832
833 /*
834 * See if there are any runscripts that need to be ran on the client.
835 */
836 if (!HaveClientRunscripts(jcr->res.job->RunScripts)) {
837 return 1;
838 }
839
840 Dmsg0(120, "dird: sending runscripts to fd\n");
841
842 msg = GetPoolMemory(PM_FNAME);
843 ehost = GetPoolMemory(PM_FNAME);
844 foreach_alist(cmd, jcr->res.job->RunScripts) {
845 if (cmd->CanRunAtLevel(jcr->getJobLevel()) && cmd->target) {
846 ehost = edit_job_codes(jcr, ehost, cmd->target, "");
847 Dmsg2(200, "dird: runscript %s -> %s\n", cmd->target, ehost);
848
849 if (bstrcmp(ehost, jcr->res.client->name())) {
850 PmStrcpy(msg, cmd->command);
851 BashSpaces(msg);
852
853 Dmsg1(120, "dird: sending runscripts to fd '%s'\n", cmd->command);
854
855 fd->fsend(runscriptcmd,
856 cmd->on_success,
857 cmd->on_failure,
858 cmd->fail_on_error,
859 cmd->when,
860 msg);
861
862 result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
863
864 if (!result) {
865 goto bail_out;
866 }
867 }
868 /* TODO : we have to play with other client */
869 /*
870 else {
871 send command to another client
872 }
873 */
874 }
875
876 /*
877 * See if this is a ClientRunBeforeJob.
878 */
879 if (cmd->when & SCRIPT_Before || cmd->when & SCRIPT_AfterVSS) {
880 has_before_jobs = true;
881 }
882 }
883
884 /*
885 * Tell the FD to execute the ClientRunBeforeJob
886 */
887 if (has_before_jobs) {
888 fd->fsend(runbeforenowcmd);
889 if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
890 goto bail_out;
891 }
892 }
893
894 FreePoolMemory(msg);
895 FreePoolMemory(ehost);
896
897 return 1;
898
899 bail_out:
900 Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" RunScript failed.\n"), ehost);
901 FreePoolMemory(msg);
902 FreePoolMemory(ehost);
903
904 return 0;
905 }
906
907 struct RestoreObjectContext {
908 JobControlRecord *jcr;
909 int count;
910 };
911
912 /**
913 * RestoreObjectHandler is called for each file found
914 */
RestoreObjectHandler(void * ctx,int num_fields,char ** row)915 static int RestoreObjectHandler(void *ctx, int num_fields, char **row)
916 {
917 BareosSocket *fd;
918 bool is_compressed;
919 RestoreObjectContext *octx = (RestoreObjectContext *)ctx;
920 JobControlRecord *jcr = octx->jcr;
921
922 fd = jcr->file_bsock;
923 if (jcr->IsJobCanceled()) {
924 return 1;
925 }
926
927 /*
928 * Old File Daemon doesn't handle restore objects
929 */
930 if (jcr->FDVersion < FD_VERSION_3) {
931 Jmsg(jcr, M_WARNING, 0, _("Client \"%s\" may not be used to restore "
932 "this job. Please upgrade your client.\n"),
933 jcr->res.client->name());
934 return 1;
935 }
936
937 if (jcr->FDVersion < FD_VERSION_5) { /* Old version without PluginName */
938 fd->fsend("restoreobject JobId=%s %s,%s,%s,%s,%s,%s\n",
939 row[0], row[1], row[2], row[3], row[4], row[5], row[6]);
940 } else {
941 /*
942 * bash spaces from PluginName
943 */
944 BashSpaces(row[9]);
945 fd->fsend("restoreobject JobId=%s %s,%s,%s,%s,%s,%s,%s\n",
946 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[9]);
947 }
948 Dmsg1(010, "Send obj hdr=%s", fd->msg);
949
950 fd->message_length = PmStrcpy(fd->msg, row[7]);
951 fd->send(); /* send Object name */
952
953 Dmsg1(010, "Send obj: %s\n", fd->msg);
954
955 jcr->db->UnescapeObject(jcr,
956 row[8], /* Object */
957 str_to_uint64(row[1]), /* Object length */
958 fd->msg,
959 &fd->message_length);
960 fd->send(); /* send object */
961 octx->count++;
962
963 /*
964 * Don't try to print compressed objects.
965 */
966 is_compressed = str_to_uint64(row[5]) > 0;
967 if (debug_level >= 100 && !is_compressed) {
968 for (int i = 0; i < fd->message_length; i++) {
969 if (!fd->msg[i]) {
970 fd->msg[i] = ' ';
971 }
972 }
973
974 Dmsg1(100, "Send obj: %s\n", fd->msg);
975 }
976
977 return 0;
978 }
979
SendPluginOptions(JobControlRecord * jcr)980 bool SendPluginOptions(JobControlRecord *jcr)
981 {
982 BareosSocket *fd = jcr->file_bsock;
983 POOLMEM *msg;
984
985 if (jcr->plugin_options) {
986 msg = GetPoolMemory(PM_FNAME);
987 PmStrcpy(msg, jcr->plugin_options);
988 BashSpaces(msg);
989
990 fd->fsend(pluginoptionscmd, msg);
991 FreePoolMemory(msg);
992
993 if (!response(jcr, fd, OKPluginOptions, "PluginOptions", DISPLAY_ERROR)) {
994 Jmsg(jcr, M_FATAL, 0, _("Plugin options failed.\n"));
995 return false;
996 }
997 }
998
999 return true;
1000 }
1001
SendGlobalRestoreObjects(JobControlRecord * jcr,RestoreObjectContext * octx)1002 static void SendGlobalRestoreObjects(JobControlRecord *jcr, RestoreObjectContext *octx)
1003 {
1004 char ed1[50];
1005 PoolMem query(PM_MESSAGE);
1006
1007 if (!jcr->JobIds || !jcr->JobIds[0]) {
1008 return;
1009 }
1010
1011 /*
1012 * Send restore objects for all jobs involved
1013 */
1014 jcr->db->FillQuery(query, BareosDb::SQL_QUERY_get_restore_objects, jcr->JobIds, FT_RESTORE_FIRST);
1015 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void *)octx);
1016
1017 jcr->db->FillQuery(query, BareosDb::SQL_QUERY_get_restore_objects, jcr->JobIds, FT_PLUGIN_CONFIG);
1018 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void *)octx);
1019
1020 /*
1021 * Send config objects for the current restore job
1022 */
1023 jcr->db->FillQuery(query, BareosDb::SQL_QUERY_get_restore_objects, edit_uint64(jcr->JobId, ed1), FT_PLUGIN_CONFIG_FILLED);
1024 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void *)octx);
1025 }
1026
SendJobSpecificRestoreObjects(JobControlRecord * jcr,JobId_t JobId,RestoreObjectContext * octx)1027 static void SendJobSpecificRestoreObjects(JobControlRecord *jcr, JobId_t JobId, RestoreObjectContext *octx)
1028 {
1029 char ed1[50];
1030 PoolMem query(PM_MESSAGE);
1031
1032 /*
1033 * Send restore objects for specific JobId.
1034 */
1035 jcr->db->FillQuery(query, BareosDb::SQL_QUERY_get_restore_objects, edit_uint64(JobId, ed1), FT_RESTORE_FIRST);
1036 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void *)octx);
1037
1038 jcr->db->FillQuery(query, BareosDb::SQL_QUERY_get_restore_objects, edit_uint64(JobId, ed1), FT_PLUGIN_CONFIG);
1039 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void *)octx);
1040 }
1041
SendRestoreObjects(JobControlRecord * jcr,JobId_t JobId,bool send_global)1042 bool SendRestoreObjects(JobControlRecord *jcr, JobId_t JobId, bool send_global)
1043 {
1044 BareosSocket *fd;
1045 RestoreObjectContext octx;
1046
1047 octx.jcr = jcr;
1048 octx.count = 0;
1049
1050 if (send_global) {
1051 SendGlobalRestoreObjects(jcr, &octx);
1052 } else {
1053 SendJobSpecificRestoreObjects(jcr, JobId, &octx);
1054 }
1055
1056 /*
1057 * Send to FD only if we have at least one restore object.
1058 * This permits backward compatibility with older FDs.
1059 */
1060 if (octx.count > 0) {
1061 fd = jcr->file_bsock;
1062 fd->fsend(restoreobjectendcmd);
1063 if (!response(jcr, fd, OKRestoreObject, "RestoreObject", DISPLAY_ERROR)) {
1064 Jmsg(jcr, M_FATAL, 0, _("RestoreObject failed.\n"));
1065 return false;
1066 }
1067 }
1068
1069 return true;
1070 }
1071
1072 /**
1073 * Read the attributes from the File daemon for
1074 * a Verify job and store them in the catalog.
1075 */
GetAttributesAndPutInCatalog(JobControlRecord * jcr)1076 int GetAttributesAndPutInCatalog(JobControlRecord *jcr)
1077 {
1078 BareosSocket *fd;
1079 int n = 0;
1080 AttributesDbRecord *ar = NULL;
1081 PoolMem digest(PM_MESSAGE);
1082
1083 fd = jcr->file_bsock;
1084 jcr->jr.FirstIndex = 1;
1085 jcr->FileIndex = 0;
1086
1087 /*
1088 * Start transaction allocates jcr->attr and jcr->ar if needed
1089 */
1090 jcr->db->StartTransaction(jcr); /* start transaction if not already open */
1091 ar = jcr->ar;
1092
1093 Dmsg0(120, "dird: waiting to receive file attributes\n");
1094
1095 /*
1096 * Pickup file attributes and digest
1097 */
1098 while (!fd->errors && (n = BgetDirmsg(fd)) > 0) {
1099 uint32_t file_index;
1100 int stream, len;
1101 char *p, *fn;
1102 PoolMem Digest(PM_MESSAGE); /* Either Verify opts or MD5/SHA1 digest */
1103
1104 Digest.check_size(fd->message_length);
1105 if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Digest.c_str())) != 3) {
1106 Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
1107 "message_length=%d msg=%s\n"), len, fd->message_length, fd->msg);
1108 jcr->setJobStatus(JS_ErrorTerminated);
1109 return 0;
1110 }
1111 p = fd->msg;
1112
1113 /*
1114 * The following three fields were sscanf'ed above so skip them
1115 */
1116 SkipNonspaces(&p); /* skip FileIndex */
1117 SkipSpaces(&p);
1118 SkipNonspaces(&p); /* skip Stream */
1119 SkipSpaces(&p);
1120 SkipNonspaces(&p); /* skip Opts_Digest */
1121 p++; /* skip space */
1122 Dmsg1(debuglevel, "Stream=%d\n", stream);
1123 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
1124 if (jcr->cached_attribute) {
1125 Dmsg3(debuglevel, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname,
1126 ar->attr);
1127 if (!jcr->db->CreateFileAttributesRecord(jcr, ar)) {
1128 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), jcr->db->strerror());
1129 }
1130 }
1131
1132 /*
1133 * Any cached attr is flushed so we can reuse jcr->attr and jcr->ar
1134 */
1135 fn = jcr->fname = CheckPoolMemorySize(jcr->fname, fd->message_length);
1136 while (*p != 0) {
1137 *fn++ = *p++; /* copy filename */
1138 }
1139 *fn = *p++; /* term filename and point p to attribs */
1140 PmStrcpy(jcr->attr, p); /* save attributes */
1141 jcr->JobFiles++;
1142 jcr->FileIndex = file_index;
1143 ar->attr = jcr->attr;
1144 ar->fname = jcr->fname;
1145 ar->FileIndex = file_index;
1146 ar->Stream = stream;
1147 ar->link = NULL;
1148 ar->JobId = jcr->JobId;
1149 ar->ClientId = jcr->ClientId;
1150 ar->PathId = 0;
1151 ar->Digest = NULL;
1152 ar->DigestType = CRYPTO_DIGEST_NONE;
1153 ar->DeltaSeq = 0;
1154 jcr->cached_attribute = true;
1155
1156 Dmsg2(debuglevel, "dird<filed: stream=%d %s\n", stream, jcr->fname);
1157 Dmsg1(debuglevel, "dird<filed: attr=%s\n", ar->attr);
1158 jcr->FileId = ar->FileId;
1159 } else if (CryptoDigestStreamType(stream) != CRYPTO_DIGEST_NONE) {
1160 size_t length;
1161
1162 /*
1163 * First, get STREAM_UNIX_ATTRIBUTES and fill AttributesDbRecord structure
1164 * Next, we CAN have a CRYPTO_DIGEST, so we fill AttributesDbRecord with it (or not)
1165 * When we get a new STREAM_UNIX_ATTRIBUTES, we known that we can add file to the catalog
1166 * At the end, we have to add the last file
1167 */
1168 if (jcr->FileIndex != (uint32_t)file_index) {
1169 Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
1170 stream_to_ascii(stream), file_index, jcr->FileIndex);
1171 continue;
1172 }
1173
1174 ar->Digest = digest.c_str();
1175 ar->DigestType = CryptoDigestStreamType(stream);
1176 length = strlen(Digest.c_str());
1177 digest.check_size(length * 2 + 1);
1178 jcr->db->EscapeString(jcr, digest.c_str(), Digest.c_str(), length);
1179 Dmsg4(debuglevel, "stream=%d DigestLen=%d Digest=%s type=%d\n", stream,
1180 strlen(digest.c_str()), digest.c_str(), ar->DigestType);
1181 }
1182 jcr->jr.JobFiles = jcr->JobFiles = file_index;
1183 jcr->jr.LastIndex = file_index;
1184 }
1185
1186 if (IsBnetError(fd)) {
1187 Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
1188 fd->bstrerror());
1189 return 0;
1190 }
1191
1192 if (jcr->cached_attribute) {
1193 Dmsg3(debuglevel, "Cached attr with digest. Stream=%d fname=%s attr=%s\n", ar->Stream,
1194 ar->fname, ar->attr);
1195 if (!jcr->db->CreateFileAttributesRecord(jcr, ar)) {
1196 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), jcr->db->strerror());
1197 }
1198 jcr->cached_attribute = false;
1199 }
1200
1201 jcr->setJobStatus(JS_Terminated);
1202
1203 return 1;
1204 }
1205
1206 /**
1207 * Cancel a job running in the File daemon
1208 */
CancelFileDaemonJob(UaContext * ua,JobControlRecord * jcr)1209 bool CancelFileDaemonJob(UaContext *ua, JobControlRecord *jcr)
1210 {
1211 BareosSocket *fd;
1212
1213 ua->jcr->res.client = jcr->res.client;
1214 if (!ConnectToFileDaemon(ua->jcr, 10, me->FDConnectTimeout, true, ua)) {
1215 ua->ErrorMsg(_("\nFailed to connect to File daemon.\n"));
1216 return false;
1217 }
1218 Dmsg0(200, "Connected to file daemon\n");
1219 fd = ua->jcr->file_bsock;
1220 fd->fsend("cancel Job=%s\n", jcr->Job);
1221 while (fd->recv() >= 0) {
1222 ua->SendMsg("%s", fd->msg);
1223 }
1224 fd->signal(BNET_TERMINATE);
1225 fd->close();
1226 delete ua->jcr->file_bsock;
1227 ua->jcr->file_bsock = NULL;
1228 jcr->file_bsock->SetTerminated();
1229 jcr->MyThreadSendSignal(TIMEOUT_SIGNAL);
1230 return true;
1231 }
1232
1233 /**
1234 * Get the status of a remote File Daemon.
1235 */
DoNativeClientStatus(UaContext * ua,ClientResource * client,char * cmd)1236 void DoNativeClientStatus(UaContext *ua, ClientResource *client, char *cmd)
1237 {
1238 BareosSocket *fd;
1239
1240 /*
1241 * Connect to File daemon
1242 */
1243 ua->jcr->res.client = client;
1244
1245 /*
1246 * Try to connect for 15 seconds
1247 */
1248 if (!ua->api) {
1249 ua->SendMsg(_("Connecting to Client %s at %s:%d\n"),
1250 client->name(), client->address, client->FDport);
1251 }
1252
1253 if (!ConnectToFileDaemon(ua->jcr, 1, 15, false, ua)) {
1254 ua->SendMsg(_("\nFailed to connect to Client %s.\n====\n"),
1255 client->name());
1256 if (ua->jcr->file_bsock) {
1257 ua->jcr->file_bsock->close();
1258 delete ua->jcr->file_bsock;
1259 ua->jcr->file_bsock = NULL;
1260 }
1261 return;
1262 }
1263
1264 Dmsg0(20, _("Connected to file daemon\n"));
1265 fd = ua->jcr->file_bsock;
1266 if (cmd) {
1267 fd->fsend(".status %s", cmd);
1268 } else {
1269 fd->fsend("status");
1270 }
1271
1272 while (fd->recv() >= 0) {
1273 ua->SendMsg("%s", fd->msg);
1274 }
1275
1276 fd->signal(BNET_TERMINATE);
1277 fd->close();
1278 delete ua->jcr->file_bsock;
1279 ua->jcr->file_bsock = NULL;
1280
1281 return;
1282 }
1283
1284 /**
1285 * resolve a host on a filedaemon
1286 */
DoClientResolve(UaContext * ua,ClientResource * client)1287 void DoClientResolve(UaContext *ua, ClientResource *client)
1288 {
1289 BareosSocket *fd;
1290
1291 /*
1292 * Connect to File daemon
1293 */
1294 ua->jcr->res.client = client;
1295
1296 /*
1297 * Try to connect for 15 seconds
1298 */
1299 if (!ua->api) {
1300 ua->SendMsg(_("Connecting to Client %s at %s:%d\n"),
1301 client->name(), client->address, client->FDport);
1302 }
1303
1304 if (!ConnectToFileDaemon(ua->jcr, 1, 15, false, ua)) {
1305 ua->SendMsg(_("\nFailed to connect to Client %s.\n====\n"),
1306 client->name());
1307 if (ua->jcr->file_bsock) {
1308 ua->jcr->file_bsock->close();
1309 delete ua->jcr->file_bsock;
1310 ua->jcr->file_bsock = NULL;
1311 }
1312 return;
1313 }
1314
1315 Dmsg0(20, _("Connected to file daemon\n"));
1316 fd = ua->jcr->file_bsock;
1317
1318 for (int i = 1; i < ua->argc; i++) {
1319 if (!*ua->argk[i]) {
1320 continue;
1321 }
1322
1323 fd->fsend("resolve %s", ua->argk[i]);
1324 while (fd->recv() >= 0) {
1325 ua->SendMsg("%s", fd->msg);
1326 }
1327 }
1328
1329 fd->signal(BNET_TERMINATE);
1330 fd->close();
1331 delete ua->jcr->file_bsock;
1332 ua->jcr->file_bsock = NULL;
1333
1334 return;
1335 }
1336
1337 /**
1338 * After receiving a connection (in socket_server.c) if it is
1339 * from the File daemon, this routine is called.
1340 */
HandleFiledConnection(ConnectionPool * connections,BareosSocket * fd,char * client_name,int fd_protocol_version)1341 void *HandleFiledConnection(ConnectionPool *connections, BareosSocket *fd,
1342 char *client_name, int fd_protocol_version)
1343 {
1344 ClientResource *client_resource;
1345 Connection *connection = NULL;
1346
1347 client_resource = (ClientResource *)my_config->GetResWithName(R_CLIENT, client_name);
1348 if (!client_resource) {
1349 Emsg1(M_WARNING, 0, "Client \"%s\" tries to connect, "
1350 "but no matching resource is defined.\n", client_name);
1351 goto getout;
1352 }
1353
1354 if (!IsConnectFromClientAllowed(client_resource)) {
1355 Emsg1(M_WARNING, 0, "Client \"%s\" tries to connect, "
1356 "but does not have the required permission.\n", client_name);
1357 goto getout;
1358 }
1359
1360 if (!AuthenticateFileDaemon(fd, client_name)) {
1361 goto getout;
1362 }
1363
1364 Dmsg1(20, "Connected to file daemon %s\n", client_name);
1365
1366 connection = connections->add_connection(client_name, fd_protocol_version, fd, true);
1367 if (!connection) {
1368 Emsg0(M_ERROR, 0, "Failed to add connection to pool.\n");
1369 goto getout;
1370 }
1371
1372 /*
1373 * The connection is now kept in connection_pool.
1374 * This thread is no longer required and will end now.
1375 */
1376 return NULL;
1377
1378 getout:
1379 fd->close();
1380 delete(fd);
1381 return NULL;
1382 }
1383 } /* namespace directordaemon */
1384