1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
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.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 *
21 * Bacula Director -- backup.c -- responsible for doing backup jobs
22 *
23 * Kern Sibbald, March MM
24 *
25 * Basic tasks done here:
26 * Open DB and create records for this job.
27 * Open Message Channel with Storage daemon to tell him a job will be starting.
28 * Open connection with File daemon and pass him commands
29 * to do the backup.
30 * When the File daemon finishes the job, update the DB.
31 *
32 */
33
34 #include "bacula.h"
35 #include "dird.h"
36 #include "ua.h"
37
38
39 /* Commands sent to File daemon */
40 static char backupcmd[] = "backup FileIndex=%ld\n";
41 static char storaddr[] = "storage address=%s port=%d ssl=%d\n";
42
43 /* Responses received from File daemon */
44 static char OKbackup[] = "2000 OK backup\n";
45 static char OKstore[] = "2000 OK storage\n";
46 /* After 17 Aug 2013 */
47 static char newEndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
48 "ReadBytes=%llu JobBytes=%llu Errors=%u "
49 "VSS=%d Encrypt=%d "
50 "CommBytes=%lld CompressCommBytes=%lld\n";
51 /* Pre 17 Aug 2013 */
52 static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
53 "ReadBytes=%llu JobBytes=%llu Errors=%u "
54 "VSS=%d Encrypt=%d\n";
55 /* Pre 1.39.29 (04Dec06) EndJob */
56 static char OldEndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
57 "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
58
59 /* Commands sent to Storage daemon */
60 static char clientaddr[] = "client address=%s port=%d ssl=%d\n";
61
62 /* Commands received from Storage daemon */
63 static char OKclient[] = "3000 OK client command\n";
64
65 /*
66 * Called here before the job is run to do the job
67 * specific setup.
68 */
do_backup_init(JCR * jcr)69 bool do_backup_init(JCR *jcr)
70 {
71 /* Make local copy */
72 jcr->RescheduleIncompleteJobs = jcr->job->RescheduleIncompleteJobs;
73
74 if (!get_or_create_fileset_record(jcr)) {
75 Dmsg1(100, "JobId=%d no FileSet\n", (int)jcr->JobId);
76 return false;
77 }
78
79 /*
80 * Get definitive Job level and since time
81 * unless it's a virtual full. In that case
82 * it is not needed.
83 */
84 if (!jcr->is_JobLevel(L_VIRTUAL_FULL)) {
85 get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
86 }
87
88 apply_pool_overrides(jcr);
89
90 if (!allow_duplicate_job(jcr)) {
91 return false;
92 }
93
94 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
95 if (jcr->jr.PoolId == 0) {
96 Dmsg1(100, "JobId=%d no PoolId\n", (int)jcr->JobId);
97 Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
98 return false;
99 }
100
101 /*
102 * If we are a virtual full job or got upgraded to one
103 * then we divert at this point and call the virtual full
104 * backup init method
105 */
106 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
107 return do_vbackup_init(jcr);
108 }
109
110 free_rstorage(jcr); /* we don't read so release */
111
112 /* If pool storage specified, use it instead of job storage */
113 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
114
115 if (!jcr->wstorage) {
116 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
117 return false;
118 }
119
120 create_clones(jcr); /* run any clone jobs */
121
122 return true;
123 }
124
125 /* Take all base jobs from job resource and find the
126 * last L_BASE jobid.
127 */
get_base_jobids(JCR * jcr,db_list_ctx * jobids)128 static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids)
129 {
130 JOB_DBR jr;
131 JOB *job;
132 JobId_t id;
133 char str_jobid[50];
134
135 if (!jcr->job->base) {
136 return false; /* no base job, stop accurate */
137 }
138
139 memset(&jr, 0, sizeof(JOB_DBR));
140 jr.StartTime = jcr->jr.StartTime;
141
142 foreach_alist(job, jcr->job->base) {
143 bstrncpy(jr.Name, job->name(), sizeof(jr.Name));
144 db_get_base_jobid(jcr, jcr->db, &jr, &id);
145
146 if (id) {
147 if (jobids->count) {
148 pm_strcat(jobids->list, ",");
149 }
150 pm_strcat(jobids->list, edit_uint64(id, str_jobid));
151 jobids->count++;
152 }
153 }
154
155 return jobids->count > 0;
156 }
157
158 /*
159 * Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
160 * row[0]=Path, row[1]=Filename, row[2]=FileIndex
161 * row[3]=JobId row[4]=LStat row[5]=DeltaSeq row[6]=MD5
162 */
accurate_list_handler(void * ctx,int num_fields,char ** row)163 static int accurate_list_handler(void *ctx, int num_fields, char **row)
164 {
165 JCR *jcr = (JCR *)ctx;
166
167 if (job_canceled(jcr)) {
168 return 1;
169 }
170
171 if (row[2][0] == '0') { /* discard when file_index == 0 */
172 return 0;
173 }
174
175 /* sending with checksum */
176 if (jcr->use_accurate_chksum
177 && num_fields == 7
178 && row[6][0] /* skip checksum = '0' */
179 && row[6][1])
180 {
181 jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s",
182 row[0], row[1], 0, row[4], 0, row[6], 0, row[5]);
183 } else {
184 jcr->file_bsock->fsend("%s%s%c%s%c%c%s",
185 row[0], row[1], 0, row[4], 0, 0, row[5]);
186 }
187 return 0;
188 }
189
190 /* In this procedure, we check if the current fileset is using checksum
191 * FileSet-> Include-> Options-> Accurate/Verify/BaseJob=checksum
192 * This procedure uses jcr->HasBase, so it must be call after the initialization
193 */
is_checksum_needed_by_fileset(JCR * jcr)194 static bool is_checksum_needed_by_fileset(JCR *jcr)
195 {
196 FILESET *f;
197 INCEXE *inc;
198 FOPTS *fopts;
199 bool in_block=false;
200 bool have_basejob_option=false;
201 if (!jcr->job || !jcr->job->fileset) {
202 return false;
203 }
204
205 f = jcr->job->fileset;
206
207 for (int i=0; i < f->num_includes; i++) { /* Parse all Include {} */
208 inc = f->include_items[i];
209
210 for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
211 fopts = inc->opts_list[j];
212
213 for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
214 switch (*k) {
215 case 'V': /* verify */
216 in_block = (jcr->getJobType() == JT_VERIFY); /* not used now */
217 break;
218 case 'J': /* Basejob keyword */
219 have_basejob_option = in_block = jcr->HasBase;
220 break;
221 case 'C': /* Accurate keyword */
222 in_block = !jcr->is_JobLevel(L_FULL);
223 break;
224 case ':': /* End of keyword */
225 in_block = false;
226 break;
227 case '5': /* MD5 */
228 case '1': /* SHA1 */
229 if (in_block) {
230 Dmsg0(50, "Checksum will be sent to FD\n");
231 return true;
232 }
233 break;
234 default:
235 break;
236 }
237 }
238 }
239 }
240
241 /* By default for BaseJobs, we send the checksum */
242 if (!have_basejob_option && jcr->HasBase) {
243 return true;
244 }
245
246 Dmsg0(50, "Checksum will be sent to FD\n");
247 return false;
248 }
249
250 /*
251 * Send current file list to FD
252 * DIR -> FD : accurate files=xxxx
253 * DIR -> FD : /path/to/file\0Lstat\0MD5\0Delta
254 * DIR -> FD : /path/to/dir/\0Lstat\0MD5\0Delta
255 * ...
256 * DIR -> FD : EOD
257 */
send_accurate_current_files(JCR * jcr)258 bool send_accurate_current_files(JCR *jcr)
259 {
260 POOL_MEM buf;
261 db_list_ctx jobids;
262 db_list_ctx nb;
263 char ed1[50];
264
265 /* In base level, no previous job is used and no restart incomplete jobs */
266 if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
267 return true;
268 }
269 if (!jcr->accurate && !jcr->rerunning) {
270 return true;
271 }
272
273 if (jcr->is_JobLevel(L_FULL)) {
274 /* On Full mode, if no previous base job, no accurate things */
275 if (get_base_jobids(jcr, &jobids)) {
276 jcr->HasBase = true;
277 Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
278 } else if (!jcr->rerunning) {
279 return true;
280 }
281
282 } else if (jcr->is_JobLevel(L_VERIFY_DATA)) {
283 char ed1[50];
284 jobids.add(edit_uint64(jcr->previous_jr.JobId, ed1));
285
286 } else {
287 /* For Incr/Diff level, we search for older jobs */
288 db_get_accurate_jobids(jcr, jcr->db, &jcr->jr, &jobids);
289
290 /* We are in Incr/Diff, but no Full to build the accurate list... */
291 if (jobids.count == 0) {
292 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
293 return false; /* fail */
294 }
295 }
296
297 /* For incomplete Jobs, we add our own id */
298 if (jcr->rerunning) {
299 edit_int64(jcr->JobId, ed1);
300 jobids.add(ed1);
301 }
302
303 /* Don't send and store the checksum if fileset doesn't require it */
304 jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
305
306 if (jcr->JobId) { /* display the message only for real jobs */
307 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information to the FD.\n"));
308 }
309
310 /* to be able to allocate the right size for htable */
311 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
312 db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
313 Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
314 jcr->file_bsock->fsend("accurate files=%s\n", nb.list);
315
316 if (!db_open_batch_connexion(jcr, jcr->db)) {
317 Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
318 return false; /* Fail */
319 }
320
321 if (jcr->HasBase) {
322 jcr->nb_base_files = str_to_int64(nb.list);
323 if (!db_create_base_file_list(jcr, jcr->db, jobids.list)) {
324 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
325 return false;
326 }
327 if (!db_get_base_file_list(jcr, jcr->db, jcr->use_accurate_chksum,
328 accurate_list_handler, (void *)jcr)) {
329 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
330 return false;
331 }
332
333 } else {
334 int opts = jcr->use_accurate_chksum ? DBL_USE_MD5 : DBL_NONE;
335 if (!db_get_file_list(jcr, jcr->db_batch,
336 jobids.list, opts,
337 accurate_list_handler, (void *)jcr)) {
338 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db_batch));
339 return false;
340 }
341 }
342
343 /* TODO: close the batch connection ? (can be used very soon) */
344 jcr->file_bsock->signal(BNET_EOD);
345 return true;
346 }
347
send_store_addr_to_fd(JCR * jcr,STORE * store,char * store_address,uint32_t store_port)348 bool send_store_addr_to_fd(JCR *jcr, STORE *store,
349 char *store_address, uint32_t store_port)
350 {
351 int tls_need = BNET_TLS_NONE;
352
353 /* TLS Requirement */
354 if (store->tls_enable) {
355 if (store->tls_require) {
356 tls_need = BNET_TLS_REQUIRED;
357 } else {
358 tls_need = BNET_TLS_OK;
359 }
360 }
361
362 /*
363 * Send Storage address to the FD
364 */
365 jcr->file_bsock->fsend(storaddr, store_address, store_port, tls_need);
366 if (!response(jcr, jcr->file_bsock, OKstore, "Storage", DISPLAY_ERROR)) {
367 return false;
368 }
369 return true;
370 }
371
send_client_addr_to_sd(JCR * jcr)372 bool send_client_addr_to_sd(JCR *jcr)
373 {
374 int tls_need = BNET_TLS_NONE;
375 BSOCK *sd = jcr->store_bsock;
376 POOL_MEM buf;
377
378 /* TLS Requirement for the client */
379 if (jcr->client->tls_enable) {
380 if (jcr->client->tls_require) {
381 tls_need = BNET_TLS_REQUIRED;
382 } else {
383 tls_need = BNET_TLS_OK;
384 }
385 }
386 /* ATTN tls_need is not used on the other side !!!!!!!!! */
387 /*
388 * Send Client address to the SD
389 */
390 sd->fsend(clientaddr, get_client_address(jcr, jcr->client, buf.addr()), jcr->client->FDport, tls_need);
391 if (!response(jcr, sd, OKclient, "Client", DISPLAY_ERROR)) {
392 return false;
393 }
394 return true;
395 }
396
397 /*
398 * Allow to specify the address used by the Client to
399 * connect to the storage daemon in the Client resource
400 * or in the Storage resource.
401 */
get_storage_address(CLIENT * client,STORE * store)402 char *get_storage_address(CLIENT *client, STORE *store)
403 {
404 char *store_address;
405
406 if (client && client->fd_storage_address) {
407 Dmsg0(10, "Using Client resource FD Storage Address to contact the Storage\n");
408 store_address = client->fd_storage_address;
409
410 } else if (store->fd_storage_address) {
411 Dmsg0(10, "Using Storage resource FD Storage Address to contact the Storage\n");
412 store_address = store->fd_storage_address;
413
414 } else {
415 Dmsg0(10, "Using default Storage address\n");
416 store_address = store->address;
417 }
418 return store_address;
419 }
420
run_storage_and_start_message_thread(JCR * jcr,BSOCK * sd)421 bool run_storage_and_start_message_thread(JCR *jcr, BSOCK *sd)
422 {
423 /*
424 * Start the job prior to starting the message thread below
425 * to avoid two threads from using the BSOCK structure at
426 * the same time.
427 */
428 if (!sd->fsend("run")) {
429 return false;
430 }
431
432 /*
433 * Now start a Storage daemon message thread. Note,
434 * this thread is used to provide the catalog services
435 * for the backup job, including inserting the attributes
436 * into the catalog. See catalog_update() in catreq.c
437 */
438 if (!start_storage_daemon_message_thread(jcr)) {
439 return false;
440 }
441 Dmsg0(150, "Storage daemon connection OK\n");
442 return true;
443 }
444
445 /*
446 * Do a backup of the specified FileSet
447 *
448 * Returns: false on failure
449 * true on success
450 */
do_backup(JCR * jcr)451 bool do_backup(JCR *jcr)
452 {
453 int stat;
454 BSOCK *fd, *sd;
455 STORE *store;
456 char *store_address;
457 uint32_t store_port;
458 char ed1[100];
459 db_int64_ctx job, first, last;
460 int64_t val=0;
461 POOL_MEM buf;
462
463 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
464 return do_vbackup(jcr);
465 }
466
467 /* Print Job Start message */
468 if (jcr->rerunning) {
469 Jmsg(jcr, M_INFO, 0, _("Restart Incomplete Backup JobId %s, Job=%s\n"),
470 edit_uint64(jcr->JobId, ed1), jcr->Job);
471 } else {
472 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
473 edit_uint64(jcr->JobId, ed1), jcr->Job);
474 }
475
476 jcr->setJobStatus(JS_Running);
477 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
478 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
479 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
480 return false;
481 }
482
483 /* For incomplete Jobs, we add our own id */
484 if (jcr->rerunning) {
485 edit_int64(jcr->JobId, ed1);
486 Mmsg(buf, "SELECT max(FileIndex) FROM File WHERE JobId=%s", ed1);
487 if (db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
488 Jmsg(jcr, M_INFO, 0, _("Found %ld files from prior incomplete Job.\n"),
489 (int32_t)job.value);
490 } else {
491 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
492 return false;
493 }
494 Mmsg(buf, "SELECT max(LastIndex) FROM JobMedia WHERE JobId=%s", ed1);
495 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &last)) {
496 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
497 return false;
498 }
499 Mmsg(buf, "SELECT max(FirstIndex) FROM JobMedia WHERE JobId=%s", ed1);
500 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &first)) {
501 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
502 return false;
503 }
504 /* We skip the last FileIndex (MAX) and the one after (MAX+1), can be
505 * already referenced in JobMedia or a Volume
506 */
507 val = MAX(job.value, MAX(first.value, last.value));
508 jcr->JobFiles = val + 2;
509 Dmsg1(100, "==== FI=%ld\n", jcr->JobFiles);
510 Mmsg(buf, "SELECT VolSessionId FROM Job WHERE JobId=%s", ed1);
511 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
512 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
513 return false;
514 }
515 jcr->VolSessionId = job.value;
516 Mmsg(buf, "SELECT VolSessionTime FROM Job WHERE JobId=%s", ed1);
517 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
518 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
519 return false;
520 }
521 jcr->VolSessionTime = job.value;
522 Dmsg4(100, "JobId=%s JobFiles=%ld VolSessionId=%ld VolSessionTime=%ld\n", ed1,
523 jcr->JobFiles, jcr->VolSessionId, jcr->VolSessionTime);
524 }
525
526 /*
527 * Open a message channel connection with the Storage
528 * daemon. This is to let him know that our client
529 * will be contacting him for a backup session.
530 *
531 */
532 Dmsg0(110, "Open connection with storage daemon\n");
533 jcr->setJobStatus(JS_WaitSD);
534 /*
535 * Start conversation with Storage daemon
536 */
537 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
538 return false;
539 }
540 /*
541 * Now start a job with the Storage daemon
542 */
543 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
544 return false;
545 }
546 sd = jcr->store_bsock;
547 if (jcr->client) {
548 jcr->sd_calls_client = jcr->client->sd_calls_client;
549 }
550 /*
551 * Note startup sequence of SD/FD is different depending on
552 * whether the SD listens (normal) or the SD calls the FD.
553 */
554 if (!jcr->sd_calls_client) {
555 if (!run_storage_and_start_message_thread(jcr, sd)) {
556 goto bail_out;
557 }
558 }
559 jcr->setJobStatus(JS_WaitFD);
560 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
561 goto bail_out;
562 }
563
564 jcr->setJobStatus(JS_Running);
565 fd = jcr->file_bsock;
566
567 if (!send_level_command(jcr)) {
568 goto bail_out;
569 }
570
571 if (!send_include_list(jcr)) {
572 goto bail_out;
573 }
574
575 if (!send_exclude_list(jcr)) {
576 goto bail_out;
577 }
578
579 /* TODO: See priority with bandwidth parameter */
580 if (jcr->job->max_bandwidth > 0) {
581 jcr->max_bandwidth = jcr->job->max_bandwidth;
582 } else if (jcr->client->max_bandwidth > 0) {
583 jcr->max_bandwidth = jcr->client->max_bandwidth;
584 }
585
586 if (jcr->max_bandwidth > 0) {
587 send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
588 }
589
590 send_snapshot_retention(jcr, jcr->snapshot_retention);
591
592 store = jcr->wstore;
593
594 if (jcr->sd_calls_client) {
595 if (jcr->FDVersion < 10) {
596 Jmsg(jcr, M_FATAL, 0, _("The File daemon does not support SDCallsClient.\n"));
597 goto bail_out;
598 }
599 if (!send_client_addr_to_sd(jcr)) {
600 goto bail_out;
601 }
602
603 if (!run_storage_and_start_message_thread(jcr, sd)) {
604 goto bail_out;
605 }
606
607 store_address = jcr->wstore->address; /* dummy */
608 store_port = 0; /* flag that SD calls FD */
609 } else {
610 /*
611 * send Storage daemon address to the File daemon
612 */
613 if (store->SDDport == 0) {
614 store->SDDport = store->SDport;
615 }
616
617 store_address = get_storage_address(jcr->client, store);
618 store_port = store->SDDport;
619 }
620
621 if (!send_store_addr_to_fd(jcr, store, store_address, store_port)) {
622 goto bail_out;
623 }
624
625 /* Declare the job started to start the MaxRunTime check */
626 jcr->setJobStarted();
627
628 /* Send and run the RunBefore */
629 if (!send_runscripts_commands(jcr)) {
630 goto bail_out;
631 }
632
633 /*
634 * We re-update the job start record so that the start
635 * time is set after the run before job. This avoids
636 * that any files created by the run before job will
637 * be saved twice. They will be backed up in the current
638 * job, but not in the next one unless they are changed.
639 * Without this, they will be backed up in this job and
640 * in the next job run because in that case, their date
641 * is after the start of this run.
642 */
643 jcr->start_time = time(NULL);
644 jcr->jr.StartTime = jcr->start_time;
645 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
646 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
647 }
648
649 /*
650 * If backup is in accurate mode, we send the list of
651 * all files to FD.
652 */
653 if (!send_accurate_current_files(jcr)) {
654 goto bail_out; /* error */
655 }
656
657 /* Send backup command */
658 fd->fsend(backupcmd, jcr->JobFiles);
659 Dmsg1(100, ">filed: %s", fd->msg);
660 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
661 goto bail_out;
662 }
663
664 /* Pickup Job termination data */
665 stat = wait_for_job_termination(jcr);
666
667 flush_file_records(jcr); /* cached attribute + batch insert */
668
669 if (jcr->HasBase) {
670 db_commit_base_file_attributes_record(jcr, jcr->db);
671 /* Any error already printed */
672 }
673
674 if (!jcr->is_canceled() && stat == JS_Terminated) {
675 backup_cleanup(jcr, stat);
676 return true;
677 }
678 return false;
679
680 /* Come here only after starting SD thread */
681 bail_out:
682 jcr->setJobStatus(JS_ErrorTerminated);
683 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
684 /* Cancel SD */
685 wait_for_job_termination(jcr, FDConnectTimeout);
686 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
687 return false;
688 }
689
690
691 /*
692 * Here we wait for the File daemon to signal termination,
693 * then we wait for the Storage daemon. When both
694 * are done, we return the job status.
695 * Also used by restore.c
696 */
wait_for_job_termination(JCR * jcr,int timeout)697 int wait_for_job_termination(JCR *jcr, int timeout)
698 {
699 int32_t n = 0;
700 BSOCK *fd = jcr->file_bsock;
701 bool fd_ok = false;
702 uint32_t JobFiles, JobErrors;
703 uint32_t JobWarnings = 0;
704 uint64_t ReadBytes = 0;
705 uint64_t JobBytes = 0;
706 uint64_t CommBytes = 0;
707 uint64_t CommCompressedBytes = 0;
708 int VSS = 0; /* or Snapshot on Unix */
709 int Encrypt = 0;
710 btimer_t *tid=NULL;
711
712 if (fd) {
713 if (timeout) {
714 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
715 }
716 /* Wait for Client to terminate */
717 while ((n = bget_dirmsg(fd)) >= 0) {
718 if (!fd_ok &&
719 (sscanf(fd->msg, newEndJob, &jcr->FDJobStatus, &JobFiles,
720 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt,
721 &CommBytes, &CommCompressedBytes) == 9 ||
722 sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
723 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
724 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
725 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
726 fd_ok = true;
727 jcr->setJobStatus(jcr->FDJobStatus);
728 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
729 } else {
730 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
731 fd->msg);
732 }
733 if (job_canceled(jcr)) {
734 break;
735 }
736 }
737 if (tid) {
738 stop_bsock_timer(tid);
739 }
740
741 if (fd->is_error() && jcr->getJobStatus() != JS_Canceled) {
742 int i = 0;
743 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
744 job_type_to_str(jcr->getJobType()), fd->bstrerror());
745 while (i++ < 20 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
746 bmicrosleep(3, 0);
747 }
748 }
749 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
750 }
751
752 /*
753 * Force cancel in SD if failing, but not for Incomplete jobs
754 * so that we let the SD despool.
755 */
756 Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
757 jcr->JobStatus, jcr->SDJobStatus);
758 if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
759 Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
760 jcr->JobStatus, jcr->SDJobStatus);
761 cancel_storage_daemon_job(jcr);
762 }
763
764 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
765 wait_for_storage_daemon_termination(jcr);
766
767 /* Return values from FD */
768 if (fd_ok) {
769 jcr->JobFiles = JobFiles;
770 jcr->JobErrors += JobErrors; /* Keep total errors */
771 jcr->ReadBytes = ReadBytes;
772 jcr->JobBytes = JobBytes;
773 jcr->JobWarnings = JobWarnings;
774 jcr->CommBytes = CommBytes;
775 jcr->CommCompressedBytes = CommCompressedBytes;
776 jcr->Snapshot = VSS;
777 jcr->Encrypt = Encrypt;
778 } else if (jcr->getJobStatus() != JS_Canceled) {
779 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
780 }
781
782 /* Return the first error status we find Dir, FD, or SD */
783 if (!fd_ok || fd->is_error()) { /* if fd not set, that use !fd_ok */
784 if (jcr->getJobStatus() == JS_Canceled) {
785 jcr->FDJobStatus = JS_Canceled;
786 } else {
787 jcr->FDJobStatus = JS_ErrorTerminated;
788 }
789 }
790 if (jcr->JobStatus != JS_Terminated) {
791 return jcr->JobStatus;
792 }
793 if (jcr->FDJobStatus != JS_Terminated) {
794 return jcr->FDJobStatus;
795 }
796 return jcr->SDJobStatus;
797 }
798
799 /* When the job is incomplete, we need to make sure the catalog
800 * is consistent. The JobMedia table should reference Files that
801 * are not in the file table for example.
802 */
incomplete_cleanup(JCR * jcr)803 void incomplete_cleanup(JCR *jcr)
804 {
805 POOL_MEM buf;
806 char ed1[50], *jmid;
807 bool ok=true;
808 db_int64_ctx job;
809 alist ids(owned_by_alist, 10);
810 alist *pids = &ids;
811
812 if (!jcr->is_incomplete()) {
813 return;
814 }
815
816 edit_int64(jcr->JobId, ed1);
817 /* Get the last valid FileIndex */
818 Mmsg(buf, "SELECT max(FileIndex) FROM File WHERE JobId=%s", ed1);
819 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
820 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
821 return;
822 }
823
824 Mmsg(buf, "SELECT JobMediaId FROM JobMedia "
825 "WHERE JobId=%s "
826 "AND (FirstIndex > %lld OR LastIndex > %lld)",
827 ed1, job.value, job.value);
828 if (!db_sql_query(jcr->db, buf.c_str(), db_string_list_handler, &pids)) {
829 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
830 return;
831 }
832 /* Nothing to fix */
833 if (pids->size() == 0) {
834 return;
835 }
836
837 db_lock(jcr->db);
838 db_start_transaction(jcr, jcr->db);
839
840 /* Foreach id */
841 foreach_alist(jmid, pids) {
842 JOBMEDIA_DBR jmr;
843 memset(&jmr, 0, sizeof(jmr));
844
845 jmr.JobMediaId = str_to_int64(jmid);
846 if (!db_get_jobmedia_record(jcr, jcr->db, &jmr)) {
847 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
848 ok=false;
849 goto bail_out;
850 }
851 if (jmr.FirstIndex > job.value) { /* JobMedia for files not in the catalog */
852 if (jmr.LastIndex > job.value) {
853 Dmsg3(50, "Drop JobMediaId=%d FirstIndex=%lld LastIndex=%lld\n",
854 jmr.JobMediaId, jmr.FirstIndex, jmr.LastIndex);
855
856 } else {
857 Dmsg3(50, "Incorrect JobMediaId=%d FirstIndex=%lld LastIndex=%lld\n",
858 jmr.JobMediaId, jmr.FirstIndex, jmr.LastIndex);
859 }
860 Mmsg(buf, "DELETE FROM JobMedia WHERE JobMediaId=%s", jmid);
861 if (!db_sql_query(jcr->db, buf.c_str(), NULL, NULL)) {
862 ok=false;
863 goto bail_out;
864 }
865 } else if (jmr.LastIndex > job.value) { /* The last index is not in the catalog */
866 Dmsg4(50, "Fix JobMediaId=%d LastIndex=%lld FirstIndex=%lld LastIndex=%lld\n",
867 jmr.JobMediaId, job.value, jmr.FirstIndex, jmr.LastIndex);
868 Mmsg(buf, "UPDATE JobMedia SET LastIndex=%lld WHERE JobMediaId=%s", job.value, jmid);
869 if (!db_sql_query(jcr->db, buf.c_str(), NULL, NULL)) {
870 ok=false;
871 goto bail_out;
872 }
873 } else {
874 Dmsg1(50, "?? JobMedia %d\n", jmr.JobMediaId);
875 }
876 }
877 bail_out:
878 if (!ok) {
879 db_sql_query(jcr->db, "ROLLBACK", NULL, NULL);
880 Jmsg(jcr, M_FATAL, 0, _("Unable to cleanup JobMedia records\n"));
881 }
882 db_end_transaction(jcr, jcr->db);
883 db_unlock(jcr->db);
884 }
885
886 /*
887 * Release resources allocated during backup.
888 */
backup_cleanup(JCR * jcr,int TermCode)889 void backup_cleanup(JCR *jcr, int TermCode)
890 {
891 char sdt[50], edt[50], schedt[50], edl[50];
892 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30];
893 char ec6[30], ec7[30], ec8[30], ec9[30], ec10[30], elapsed[50];
894 char data_compress[200], comm_compress[200];
895 char fd_term_msg[100], sd_term_msg[100];
896 POOL_MEM term_msg;
897 int msg_type = M_INFO;
898 MEDIA_DBR mr;
899 CLIENT_DBR cr;
900 double kbps, compression, ratio;
901 utime_t RunTime;
902 POOL_MEM base_info;
903 POOL_MEM vol_info;
904
905 remove_dummy_jobmedia_records(jcr);
906
907 /* cleanup the job media table after an incomplete job, should not be needed */
908 incomplete_cleanup(jcr);
909
910 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
911 vbackup_cleanup(jcr, TermCode);
912 return;
913 }
914
915 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
916 memset(&cr, 0, sizeof(cr));
917
918 #ifdef xxxx
919 /* The current implementation of the JS_Warning status is not
920 * completed. SQL part looks to be ok, but the code is using
921 * JS_Terminated almost everywhere instead of (JS_Terminated || JS_Warning)
922 * as we do with is_canceled()
923 */
924 if (jcr->getJobStatus() == JS_Terminated &&
925 (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
926 TermCode = JS_Warnings;
927 }
928 #endif
929
930 update_job_end(jcr, TermCode);
931
932 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
933 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
934 db_strerror(jcr->db));
935 jcr->setJobStatus(JS_ErrorTerminated);
936 }
937
938 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
939 if (!db_get_client_record(jcr, jcr->db, &cr)) {
940 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
941 db_strerror(jcr->db));
942 }
943
944 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
945 if (!db_get_media_record(jcr, jcr->db, &mr)) {
946 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
947 mr.VolumeName, db_strerror(jcr->db));
948 jcr->setJobStatus(JS_ErrorTerminated);
949 }
950
951 update_bootstrap_file(jcr);
952
953 if (jcr->is_incomplete() && !jcr->job->allow_incomplete_jobs) {
954 jcr->forceJobStatus(JS_FatalError);
955 }
956
957 switch (jcr->JobStatus) {
958 case JS_Terminated:
959 if (jcr->JobErrors || jcr->SDErrors) {
960 Mmsg(term_msg, _("Backup OK -- %s"), jcr->StatusErrMsg[0] ? jcr->StatusErrMsg : _("with warnings"));
961
962 } else {
963 Mmsg(term_msg, _("Backup OK"));
964 }
965 break;
966 case JS_Incomplete:
967 Mmsg(term_msg, _("Backup failed -- Incomplete"));
968 break;
969 case JS_Warnings:
970 Mmsg(term_msg, _("Backup OK -- %s"), jcr->StatusErrMsg[0] ? jcr->StatusErrMsg : _("with warnings"));
971 break;
972 case JS_FatalError:
973 case JS_ErrorTerminated:
974 Mmsg(term_msg, _("*** Backup Error ***"));
975 msg_type = M_ERROR; /* Generate error message */
976 terminate_sd_msg_chan_thread(jcr);
977 break;
978 case JS_Canceled:
979 Mmsg(term_msg, _("Backup Canceled"));
980 terminate_sd_msg_chan_thread(jcr);
981 break;
982 default:
983 Mmsg(term_msg, _("Inappropriate term code: %c\n"), jcr->JobStatus);
984 break;
985 }
986 bstrftimes_na(schedt, sizeof(schedt), jcr->jr.SchedTime);
987 bstrftimes_na(sdt, sizeof(sdt), jcr->jr.StartTime);
988 bstrftimes_na(edt, sizeof(edt), jcr->jr.EndTime);
989 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
990 if (jcr->jr.StartTime == 0 || RunTime <= 0) {
991 RunTime = 1;
992 }
993 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
994 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
995 /*
996 * Note, if the job has erred, most likely it did not write any
997 * tape, so suppress this "error" message since in that case
998 * it is normal. Or look at it the other way, only for a
999 * normal exit should we complain about this error.
1000 */
1001 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
1002 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
1003 }
1004 jcr->VolumeName[0] = 0; /* none */
1005 }
1006
1007 if (jcr->ReadBytes == 0) {
1008 bstrncpy(data_compress, "None", sizeof(data_compress));
1009 } else {
1010 compression = (double)100 - 100.0 * ((double)jcr->SDJobBytes / (double)jcr->ReadBytes);
1011 if (compression < 0.5) {
1012 bstrncpy(data_compress, "None", sizeof(data_compress));
1013 } else {
1014 if (jcr->SDJobBytes > 0) {
1015 ratio = (double)jcr->ReadBytes / (double)jcr->SDJobBytes;
1016 } else {
1017 ratio = 1.0;
1018 }
1019 bsnprintf(data_compress, sizeof(data_compress), "%.1f%% %.1f:1",
1020 compression, ratio);
1021 }
1022 }
1023 if (jcr->CommBytes == 0 || jcr->CommCompressedBytes == 0) {
1024 bstrncpy(comm_compress, "None", sizeof(comm_compress));
1025 } else {
1026 compression = (double)100 - 100.0 * ((double)jcr->CommCompressedBytes / (double)jcr->CommBytes);
1027 if (compression < 0.5) {
1028 bstrncpy(comm_compress, "None", sizeof(comm_compress));
1029 } else {
1030 ratio = (double)jcr->CommBytes / (double)jcr->CommCompressedBytes;
1031 bsnprintf(comm_compress, sizeof(comm_compress), "%.1f%% %.1f:1",
1032 compression, ratio);
1033 }
1034 Dmsg2(200, "=== CommCompressed=%lld CommBytes=%lld\n",
1035 jcr->CommCompressedBytes, jcr->CommBytes);
1036 }
1037 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
1038 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1039
1040 if (jcr->HasBase) {
1041 Mmsg(base_info, _(" Base files/Used files: %lld/%lld (%.2f%%)\n"),
1042 jcr->nb_base_files,
1043 jcr->nb_base_files_used,
1044 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
1045 }
1046 /* Edit string for last volume size */
1047 if (mr.VolABytes != 0) {
1048 Mmsg(vol_info, _("meta: %s (%sB) aligned: %s (%sB)"),
1049 edit_uint64_with_commas(mr.VolBytes, ec7),
1050 edit_uint64_with_suffix(mr.VolBytes, ec8),
1051 edit_uint64_with_commas(mr.VolABytes, ec9),
1052 edit_uint64_with_suffix(mr.VolABytes, ec10));
1053 } else {
1054 Mmsg(vol_info, _("%s (%sB)"),
1055 edit_uint64_with_commas(mr.VolBytes, ec7),
1056 edit_uint64_with_suffix(mr.VolBytes, ec8));
1057 }
1058
1059 // bmicrosleep(15, 0); /* for debugging SIGHUP */
1060
1061 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
1062 " Build OS: %s %s %s\n"
1063 " JobId: %d\n"
1064 " Job: %s\n"
1065 " Backup Level: %s%s\n"
1066 " Client: \"%s\" %s\n"
1067 " FileSet: \"%s\" %s\n"
1068 " Pool: \"%s\" (From %s)\n"
1069 " Catalog: \"%s\" (From %s)\n"
1070 " Storage: \"%s\" (From %s)\n"
1071 " Scheduled time: %s\n"
1072 " Start time: %s\n"
1073 " End time: %s\n"
1074 " Elapsed time: %s\n"
1075 " Priority: %d\n"
1076 " FD Files Written: %s\n"
1077 " SD Files Written: %s\n"
1078 " FD Bytes Written: %s (%sB)\n"
1079 " SD Bytes Written: %s (%sB)\n"
1080 " Rate: %.1f KB/s\n"
1081 " Software Compression: %s\n"
1082 " Comm Line Compression: %s\n"
1083 "%s" /* Basefile info */
1084 " Snapshot/VSS: %s\n"
1085 " Encryption: %s\n"
1086 " Accurate: %s\n"
1087 " Volume name(s): %s\n"
1088 " Volume Session Id: %d\n"
1089 " Volume Session Time: %d\n"
1090 " Last Volume Bytes: %s\n"
1091 " Non-fatal FD errors: %d\n"
1092 " SD Errors: %d\n"
1093 " FD termination status: %s\n"
1094 " SD termination status: %s\n"
1095 " Termination: %s\n\n"),
1096 BACULA, my_name, VERSION, LSMDATE,
1097 HOST_OS, DISTNAME, DISTVER,
1098 jcr->jr.JobId,
1099 jcr->jr.Job,
1100 level_to_str(edl, sizeof(edl), jcr->getJobLevel()), jcr->since,
1101 jcr->client->name(), cr.Uname,
1102 jcr->fileset->name(), jcr->FSCreateTime,
1103 jcr->pool->name(), jcr->pool_source,
1104 jcr->catalog->name(), jcr->catalog_source,
1105 jcr->wstore->name(), jcr->wstore_source,
1106 schedt,
1107 sdt,
1108 edt,
1109 edit_utime(RunTime, elapsed, sizeof(elapsed)),
1110 jcr->JobPriority,
1111 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
1112 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
1113 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
1114 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
1115 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
1116 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
1117 kbps,
1118 data_compress,
1119 comm_compress,
1120 base_info.c_str(),
1121 jcr->Snapshot?_("yes"):_("no"),
1122 jcr->Encrypt?_("yes"):_("no"),
1123 jcr->accurate?_("yes"):_("no"),
1124 jcr->VolumeName,
1125 jcr->VolSessionId,
1126 jcr->VolSessionTime,
1127 vol_info.c_str(),
1128 jcr->JobErrors,
1129 jcr->SDErrors,
1130 fd_term_msg,
1131 sd_term_msg,
1132 term_msg.c_str());
1133
1134 Dmsg0(100, "Leave backup_cleanup()\n");
1135 }
1136
update_bootstrap_file(JCR * jcr)1137 void update_bootstrap_file(JCR *jcr)
1138 {
1139 /* Now update the bootstrap file if any */
1140 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
1141 jcr->job->WriteBootstrap) {
1142 FILE *fd;
1143 BPIPE *bpipe = NULL;
1144 int got_pipe = 0;
1145 char edl[50];
1146 POOLMEM *fname = get_pool_memory(PM_FNAME);
1147 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "",
1148 job_code_callback_director);
1149
1150 VOL_PARAMS *VolParams = NULL;
1151 int VolCount;
1152 char edt[50], ed1[50], ed2[50];
1153
1154 if (*fname == '|') {
1155 got_pipe = 1;
1156 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
1157 fd = bpipe ? bpipe->wfd : NULL;
1158 } else {
1159 /* ***FIXME*** handle BASE */
1160 fd = bfopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
1161 }
1162 if (fd) {
1163 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
1164 &VolParams);
1165 if (VolCount == 0) {
1166 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
1167 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
1168 if (jcr->SDJobFiles != 0) {
1169 jcr->setJobStatus(JS_ErrorTerminated);
1170 }
1171
1172 }
1173 /* Start output with when and who wrote it */
1174 bstrftimes(edt, sizeof(edt), time(NULL));
1175 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
1176 level_to_str(edl, sizeof(edl), jcr->getJobLevel()),
1177 jcr->since);
1178 for (int i=0; i < VolCount; i++) {
1179 /* Write the record */
1180 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
1181 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
1182 if (VolParams[i].Slot > 0) {
1183 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
1184 }
1185 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
1186 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
1187 fprintf(fd, "VolAddr=%s-%s\n",
1188 edit_uint64(VolParams[i].StartAddr, ed1),
1189 edit_uint64(VolParams[i].EndAddr, ed2));
1190 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
1191 VolParams[i].LastIndex);
1192 }
1193 if (VolParams) {
1194 free(VolParams);
1195 }
1196 if (got_pipe) {
1197 close_bpipe(bpipe);
1198 } else {
1199 fclose(fd);
1200 }
1201 } else {
1202 berrno be;
1203 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
1204 "%s: ERR=%s\n"), fname, be.bstrerror());
1205 jcr->setJobStatus(JS_ErrorTerminated);
1206 }
1207 free_pool_memory(fname);
1208 }
1209 }
1210