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