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