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 "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 if (me->nokeepalive) { fd->ClearKeepalive(); }
138 heart_beat = get_heartbeat_interval(jcr->impl->res.client);
139
140 char name[MAX_NAME_LENGTH + 100];
141 bstrncpy(name, _("Client: "), sizeof(name));
142 bstrncat(name, jcr->impl->res.client->resource_name_, sizeof(name));
143
144 fd->SetSourceAddress(me->DIRsrc_addr);
145 if (!fd->connect(jcr, retry_interval, max_retry_time, heart_beat, name,
146 jcr->impl->res.client->address, NULL,
147 jcr->impl->res.client->FDport, verbose)) {
148 delete fd;
149 fd = NULL;
150 jcr->setJobStatus(JS_ErrorTerminated);
151 } else {
152 jcr->file_bsock = fd;
153 jcr->authenticated = false;
154 Dmsg0(10, "Opened connection with File daemon\n");
155 result = true;
156 }
157
158 return result;
159 }
160
OutputMessageForConnectionTry(JobControlRecord * jcr,UaContext * ua)161 static void OutputMessageForConnectionTry(JobControlRecord* jcr, UaContext* ua)
162 {
163 std::string m;
164
165 if (jcr->impl->res.client->connection_successful_handshake_ ==
166 ClientConnectionHandshakeMode::kUndefined ||
167 jcr->impl->res.client->connection_successful_handshake_ ==
168 ClientConnectionHandshakeMode::kFailed) {
169 m = "Probing client protocol... (result will be saved until config reload)";
170 } else {
171 return;
172 }
173
174 if (jcr && jcr->JobId != 0) {
175 std::string m1 = m + "\n";
176 Jmsg(jcr, M_INFO, 0, m1.c_str());
177 }
178 if (ua) { ua->SendMsg(m.c_str()); }
179 }
180
SendInfoChosenCipher(JobControlRecord * jcr,UaContext * ua)181 static void SendInfoChosenCipher(JobControlRecord* jcr, UaContext* ua)
182 {
183 std::string str;
184 jcr->file_bsock->GetCipherMessageString(str);
185 str += '\n';
186 if (jcr->JobId != 0) { Jmsg(jcr, M_INFO, 0, str.c_str()); }
187 if (ua) { /* only whith console connection */
188 ua->SendRawMsg(str.c_str());
189 }
190 }
191
SendInfoSuccess(JobControlRecord * jcr,UaContext * ua)192 static void SendInfoSuccess(JobControlRecord* jcr, UaContext* ua)
193 {
194 std::string m;
195 if (jcr->impl->res.client->connection_successful_handshake_ ==
196 ClientConnectionHandshakeMode::kUndefined) {
197 m += "\r\v";
198 }
199 bool add_newline_in_joblog = false;
200 switch (jcr->impl->connection_handshake_try_) {
201 case ClientConnectionHandshakeMode::kTlsFirst:
202 m += " Handshake: Immediate TLS,";
203 break;
204 case ClientConnectionHandshakeMode::kCleartextFirst:
205 m += " Handshake: Cleartext,";
206 add_newline_in_joblog = true;
207 break;
208 default:
209 m += " unknown mode\n";
210 break;
211 }
212
213 if (jcr && jcr->JobId != 0) {
214 std::string m1 = m;
215 std::replace(m1.begin(), m1.end(), '\r', ' ');
216 std::replace(m1.begin(), m1.end(), '\v', ' ');
217 std::replace(m1.begin(), m1.end(), ',', ' ');
218 if (add_newline_in_joblog) { m1 += std::string("\n"); }
219 Jmsg(jcr, M_INFO, 0, m1.c_str());
220 }
221 if (ua) { /* only whith console connection */
222 ua->SendRawMsg(m.c_str());
223 }
224 }
225
ConnectToFileDaemon(JobControlRecord * jcr,int retry_interval,int max_retry_time,bool verbose,UaContext * ua)226 bool ConnectToFileDaemon(JobControlRecord* jcr,
227 int retry_interval,
228 int max_retry_time,
229 bool verbose,
230 UaContext* ua)
231 {
232 bool success = false;
233 bool tcp_connect_failed = false;
234 int connect_tries =
235 3; /* as a finish-hook for the UseWaitingClient mechanism */
236
237 /* try the connection modes starting with tls directly,
238 * in case there is a client that cannot do Tls immediately then
239 * fall back to cleartext md5-handshake */
240 OutputMessageForConnectionTry(jcr, ua);
241 if (jcr->impl->res.client->connection_successful_handshake_ ==
242 ClientConnectionHandshakeMode::kUndefined ||
243 jcr->impl->res.client->connection_successful_handshake_ ==
244 ClientConnectionHandshakeMode::kFailed) {
245 if (jcr->impl->res.client->IsTlsConfigured()) {
246 jcr->impl->connection_handshake_try_ =
247 ClientConnectionHandshakeMode::kTlsFirst;
248 } else {
249 jcr->impl->connection_handshake_try_ =
250 ClientConnectionHandshakeMode::kCleartextFirst;
251 }
252 jcr->is_passive_client_connection_probing = true;
253 } else {
254 /* if there is a stored mode from a previous connection then use this */
255 jcr->impl->connection_handshake_try_ =
256 jcr->impl->res.client->connection_successful_handshake_;
257 jcr->is_passive_client_connection_probing = false;
258 }
259
260 do { /* while (tcp_connect_failed ...) */
261 /* connect the tcp socket */
262 if (!jcr->file_bsock) {
263 if (!UseWaitingClient(jcr, 0)) {
264 if (!connect_outbound_to_file_daemon(jcr, retry_interval,
265 max_retry_time, verbose)) {
266 if (!UseWaitingClient(
267 jcr,
268 max_retry_time)) { /* will set jcr->file_bsock accordingly */
269 tcp_connect_failed = true;
270 }
271 }
272 }
273 }
274
275 if (jcr->file_bsock) {
276 jcr->setJobStatus(JS_Running);
277 if (AuthenticateWithFileDaemon(jcr)) {
278 success = true;
279 SendInfoSuccess(jcr, ua);
280 SendInfoChosenCipher(jcr, ua);
281 jcr->is_passive_client_connection_probing = false;
282 jcr->impl->res.client->connection_successful_handshake_ =
283 jcr->impl->connection_handshake_try_;
284 } else {
285 /* authentication failed due to
286 * - tls mismatch or
287 * - if an old client cannot do tls- before md5-handshake
288 * */
289 switch (jcr->impl->connection_handshake_try_) {
290 case ClientConnectionHandshakeMode::kTlsFirst:
291 if (jcr->file_bsock) {
292 jcr->file_bsock->close();
293 delete jcr->file_bsock;
294 jcr->file_bsock = nullptr;
295 }
296 jcr->resetJobStatus(JS_Running);
297 jcr->impl->connection_handshake_try_ =
298 ClientConnectionHandshakeMode::kCleartextFirst;
299 break;
300 case ClientConnectionHandshakeMode::kCleartextFirst:
301 jcr->impl->connection_handshake_try_ =
302 ClientConnectionHandshakeMode::kFailed;
303 break;
304 case ClientConnectionHandshakeMode::kFailed:
305 default: /* should bei one of class ClientConnectionHandshakeMode */
306 ASSERT(false);
307 break;
308 }
309 }
310 } else {
311 Jmsg(jcr, M_FATAL, 0, "\nFailed to connect to client \"%s\".\n",
312 jcr->impl->res.client->resource_name_);
313 }
314 connect_tries--;
315 } while (!tcp_connect_failed && connect_tries && !success &&
316 jcr->impl->connection_handshake_try_ !=
317 ClientConnectionHandshakeMode::kFailed);
318
319 if (!success) { jcr->setJobStatus(JS_ErrorTerminated); }
320
321 return success;
322 }
323
SendJobInfoToFileDaemon(JobControlRecord * jcr)324 int SendJobInfoToFileDaemon(JobControlRecord* jcr)
325 {
326 BareosSocket* fd = jcr->file_bsock;
327
328 if (jcr->sd_auth_key == NULL) { jcr->sd_auth_key = strdup("dummy"); }
329
330 if (jcr->impl->res.client->connection_successful_handshake_ ==
331 ClientConnectionHandshakeMode::kTlsFirst) {
332 /* client protocol onwards Bareos 18.2 */
333 TlsPolicy tls_policy = kBnetTlsUnknown;
334 int JobLevel = jcr->getJobLevel();
335 switch (JobLevel) {
336 case L_VERIFY_INIT:
337 case L_VERIFY_CATALOG:
338 case L_VERIFY_DISK_TO_CATALOG:
339 tls_policy = kBnetTlsUnknown;
340 break;
341 default:
342 StorageResource* storage = nullptr;
343 if (jcr->impl->res.write_storage) {
344 storage = jcr->impl->res.write_storage;
345 } else if (jcr->impl->res.read_storage) {
346 storage = jcr->impl->res.read_storage;
347 } else {
348 Jmsg(jcr, M_FATAL, 0, _("No read or write storage defined\n"));
349 jcr->setJobStatus(JS_ErrorTerminated);
350 return 0;
351 }
352 if (storage) {
353 tls_policy = storage->IsTlsConfigured() ? TlsPolicy::kBnetTlsAuto
354 : TlsPolicy::kBnetTlsNone;
355 }
356 break;
357 }
358 char ed1[30];
359 fd->fsend(jobcmdssl, edit_int64(jcr->JobId, ed1), jcr->Job,
360 jcr->VolSessionId, jcr->VolSessionTime, jcr->sd_auth_key,
361 tls_policy);
362 } else { /* jcr->impl_->res.client->connection_successful_handshake_ !=
363 ClientConnectionHandshakeMode::kTlsFirst */
364 /* client protocol before Bareos 18.2 */
365 char ed1[30];
366 fd->fsend(jobcmd, edit_int64(jcr->JobId, ed1), jcr->Job, jcr->VolSessionId,
367 jcr->VolSessionTime, jcr->sd_auth_key);
368 } /* if (jcr->impl_->res.client->connection_successful_handshake_ ==
369 ClientConnectionHandshakeMode::kTlsFirst) */
370
371 if (!jcr->impl->keep_sd_auth_key && !bstrcmp(jcr->sd_auth_key, "dummy")) {
372 memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
373 }
374
375 Dmsg1(100, ">filed: %s", fd->msg);
376 if (BgetDirmsg(fd) > 0) {
377 Dmsg1(110, "<filed: %s", fd->msg);
378 if (!bstrncmp(fd->msg, OKjob, strlen(OKjob))) {
379 Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"),
380 jcr->impl->res.client->resource_name_, fd->msg);
381 jcr->setJobStatus(JS_ErrorTerminated);
382 return 0;
383 } else if (jcr->db) {
384 ClientDbRecord cr;
385
386 bstrncpy(cr.Name, jcr->impl->res.client->resource_name_, sizeof(cr.Name));
387 cr.AutoPrune = jcr->impl->res.client->AutoPrune;
388 cr.FileRetention = jcr->impl->res.client->FileRetention;
389 cr.JobRetention = jcr->impl->res.client->JobRetention;
390 bstrncpy(cr.Uname, fd->msg + strlen(OKjob) + 1, sizeof(cr.Uname));
391 if (!jcr->db->UpdateClientRecord(jcr, &cr)) {
392 Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"),
393 jcr->db->strerror());
394 }
395 }
396 } else {
397 Jmsg(jcr, M_FATAL, 0, _("FD gave bad response to JobId command: %s\n"),
398 BnetStrerror(fd));
399 jcr->setJobStatus(JS_ErrorTerminated);
400 return 0;
401 }
402
403 return 1;
404 }
405
SendPreviousRestoreObjects(JobControlRecord * jcr)406 bool SendPreviousRestoreObjects(JobControlRecord* jcr)
407 {
408 int JobLevel;
409
410 JobLevel = jcr->getJobLevel();
411 switch (JobLevel) {
412 case L_DIFFERENTIAL:
413 case L_INCREMENTAL:
414 if (jcr->impl->previous_jr.JobId > 0) {
415 if (!SendRestoreObjects(jcr, jcr->impl->previous_jr.JobId, false)) {
416 return false;
417 }
418 }
419 break;
420 default:
421 break;
422 }
423
424 return true;
425 }
426
SendBwlimitToFd(JobControlRecord * jcr,const char * Job)427 bool SendBwlimitToFd(JobControlRecord* jcr, const char* Job)
428 {
429 BareosSocket* fd = jcr->file_bsock;
430
431 if (jcr->impl->FDVersion >= FD_VERSION_4) {
432 fd->fsend(bandwidthcmd, jcr->max_bandwidth, Job);
433 if (!response(jcr, fd, OKBandwidth, "Bandwidth", DISPLAY_ERROR)) {
434 jcr->max_bandwidth = 0; /* can't set bandwidth limit */
435 return false;
436 }
437 }
438
439 return true;
440 }
441
SendSecureEraseReqToFd(JobControlRecord * jcr)442 bool SendSecureEraseReqToFd(JobControlRecord* jcr)
443 {
444 int32_t n;
445 BareosSocket* fd = jcr->file_bsock;
446
447 if (!jcr->impl->FDSecureEraseCmd) {
448 jcr->impl->FDSecureEraseCmd = GetPoolMemory(PM_NAME);
449 }
450
451 if (jcr->impl->FDVersion > FD_VERSION_53) {
452 fd->fsend(getSecureEraseCmd);
453 while ((n = BgetDirmsg(fd)) >= 0) {
454 jcr->impl->FDSecureEraseCmd =
455 CheckPoolMemorySize(jcr->impl->FDSecureEraseCmd, fd->message_length);
456 if (sscanf(fd->msg, OKgetSecureEraseCmd, jcr->impl->FDSecureEraseCmd) ==
457 1) {
458 Dmsg1(400, "Got FD Secure Erase Cmd: %s\n",
459 jcr->impl->FDSecureEraseCmd);
460 break;
461 } else {
462 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Secure Erase Cmd: %s\n"),
463 fd->msg);
464 PmStrcpy(jcr->impl->FDSecureEraseCmd, "*None*");
465 return false;
466 }
467 }
468 } else {
469 PmStrcpy(jcr->impl->FDSecureEraseCmd, "*None*");
470 }
471
472 return true;
473 }
474
SendSinceTime(JobControlRecord * jcr)475 static void SendSinceTime(JobControlRecord* jcr)
476 {
477 char ed1[50];
478 utime_t stime;
479 BareosSocket* fd = jcr->file_bsock;
480
481 stime = StrToUtime(jcr->stime);
482 fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0,
483 NT_("prev_job="), jcr->impl->PrevJob);
484
485 while (BgetDirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */
486 Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
487 }
488 }
489
490 /**
491 * Send level command to FD.
492 * Used for backup jobs and estimate command.
493 */
SendLevelCommand(JobControlRecord * jcr)494 bool SendLevelCommand(JobControlRecord* jcr)
495 {
496 int JobLevel;
497 BareosSocket* fd = jcr->file_bsock;
498 const char* accurate = jcr->accurate ? "accurate_" : "";
499 const char* not_accurate = "";
500 const char* rerunning = jcr->rerunning ? " rerunning " : " ";
501
502 /*
503 * Send Level command to File daemon
504 */
505 JobLevel = jcr->getJobLevel();
506 switch (JobLevel) {
507 case L_BASE:
508 fd->fsend(levelcmd, not_accurate, "base", rerunning, 0, "", "");
509 break;
510 case L_NONE: /* L_NONE is the console, sending something off to the FD */
511 case L_FULL:
512 fd->fsend(levelcmd, not_accurate, "full", rerunning, 0, "", "");
513 break;
514 case L_DIFFERENTIAL:
515 fd->fsend(levelcmd, accurate, "differential", rerunning, 0, "", "");
516 SendSinceTime(jcr);
517 break;
518 case L_INCREMENTAL:
519 fd->fsend(levelcmd, accurate, "incremental", rerunning, 0, "", "");
520 SendSinceTime(jcr);
521 break;
522 case L_SINCE:
523 Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"), JobLevel,
524 JobLevel);
525 break;
526 default:
527 Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"), JobLevel,
528 JobLevel);
529 return 0;
530 }
531
532 Dmsg1(120, ">filed: %s", fd->msg);
533
534 if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) { return false; }
535
536 return true;
537 }
538
539 /**
540 * Send either an Included or an Excluded list to FD
541 */
SendFileset(JobControlRecord * jcr)542 static bool SendFileset(JobControlRecord* jcr)
543 {
544 FilesetResource* fileset = jcr->impl->res.fileset;
545 BareosSocket* fd = jcr->file_bsock;
546 StorageResource* store = jcr->impl->res.write_storage;
547 int num;
548 bool include = true;
549
550 while (1) {
551 if (include) {
552 num = fileset->include_items.size();
553 } else {
554 num = fileset->exclude_items.size();
555 }
556 for (int i = 0; i < num; i++) {
557 char* item;
558 IncludeExcludeItem* ie;
559
560 if (include) {
561 ie = fileset->include_items[i];
562 fd->fsend("I\n");
563 } else {
564 ie = fileset->exclude_items[i];
565 fd->fsend("E\n");
566 }
567
568 for (int j = 0; j < ie->ignoredir.size(); j++) {
569 fd->fsend("Z %s\n", ie->ignoredir.get(j));
570 }
571
572 for (std::size_t j = 0; j < ie->file_options_list.size(); j++) {
573 FileOptions* fo = ie->file_options_list[j];
574 bool enhanced_wild = false;
575
576 for (int k = 0; fo->opts[k] != '\0'; k++) {
577 if (fo->opts[k] == 'W') {
578 enhanced_wild = true;
579 break;
580 }
581 }
582
583 /*
584 * Strip out compression option Zn if disallowed for this Storage
585 */
586 if (store && !store->AllowCompress) {
587 char newopts[MAX_FOPTS];
588 bool done =
589 false; /* print warning only if compression enabled in FS */
590 int l = 0;
591
592 for (int k = 0; fo->opts[k] != '\0'; k++) {
593 /*
594 * Z compress option is followed by the single-digit compress level
595 * or 'o' For fastlz its Zf with a single char selecting the actual
596 * compression algo.
597 */
598 if (fo->opts[k] == 'Z' && fo->opts[k + 1] == 'f') {
599 done = true;
600 k += 2; /* skip option */
601 } else if (fo->opts[k] == 'Z') {
602 done = true;
603 k++; /* skip option and level */
604 } else {
605 newopts[l] = fo->opts[k];
606 l++;
607 }
608 }
609 newopts[l] = '\0';
610
611 if (done) {
612 Jmsg(jcr, M_INFO, 0,
613 _("FD compression disabled for this Job because "
614 "AllowCompress=No in Storage resource.\n"));
615 }
616
617 /*
618 * Send the new trimmed option set without overwriting fo->opts
619 */
620 fd->fsend("O %s\n", newopts);
621 } else {
622 /*
623 * Send the original options
624 */
625 fd->fsend("O %s\n", fo->opts);
626 }
627
628 for (int k = 0; k < fo->regex.size(); k++) {
629 fd->fsend("R %s\n", fo->regex.get(k));
630 }
631
632 for (int k = 0; k < fo->regexdir.size(); k++) {
633 fd->fsend("RD %s\n", fo->regexdir.get(k));
634 }
635
636 for (int k = 0; k < fo->regexfile.size(); k++) {
637 fd->fsend("RF %s\n", fo->regexfile.get(k));
638 }
639
640 for (int k = 0; k < fo->wild.size(); k++) {
641 fd->fsend("W %s\n", fo->wild.get(k));
642 }
643
644 for (int k = 0; k < fo->wilddir.size(); k++) {
645 fd->fsend("WD %s\n", fo->wilddir.get(k));
646 }
647
648 for (int k = 0; k < fo->wildfile.size(); k++) {
649 fd->fsend("WF %s\n", fo->wildfile.get(k));
650 }
651
652 for (int k = 0; k < fo->wildbase.size(); k++) {
653 fd->fsend("W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
654 }
655
656 for (int k = 0; k < fo->base.size(); k++) {
657 fd->fsend("B %s\n", fo->base.get(k));
658 }
659
660 for (int k = 0; k < fo->fstype.size(); k++) {
661 fd->fsend("X %s\n", fo->fstype.get(k));
662 }
663
664 for (int k = 0; k < fo->Drivetype.size(); k++) {
665 fd->fsend("XD %s\n", fo->Drivetype.get(k));
666 }
667
668 if (fo->plugin) { fd->fsend("G %s\n", fo->plugin); }
669
670 if (fo->reader) { fd->fsend("D %s\n", fo->reader); }
671
672 if (fo->writer) { fd->fsend("T %s\n", fo->writer); }
673
674 fd->fsend("N\n");
675 }
676
677 for (int j = 0; j < ie->name_list.size(); j++) {
678 item = (char*)ie->name_list.get(j);
679 if (!SendListItem(jcr, "F ", item, fd)) { goto bail_out; }
680 }
681 fd->fsend("N\n");
682
683 for (int j = 0; j < ie->plugin_list.size(); j++) {
684 item = (char*)ie->plugin_list.get(j);
685 if (!SendListItem(jcr, "P ", item, fd)) { goto bail_out; }
686 }
687 fd->fsend("N\n");
688 }
689
690 if (!include) { /* If we just did excludes */
691 break; /* all done */
692 }
693
694 include = false; /* Now do excludes */
695 }
696
697 fd->signal(BNET_EOD); /* end of data */
698 if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) { goto bail_out; }
699 return true;
700
701 bail_out:
702 jcr->setJobStatus(JS_ErrorTerminated);
703 return false;
704 }
705
SendListItem(JobControlRecord * jcr,const char * code,char * item,BareosSocket * fd)706 static bool SendListItem(JobControlRecord* jcr,
707 const char* code,
708 char* item,
709 BareosSocket* fd)
710 {
711 Bpipe* bpipe;
712 FILE* ffd;
713 char buf[2000];
714 int optlen, status;
715 char* p = item;
716
717 switch (*p) {
718 case '|':
719 p++; /* skip over the | */
720 fd->msg = edit_job_codes(jcr, fd->msg, p, "");
721 bpipe = OpenBpipe(fd->msg, 0, "r");
722 if (!bpipe) {
723 BErrNo be;
724 Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"), p,
725 be.bstrerror());
726 return false;
727 }
728 bstrncpy(buf, code, sizeof(buf));
729 Dmsg1(500, "code=%s\n", buf);
730 optlen = strlen(buf);
731 while (fgets(buf + optlen, sizeof(buf) - optlen, bpipe->rfd)) {
732 fd->message_length = Mmsg(fd->msg, "%s", buf);
733 Dmsg2(500, "Inc/exc len=%d: %s", fd->message_length, fd->msg);
734 if (!BnetSend(fd)) {
735 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
736 return false;
737 }
738 }
739 if ((status = CloseBpipe(bpipe)) != 0) {
740 BErrNo be;
741 Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"), p,
742 be.bstrerror(status));
743 return false;
744 }
745 break;
746 case '<':
747 p++; /* skip over < */
748 if ((ffd = fopen(p, "rb")) == NULL) {
749 BErrNo be;
750 Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"), p,
751 be.bstrerror());
752 return false;
753 }
754 bstrncpy(buf, code, sizeof(buf));
755 Dmsg1(500, "code=%s\n", buf);
756 optlen = strlen(buf);
757 while (fgets(buf + optlen, sizeof(buf) - optlen, ffd)) {
758 fd->message_length = Mmsg(fd->msg, "%s", buf);
759 if (!BnetSend(fd)) {
760 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
761 fclose(ffd);
762 return false;
763 }
764 }
765 fclose(ffd);
766 break;
767 case '\\':
768 p++; /* skip over \ */
769 /* Note, fall through wanted */
770 default:
771 PmStrcpy(fd->msg, code);
772 fd->message_length = PmStrcat(fd->msg, p);
773 Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
774 if (!fd->send()) {
775 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
776 return false;
777 }
778 break;
779 }
780 return true;
781 }
782
783 /**
784 * Send include list to File daemon
785 */
SendIncludeList(JobControlRecord * jcr)786 bool SendIncludeList(JobControlRecord* jcr)
787 {
788 BareosSocket* fd = jcr->file_bsock;
789 if (jcr->impl->res.fileset->new_include) {
790 fd->fsend(filesetcmd, jcr->impl->res.fileset->enable_vss ? " vss=1" : "");
791 return SendFileset(jcr);
792 }
793 return true;
794 }
795
796 /**
797 * Send exclude list to File daemon
798 * Under the new scheme, the Exclude list
799 * is part of the FileSet sent with the
800 * "include_list" above.
801 */
SendExcludeList(JobControlRecord * jcr)802 bool SendExcludeList(JobControlRecord* jcr) { return true; }
803
804 /*
805 * This checks to see if there are any non local runscripts for this job.
806 */
HaveClientRunscripts(alist * RunScripts)807 static bool HaveClientRunscripts(alist* RunScripts)
808 {
809 RunScript* cmd = nullptr;
810 bool retval = false;
811
812 if (RunScripts->empty()) { return false; }
813
814 foreach_alist (cmd, RunScripts) {
815 if (!cmd->IsLocal()) { retval = true; }
816 }
817
818 return retval;
819 }
820
821 /**
822 * Send RunScripts to File daemon
823 * 1) We send all runscript to FD, they can be executed Before, After, or twice
824 * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
825 * first run_script() call. (ie ClientRunBeforeJob)
826 */
SendRunscriptsCommands(JobControlRecord * jcr)827 int SendRunscriptsCommands(JobControlRecord* jcr)
828 {
829 int result;
830 RunScript* cmd = nullptr;
831 POOLMEM *msg, *ehost;
832 BareosSocket* fd = jcr->file_bsock;
833 bool has_before_jobs = false;
834
835 /*
836 * See if there are any runscripts that need to be ran on the client.
837 */
838 if (!HaveClientRunscripts(jcr->impl->res.job->RunScripts)) { return 1; }
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->impl->res.job->RunScripts) {
845 if (!cmd->target.empty()) {
846 ehost = edit_job_codes(jcr, ehost, cmd->target.c_str(), "");
847 Dmsg2(200, "dird: runscript %s -> %s\n", cmd->target.c_str(), ehost);
848
849 if (bstrcmp(ehost, jcr->impl->res.client->resource_name_)) {
850 PmStrcpy(msg, cmd->command.c_str());
851 BashSpaces(msg);
852
853 Dmsg1(120, "dird: sending runscripts to fd '%s'\n",
854 cmd->command.c_str());
855
856 fd->fsend(runscriptcmd, cmd->on_success, cmd->on_failure,
857 cmd->fail_on_error, cmd->when, msg);
858
859 result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
860
861 if (!result) { goto bail_out; }
862 }
863 /* TODO : we have to play with other client */
864 /*
865 else {
866 send command to another client
867 }
868 */
869 }
870
871 /*
872 * See if this is a ClientRunBeforeJob.
873 */
874 if (cmd->when & SCRIPT_Before || cmd->when & SCRIPT_AfterVSS) {
875 has_before_jobs = true;
876 }
877 }
878
879 /*
880 * Tell the FD to execute the ClientRunBeforeJob
881 */
882 if (has_before_jobs) {
883 fd->fsend(runbeforenowcmd);
884 if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
885 goto bail_out;
886 }
887 }
888
889 FreePoolMemory(msg);
890 FreePoolMemory(ehost);
891
892 return 1;
893
894 bail_out:
895 Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" RunScript failed.\n"), ehost);
896 FreePoolMemory(msg);
897 FreePoolMemory(ehost);
898
899 return 0;
900 }
901
902 struct RestoreObjectContext {
903 JobControlRecord* jcr;
904 int count;
905 };
906
907 /**
908 * RestoreObjectHandler is called for each file found
909 */
RestoreObjectHandler(void * ctx,int num_fields,char ** row)910 static int RestoreObjectHandler(void* ctx, int num_fields, char** row)
911 {
912 BareosSocket* fd;
913 bool is_compressed;
914 RestoreObjectContext* octx = (RestoreObjectContext*)ctx;
915 JobControlRecord* jcr = octx->jcr;
916
917 fd = jcr->file_bsock;
918 if (jcr->IsJobCanceled()) { return 1; }
919
920 /*
921 * Old File Daemon doesn't handle restore objects
922 */
923 if (jcr->impl->FDVersion < FD_VERSION_3) {
924 Jmsg(jcr, M_WARNING, 0,
925 _("Client \"%s\" may not be used to restore "
926 "this job. Please upgrade your client.\n"),
927 jcr->impl->res.client->resource_name_);
928 return 1;
929 }
930
931 if (jcr->impl->FDVersion <
932 FD_VERSION_5) { /* Old version without PluginName */
933 fd->fsend("restoreobject JobId=%s %s,%s,%s,%s,%s,%s\n", row[0], row[1],
934 row[2], row[3], row[4], row[5], row[6]);
935 } else {
936 /*
937 * bash spaces from PluginName
938 */
939 BashSpaces(row[9]);
940 fd->fsend("restoreobject JobId=%s %s,%s,%s,%s,%s,%s,%s\n", row[0], row[1],
941 row[2], row[3], row[4], row[5], row[6], row[9]);
942 }
943 Dmsg1(010, "Send obj hdr=%s", fd->msg);
944
945 fd->message_length = PmStrcpy(fd->msg, row[7]);
946 fd->send(); /* send Object name */
947
948 Dmsg1(010, "Send obj: %s\n", fd->msg);
949
950 jcr->db->UnescapeObject(jcr, row[8], /* Object */
951 str_to_uint64(row[1]), /* Object length */
952 fd->msg, &fd->message_length);
953 fd->send(); /* send object */
954 octx->count++;
955
956 /*
957 * Don't try to print compressed objects.
958 */
959 is_compressed = str_to_uint64(row[5]) > 0;
960 if (debug_level >= 100 && !is_compressed) {
961 for (int i = 0; i < fd->message_length; i++) {
962 if (!fd->msg[i]) { fd->msg[i] = ' '; }
963 }
964
965 Dmsg1(100, "Send obj: %s\n", fd->msg);
966 }
967
968 return 0;
969 }
970
SendPluginOptions(JobControlRecord * jcr)971 bool SendPluginOptions(JobControlRecord* jcr)
972 {
973 BareosSocket* fd = jcr->file_bsock;
974 int i;
975 PoolMem cur_plugin_options(PM_MESSAGE);
976 const char* plugin_options;
977 POOLMEM* msg;
978
979 if (jcr->impl->plugin_options) {
980 msg = GetPoolMemory(PM_FNAME);
981 PmStrcpy(msg, jcr->impl->plugin_options);
982 BashSpaces(msg);
983
984 fd->fsend(pluginoptionscmd, msg);
985 FreePoolMemory(msg);
986
987 if (!response(jcr, fd, OKPluginOptions, "PluginOptions", DISPLAY_ERROR)) {
988 Jmsg(jcr, M_FATAL, 0, _("Plugin options failed.\n"));
989 return false;
990 }
991 }
992 if (jcr->impl->res.job && jcr->impl->res.job->FdPluginOptions &&
993 jcr->impl->res.job->FdPluginOptions->size()) {
994 Dmsg2(200, "dird: sendpluginoptions found FdPluginOptions in res.job");
995 foreach_alist_index (i, plugin_options,
996 jcr->impl->res.job->FdPluginOptions) {
997 PmStrcpy(cur_plugin_options, plugin_options);
998 BashSpaces(cur_plugin_options.c_str());
999
1000 fd->fsend(pluginoptionscmd, cur_plugin_options.c_str());
1001 if (!response(jcr, fd, OKPluginOptions, "PluginOptions", DISPLAY_ERROR)) {
1002 Jmsg(jcr, M_FATAL, 0, _("Plugin options failed.\n"));
1003 return false;
1004 }
1005 }
1006 }
1007
1008 return true;
1009 }
1010
SendGlobalRestoreObjects(JobControlRecord * jcr,RestoreObjectContext * octx)1011 static void SendGlobalRestoreObjects(JobControlRecord* jcr,
1012 RestoreObjectContext* octx)
1013 {
1014 char ed1[50];
1015 PoolMem query(PM_MESSAGE);
1016
1017 if (!jcr->JobIds || !jcr->JobIds[0]) { return; }
1018
1019 /*
1020 * Send restore objects for all jobs involved
1021 */
1022 jcr->db->FillQuery(query, BareosDb::SQL_QUERY::get_restore_objects,
1023 jcr->JobIds, FT_RESTORE_FIRST);
1024 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void*)octx);
1025
1026 jcr->db->FillQuery(query, BareosDb::SQL_QUERY::get_restore_objects,
1027 jcr->JobIds, FT_PLUGIN_CONFIG);
1028 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void*)octx);
1029
1030 /*
1031 * Send config objects for the current restore job
1032 */
1033 jcr->db->FillQuery(query, BareosDb::SQL_QUERY::get_restore_objects,
1034 edit_uint64(jcr->JobId, ed1), FT_PLUGIN_CONFIG_FILLED);
1035 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void*)octx);
1036 }
1037
SendJobSpecificRestoreObjects(JobControlRecord * jcr,JobId_t JobId,RestoreObjectContext * octx)1038 static void SendJobSpecificRestoreObjects(JobControlRecord* jcr,
1039 JobId_t JobId,
1040 RestoreObjectContext* octx)
1041 {
1042 char ed1[50];
1043 PoolMem query(PM_MESSAGE);
1044
1045 /*
1046 * Send restore objects for specific JobId.
1047 */
1048 jcr->db->FillQuery(query, BareosDb::SQL_QUERY::get_restore_objects,
1049 edit_uint64(JobId, ed1), FT_RESTORE_FIRST);
1050 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void*)octx);
1051
1052 jcr->db->FillQuery(query, BareosDb::SQL_QUERY::get_restore_objects,
1053 edit_uint64(JobId, ed1), FT_PLUGIN_CONFIG);
1054 jcr->db->SqlQuery(query.c_str(), RestoreObjectHandler, (void*)octx);
1055 }
1056
SendRestoreObjects(JobControlRecord * jcr,JobId_t JobId,bool send_global)1057 bool SendRestoreObjects(JobControlRecord* jcr, JobId_t JobId, bool send_global)
1058 {
1059 BareosSocket* fd;
1060 RestoreObjectContext octx;
1061
1062 octx.jcr = jcr;
1063 octx.count = 0;
1064
1065 if (send_global) {
1066 SendGlobalRestoreObjects(jcr, &octx);
1067 } else {
1068 SendJobSpecificRestoreObjects(jcr, JobId, &octx);
1069 }
1070
1071 /*
1072 * Send to FD only if we have at least one restore object.
1073 * This permits backward compatibility with older FDs.
1074 */
1075 if (octx.count > 0) {
1076 fd = jcr->file_bsock;
1077 fd->fsend(restoreobjectendcmd);
1078 if (!response(jcr, fd, OKRestoreObject, "RestoreObject", DISPLAY_ERROR)) {
1079 Jmsg(jcr, M_FATAL, 0, _("RestoreObject failed.\n"));
1080 return false;
1081 }
1082 }
1083
1084 return true;
1085 }
1086
1087 /**
1088 * Read the attributes from the File daemon for
1089 * a Verify job and store them in the catalog.
1090 */
GetAttributesAndPutInCatalog(JobControlRecord * jcr)1091 int GetAttributesAndPutInCatalog(JobControlRecord* jcr)
1092 {
1093 BareosSocket* fd;
1094 int n = 0;
1095 AttributesDbRecord* ar = NULL;
1096 PoolMem digest(PM_MESSAGE);
1097
1098 fd = jcr->file_bsock;
1099 jcr->impl->jr.FirstIndex = 1;
1100 jcr->impl->FileIndex = 0;
1101
1102 /*
1103 * Start transaction allocates jcr->attr and jcr->ar if needed
1104 */
1105 jcr->db->StartTransaction(jcr); /* start transaction if not already open */
1106 ar = jcr->ar;
1107
1108 Dmsg0(120, "dird: waiting to receive file attributes\n");
1109
1110 /*
1111 * Pickup file attributes and digest
1112 */
1113 while (!fd->errors && (n = BgetDirmsg(fd)) > 0) {
1114 uint32_t file_index;
1115 int stream, len;
1116 char *p, *fn;
1117 PoolMem Digest(PM_MESSAGE); /* Either Verify opts or MD5/SHA1 digest */
1118 Digest.check_size(fd->message_length);
1119 if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream,
1120 Digest.c_str())) != 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