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