2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
18 * Bacula Director -- fd_cmds.c -- send commands to File daemon
20 * Kern Sibbald, October MM
22 * This routine is run as a separate thread. There may be more
23 * work to be done to make it totally reentrant!!!!
25 * Utility functions for sending info to File Daemon.
26 * These functions are used by both backup and verify.
32 #include "findlib/find.h"
34 const int dbglvl = 400;
36 /* Commands sent to File daemon */
37 static char filesetcmd[] = "fileset%s\n"; /* set full fileset */
38 static char jobcmd[] = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
39 /* Note, mtime_only is not used here -- implemented as file option */
40 static char levelcmd[] = "level = %s%s%s mtime_only=%d %s%s\n";
41 static char runscript[] = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
42 static char runbeforenow[]= "RunBeforeNow\n";
43 static char bandwidthcmd[] = "setbandwidth=%lld Job=%s\n";
45 /* Responses received from File daemon */
46 static char OKinc[] = "2000 OK include\n";
47 static char OKjob[] = "2000 OK Job";
48 static char OKlevel[] = "2000 OK level\n";
49 static char OKRunScript[] = "2000 OK RunScript\n";
50 static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n";
51 static char OKRestoreObject[] = "2000 OK ObjectRestored\n";
52 static char OKBandwidth[] = "2000 OK Bandwidth\n";
54 /* Forward referenced functions */
55 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd);
57 /* External functions */
58 extern DIRRES *director;
59 extern int FDConnectTimeout;
65 * Open connection with File daemon.
66 * Try connecting every retry_interval (default 10 sec), and
67 * give up after max_retry_time (default 30 mins).
70 int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
73 BSOCK *fd = jcr->file_bsock;
77 if (jcr->client->heartbeat_interval) {
78 heart_beat = jcr->client->heartbeat_interval;
80 heart_beat = director->heartbeat_interval;
83 if (!is_bsock_open(jcr->file_bsock)) {
84 char name[MAX_NAME_LENGTH + 100];
87 fd = jcr->file_bsock = new_bsock();
89 bstrncpy(name, _("Client: "), sizeof(name));
90 bstrncat(name, jcr->client->name(), sizeof(name));
92 fd->set_source_address(director->DIRsrc_addr);
93 if (!fd->connect(jcr,retry_interval,max_retry_time, heart_beat, name, jcr->client->address,
94 NULL, jcr->client->FDport, verbose)) {
96 jcr->setJobStatus(JS_ErrorTerminated);
99 Dmsg0(10, "Opened connection with File daemon\n");
101 fd->res = (RES *)jcr->client; /* save resource in BSOCK */
102 jcr->setJobStatus(JS_Running);
104 if (!authenticate_file_daemon(jcr)) {
105 jcr->setJobStatus(JS_ErrorTerminated);
106 Dmsg0(10, "Authentication error with FD.\n");
111 * Now send JobId and authorization key
113 if (jcr->sd_auth_key == NULL) {
114 jcr->sd_auth_key = bstrdup("dummy");
116 fd->fsend(jobcmd, edit_int64(jcr->JobId, ed1), jcr->Job, jcr->VolSessionId,
117 jcr->VolSessionTime, jcr->sd_auth_key);
118 if (!jcr->keep_sd_auth_key && strcmp(jcr->sd_auth_key, "dummy")) {
119 memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
121 Dmsg1(100, ">filed: %s", fd->msg);
122 if (bget_dirmsg(fd) > 0) {
123 Dmsg1(110, "<filed: %s", fd->msg);
124 if (strncmp(fd->msg, OKjob, strlen(OKjob)) != 0) {
125 Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"),
126 jcr->client->hdr.name, fd->msg);
127 jcr->setJobStatus(JS_ErrorTerminated);
129 } else if (jcr->db) {
131 memset(&cr, 0, sizeof(cr));
132 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
133 cr.AutoPrune = jcr->client->AutoPrune;
134 cr.FileRetention = jcr->client->FileRetention;
135 cr.JobRetention = jcr->client->JobRetention;
136 bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
137 if (!db_update_client_record(jcr, jcr->db, &cr)) {
138 Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"),
139 db_strerror(jcr->db));
143 Jmsg(jcr, M_FATAL, 0, _("FD gave bad response to JobId command: %s\n"),
145 jcr->setJobStatus(JS_ErrorTerminated);
152 * This subroutine edits the last job start time into a
153 * "since=date/time" buffer that is returned in the
154 * variable since. This is used for display purposes in
155 * the job report. The time in jcr->stime is later
156 * passed to tell the File daemon what to do.
158 void get_level_since_time(JCR *jcr, char *since, int since_len)
162 bool do_full = false;
163 bool do_diff = false;
165 utime_t last_full_time = 0;
166 utime_t last_diff_time;
167 char prev_job[MAX_NAME_LENGTH];
170 /* If job cloned and a since time already given, use it */
171 if (jcr->cloned && jcr->stime && jcr->stime[0]) {
172 bstrncpy(since, _(", since="), since_len);
173 bstrncat(since, jcr->stime, since_len);
176 /* Make sure stime buffer is allocated */
178 jcr->stime = get_pool_memory(PM_MESSAGE);
180 jcr->PrevJob[0] = jcr->stime[0] = 0;
182 * Lookup the last FULL backup job to get the time/date for a
183 * differential or incremental save.
185 switch (jcr->getJobLevel()) {
188 POOLMEM *stime = get_pool_memory(PM_MESSAGE);
189 /* Look up start time of last Full job */
190 now = (utime_t)time(NULL);
191 jcr->jr.JobId = 0; /* flag to return since time */
193 * This is probably redundant, but some of the code below
194 * uses jcr->stime, so don't remove unless you are sure.
196 if (!db_find_job_start_time(jcr,jcr->db, &jcr->jr, &jcr->stime, jcr->PrevJob)) {
199 have_full = db_find_last_job_start_time(jcr, jcr->db, &jcr->jr,
200 &stime, prev_job, L_FULL);
202 last_full_time = str_to_utime(stime);
204 do_full = true; /* No full, upgrade to one */
206 Dmsg4(50, "have_full=%d do_full=%d now=%lld full_time=%lld\n", have_full,
207 do_full, now, last_full_time);
208 /* Make sure the last diff is recent enough */
209 if (have_full && jcr->getJobLevel() == L_INCREMENTAL && jcr->job->MaxDiffInterval > 0) {
210 /* Lookup last diff job */
211 if (db_find_last_job_start_time(jcr, jcr->db, &jcr->jr,
212 &stime, prev_job, L_DIFFERENTIAL)) {
213 last_diff_time = str_to_utime(stime);
214 /* If no Diff since Full, use Full time */
215 if (last_diff_time < last_full_time) {
216 last_diff_time = last_full_time;
218 Dmsg2(50, "last_diff_time=%lld last_full_time=%lld\n", last_diff_time,
221 /* No last differential, so use last full time */
222 last_diff_time = last_full_time;
223 Dmsg1(50, "No last_diff_time setting to full_time=%lld\n", last_full_time);
225 do_diff = ((now - last_diff_time) >= jcr->job->MaxDiffInterval);
226 Dmsg2(50, "do_diff=%d diffInter=%lld\n", do_diff, jcr->job->MaxDiffInterval);
228 /* Note, do_full takes precedence over do_diff */
229 if (have_full && jcr->job->MaxFullInterval > 0) {
230 do_full = ((now - last_full_time) >= jcr->job->MaxFullInterval);
232 free_pool_memory(stime);
235 /* No recent Full job found, so upgrade this one to Full */
236 Jmsg(jcr, M_INFO, 0, "%s", db_strerror(jcr->db));
237 Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found in catalog. Doing FULL backup.\n"));
238 bsnprintf(since, since_len, _(" (upgraded from %s)"),
239 level_to_str(jcr->getJobLevel()));
240 jcr->setJobLevel(jcr->jr.JobLevel = L_FULL);
241 } else if (do_diff) {
242 /* No recent diff job found, so upgrade this one to Diff */
243 Jmsg(jcr, M_INFO, 0, _("No prior or suitable Differential backup found in catalog. Doing Differential backup.\n"));
244 bsnprintf(since, since_len, _(" (upgraded from %s)"),
245 level_to_str(jcr->getJobLevel()));
246 jcr->setJobLevel(jcr->jr.JobLevel = L_DIFFERENTIAL);
248 if (jcr->job->rerun_failed_levels) {
249 if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr,
250 jcr->stime, JobLevel)) {
251 Jmsg(jcr, M_INFO, 0, _("Prior failed job found in catalog. Upgrading to %s.\n"),
252 level_to_str(JobLevel));
253 bsnprintf(since, since_len, _(" (upgraded from %s)"),
254 level_to_str(jcr->getJobLevel()));
255 jcr->setJobLevel(jcr->jr.JobLevel = JobLevel);
256 jcr->jr.JobId = jcr->JobId;
260 bstrncpy(since, _(", since="), since_len);
261 bstrncat(since, jcr->stime, since_len);
263 jcr->jr.JobId = jcr->JobId;
266 Dmsg3(100, "Level=%c last start time=%s job=%s\n",
267 jcr->getJobLevel(), jcr->stime, jcr->PrevJob);
270 static void send_since_time(JCR *jcr)
272 BSOCK *fd = jcr->file_bsock;
276 stime = str_to_utime(jcr->stime);
277 fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0,
278 NT_("prev_job="), jcr->PrevJob);
279 while (bget_dirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */
280 Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
284 bool send_bwlimit(JCR *jcr, const char *Job)
286 BSOCK *fd = jcr->file_bsock;
287 if (jcr->FDVersion >= 4) {
288 fd->fsend(bandwidthcmd, jcr->max_bandwidth, Job);
289 if (!response(jcr, fd, OKBandwidth, "Bandwidth", DISPLAY_ERROR)) {
290 jcr->max_bandwidth = 0; /* can't set bandwidth limit */
298 * Send level command to FD.
299 * Used for backup jobs and estimate command.
301 bool send_level_command(JCR *jcr)
303 BSOCK *fd = jcr->file_bsock;
304 const char *accurate = jcr->accurate?"accurate_":"";
305 const char *not_accurate = "";
306 const char *rerunning = jcr->rerunning?" rerunning ":" ";
308 * Send Level command to File daemon
310 switch (jcr->getJobLevel()) {
312 fd->fsend(levelcmd, not_accurate, "base", rerunning, 0, "", "");
314 /* L_NONE is the console, sending something off to the FD */
317 fd->fsend(levelcmd, not_accurate, "full", rerunning, 0, "", "");
320 fd->fsend(levelcmd, accurate, "differential", rerunning, 0, "", "");
321 send_since_time(jcr);
324 fd->fsend(levelcmd, accurate, "incremental", rerunning, 0, "", "");
325 send_since_time(jcr);
329 Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"),
330 jcr->getJobLevel(), jcr->getJobLevel());
333 Dmsg1(120, ">filed: %s", fd->msg);
334 if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
341 * Send either an Included or an Excluded list to FD
343 static bool send_fileset(JCR *jcr)
345 FILESET *fileset = jcr->fileset;
346 BSOCK *fd = jcr->file_bsock;
347 STORE *store = jcr->wstore;
353 num = fileset->num_includes;
355 num = fileset->num_excludes;
357 for (int i=0; i<num; i++) {
363 ie = fileset->include_items[i];
366 ie = fileset->exclude_items[i];
370 fd->fsend("Z %s\n", ie->ignoredir);
372 for (j=0; j<ie->num_opts; j++) {
373 FOPTS *fo = ie->opts_list[j];
375 bool enhanced_wild = false;
376 for (k=0; fo->opts[k]!='\0'; k++) {
377 if (fo->opts[k]=='W') {
378 enhanced_wild = true;
383 /* Strip out compression option Zn if disallowed for this Storage */
384 if (store && !store->AllowCompress) {
385 char newopts[MAX_FOPTS];
386 bool done=false; /* print warning only if compression enabled in FS */
388 for (k=0; fo->opts[k]!='\0'; k++) {
389 /* Z compress option is followed by the single-digit compress level or 'o' */
390 if (fo->opts[k]=='Z') {
392 k++; /* skip option and level */
394 newopts[j] = fo->opts[k];
402 _("FD compression disabled for this Job because AllowCompress=No in Storage resource.\n") );
404 /* Send the new trimmed option set without overwriting fo->opts */
405 fd->fsend("O %s\n", newopts);
407 /* Send the original options */
408 fd->fsend("O %s\n", fo->opts);
411 for (k=0; k<fo->regex.size(); k++) {
412 fd->fsend("R %s\n", fo->regex.get(k));
414 for (k=0; k<fo->regexdir.size(); k++) {
415 fd->fsend("RD %s\n", fo->regexdir.get(k));
417 for (k=0; k<fo->regexfile.size(); k++) {
418 fd->fsend("RF %s\n", fo->regexfile.get(k));
420 for (k=0; k<fo->wild.size(); k++) {
421 fd->fsend("W %s\n", fo->wild.get(k));
423 for (k=0; k<fo->wilddir.size(); k++) {
424 fd->fsend("WD %s\n", fo->wilddir.get(k));
426 for (k=0; k<fo->wildfile.size(); k++) {
427 fd->fsend("WF %s\n", fo->wildfile.get(k));
429 for (k=0; k<fo->wildbase.size(); k++) {
430 fd->fsend("W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
432 for (k=0; k<fo->base.size(); k++) {
433 fd->fsend("B %s\n", fo->base.get(k));
435 for (k=0; k<fo->fstype.size(); k++) {
436 fd->fsend("X %s\n", fo->fstype.get(k));
438 for (k=0; k<fo->drivetype.size(); k++) {
439 fd->fsend("XD %s\n", fo->drivetype.get(k));
442 fd->fsend("G %s\n", fo->plugin);
445 fd->fsend("D %s\n", fo->reader);
448 fd->fsend("T %s\n", fo->writer);
453 for (j=0; j<ie->name_list.size(); j++) {
454 item = (char *)ie->name_list.get(j);
455 if (!send_list_item(jcr, "F ", item, fd)) {
460 for (j=0; j<ie->plugin_list.size(); j++) {
461 item = (char *)ie->plugin_list.get(j);
462 if (!send_list_item(jcr, "P ", item, fd)) {
468 if (!include) { /* If we just did excludes */
469 break; /* all done */
471 include = false; /* Now do excludes */
474 fd->signal(BNET_EOD); /* end of data */
475 if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
481 jcr->setJobStatus(JS_ErrorTerminated);
486 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd)
496 p++; /* skip over the | */
497 fd->msg = edit_job_codes(jcr, fd->msg, p, "");
498 bpipe = open_bpipe(fd->msg, 0, "r");
501 Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
505 bstrncpy(buf, code, sizeof(buf));
506 Dmsg1(500, "code=%s\n", buf);
507 optlen = strlen(buf);
508 while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
509 fd->msglen = Mmsg(fd->msg, "%s", buf);
510 Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
512 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
516 if ((stat=close_bpipe(bpipe)) != 0) {
518 Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
519 p, be.bstrerror(stat));
524 p++; /* skip over < */
525 if ((ffd = fopen(p, "rb")) == NULL) {
527 Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
531 bstrncpy(buf, code, sizeof(buf));
532 Dmsg1(500, "code=%s\n", buf);
533 optlen = strlen(buf);
534 while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
535 fd->msglen = Mmsg(fd->msg, "%s", buf);
537 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
544 p++; /* skip over \ */
545 /* Note, fall through wanted */
547 pm_strcpy(fd->msg, code);
548 fd->msglen = pm_strcat(fd->msg, p);
549 Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
551 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
561 * Send include list to File daemon
563 bool send_include_list(JCR *jcr)
565 BSOCK *fd = jcr->file_bsock;
566 if (jcr->fileset->new_include) {
567 fd->fsend(filesetcmd, jcr->fileset->enable_vss ? " vss=1" : "");
568 return send_fileset(jcr);
574 * Send exclude list to File daemon
575 * Under the new scheme, the Exclude list
576 * is part of the FileSet sent with the
577 * "include_list" above.
579 bool send_exclude_list(JCR *jcr)
584 /* TODO: drop this with runscript.old_proto in bacula 1.42 */
585 static char runbefore[] = "RunBeforeJob %s\n";
586 static char runafter[] = "RunAfterJob %s\n";
587 static char OKRunBefore[] = "2000 OK RunBefore\n";
588 static char OKRunAfter[] = "2000 OK RunAfter\n";
590 int send_runscript_with_old_proto(JCR *jcr, int when, POOLMEM *msg)
593 Dmsg1(120, "bdird: sending old runcommand to fd '%s'\n",msg);
594 if (when & SCRIPT_Before) {
595 jcr->file_bsock->fsend(runbefore, msg);
596 ret = response(jcr, jcr->file_bsock, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR);
598 jcr->file_bsock->fsend(runafter, msg);
599 ret = response(jcr, jcr->file_bsock, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR);
605 * Send RunScripts to File daemon
606 * 1) We send all runscript to FD, they can be executed Before, After, or twice
607 * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
608 * first run_script() call. (ie ClientRunBeforeJob)
610 int send_runscripts_commands(JCR *jcr)
612 POOLMEM *msg = get_pool_memory(PM_FNAME);
613 BSOCK *fd = jcr->file_bsock;
615 bool launch_before_cmd = false;
616 POOLMEM *ehost = get_pool_memory(PM_FNAME);
619 Dmsg0(120, "bdird: sending runscripts to fd\n");
621 foreach_alist(cmd, jcr->job->RunScripts) {
622 if (cmd->can_run_at_level(jcr->getJobLevel()) && cmd->target) {
623 ehost = edit_job_codes(jcr, ehost, cmd->target, "");
624 Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
626 if (strcmp(ehost, jcr->client->name()) == 0) {
627 pm_strcpy(msg, cmd->command);
630 Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
632 /* TODO: remove this with bacula 1.42 */
633 if (cmd->old_proto) {
634 result = send_runscript_with_old_proto(jcr, cmd->when, msg);
637 fd->fsend(runscript, cmd->on_success,
643 result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
644 launch_before_cmd = true;
651 /* TODO : we have to play with other client */
654 send command to an other client
660 /* Tell the FD to execute the ClientRunBeforeJob */
661 if (launch_before_cmd) {
662 fd->fsend(runbeforenow);
663 if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
667 free_pool_memory(msg);
668 free_pool_memory(ehost);
672 Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" RunScript failed.\n"), ehost);
673 free_pool_memory(msg);
674 free_pool_memory(ehost);
683 static int restore_object_handler(void *ctx, int num_fields, char **row)
685 OBJ_CTX *octx = (OBJ_CTX *)ctx;
686 JCR *jcr = octx->jcr;
689 fd = jcr->file_bsock;
690 if (jcr->is_job_canceled()) {
693 /* Old File Daemon doesn't handle restore objects */
694 if (jcr->FDVersion < 3) {
695 Jmsg(jcr, M_WARNING, 0, _("Client \"%s\" may not be used to restore "
696 "this job. Please upgrade your client.\n"),
697 jcr->client->name());
701 if (jcr->FDVersion < 5) { /* Old version without PluginName */
702 fd->fsend("restoreobject JobId=%s %s,%s,%s,%s,%s,%s\n",
703 row[0], row[1], row[2], row[3], row[4], row[5], row[6]);
705 /* bash spaces from PluginName */
707 fd->fsend("restoreobject JobId=%s %s,%s,%s,%s,%s,%s,%s\n",
708 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[9]);
710 Dmsg1(010, "Send obj hdr=%s", fd->msg);
712 fd->msglen = pm_strcpy(fd->msg, row[7]);
713 fd->send(); /* send Object name */
715 Dmsg1(010, "Send obj: %s\n", fd->msg);
717 // fd->msglen = str_to_uint64(row[1]); /* object length */
718 // Dmsg1(000, "obj size: %lld\n", (uint64_t)fd->msglen);
721 db_unescape_object(jcr, jcr->db,
723 str_to_uint64(row[1]), /* Object length */
724 &fd->msg, &fd->msglen);
725 fd->send(); /* send object */
728 if (debug_level > 100) {
729 for (int i=0; i < fd->msglen; i++)
732 Dmsg1(000, "Send obj: %s\n", fd->msg);
739 * Send the plugin Restore Objects, which allow the
740 * plugin to get information early in the restore
741 * process. The RestoreObjects were created during
742 * the backup by the plugin.
744 bool send_restore_objects(JCR *jcr)
747 POOL_MEM query(PM_MESSAGE);
751 if (!jcr->JobIds || !jcr->JobIds[0]) {
757 /* restore_object_handler is called for each file found */
759 /* send restore objects for all jobs involved */
760 Mmsg(query, get_restore_objects, jcr->JobIds, FT_RESTORE_FIRST);
761 db_sql_query(jcr->db, query.c_str(), restore_object_handler, (void *)&octx);
763 /* send config objects for the current restore job */
764 Mmsg(query, get_restore_objects,
765 edit_uint64(jcr->JobId, ed1), FT_PLUGIN_CONFIG_FILLED);
766 db_sql_query(jcr->db, query.c_str(), restore_object_handler, (void *)&octx);
769 * Send to FD only if we have at least one restore object.
770 * This permits backward compatibility with older FDs.
772 if (octx.count > 0) {
773 fd = jcr->file_bsock;
774 fd->fsend("restoreobject end\n");
775 if (!response(jcr, fd, OKRestoreObject, "RestoreObject", DISPLAY_ERROR)) {
776 Jmsg(jcr, M_FATAL, 0, _("RestoreObject failed.\n"));
784 * Read the attributes from the File daemon for
785 * a Verify job and store them in the catalog.
787 int get_attributes_and_put_in_catalog(JCR *jcr)
792 char digest[MAXSTRING];
794 fd = jcr->file_bsock;
795 jcr->jr.FirstIndex = 1;
797 /* Start transaction allocates jcr->attr and jcr->ar if needed */
798 db_start_transaction(jcr, jcr->db); /* start transaction if not already open */
801 Dmsg0(120, "bdird: waiting to receive file attributes\n");
802 /* Pickup file attributes and digest */
803 while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
807 char Digest[MAXSTRING]; /* either Verify opts or MD5/SHA1 digest */
809 /* Stop here if canceled */
810 if (jcr->is_job_canceled()) {
811 jcr->cached_attribute = false;
815 if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Digest)) != 3) {
816 Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
817 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
818 jcr->setJobStatus(JS_ErrorTerminated);
819 jcr->cached_attribute = false;
823 /* The following three fields were sscanf'ed above so skip them */
824 skip_nonspaces(&p); /* skip FileIndex */
826 skip_nonspaces(&p); /* skip Stream */
828 skip_nonspaces(&p); /* skip Opts_Digest */
829 p++; /* skip space */
830 Dmsg1(dbglvl, "Stream=%d\n", stream);
831 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
832 if (jcr->cached_attribute) {
833 Dmsg3(dbglvl, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname,
835 if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
836 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
838 jcr->cached_attribute = false;
840 /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
841 fn = jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
843 *fn++ = *p++; /* copy filename */
845 *fn = *p++; /* term filename and point p to attribs */
846 pm_strcpy(jcr->attr, p); /* save attributes */
848 jcr->FileIndex = file_index;
849 ar->attr = jcr->attr;
850 ar->fname = jcr->fname;
851 ar->FileIndex = file_index;
854 ar->JobId = jcr->JobId;
855 ar->ClientId = jcr->ClientId;
858 ar->DigestType = CRYPTO_DIGEST_NONE;
860 jcr->cached_attribute = true;
862 Dmsg2(dbglvl, "dird<filed: stream=%d %s\n", stream, jcr->fname);
863 Dmsg1(dbglvl, "dird<filed: attr=%s\n", ar->attr);
864 jcr->FileId = ar->FileId;
866 * First, get STREAM_UNIX_ATTRIBUTES and fill ATTR_DBR structure
867 * Next, we CAN have a CRYPTO_DIGEST, so we fill ATTR_DBR with it (or not)
868 * When we get a new STREAM_UNIX_ATTRIBUTES, we known that we can add file to the catalog
869 * At the end, we have to add the last file
871 } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
872 if (jcr->FileIndex != (uint32_t)file_index) {
873 Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
874 stream_to_ascii(stream), file_index, jcr->FileIndex);
878 ar->DigestType = crypto_digest_stream_type(stream);
879 db_escape_string(jcr, jcr->db, digest, Digest, strlen(Digest));
880 Dmsg4(dbglvl, "stream=%d DigestLen=%d Digest=%s type=%d\n", stream,
881 strlen(digest), digest, ar->DigestType);
883 jcr->jr.JobFiles = jcr->JobFiles = file_index;
884 jcr->jr.LastIndex = file_index;
886 if (fd->is_error()) {
887 Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
889 jcr->cached_attribute = false;
892 if (jcr->cached_attribute) {
893 Dmsg3(dbglvl, "Cached attr with digest. Stream=%d fname=%s attr=%s\n", ar->Stream,
894 ar->fname, ar->attr);
895 if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
896 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
898 jcr->cached_attribute = false;
900 jcr->setJobStatus(JS_Terminated);