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