2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
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 General Public License for more details.
18 You should have received a copy of the GNU 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
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- backup.c -- responsible for doing backup jobs
32 * Kern Sibbald, March MM
34 * Basic tasks done here:
35 * Open DB and create records for this job.
36 * Open Message Channel with Storage daemon to tell him a job will be starting.
37 * Open connection with File daemon and pass him commands
39 * When the File daemon finishes the job, update the DB.
48 /* Commands sent to File daemon */
49 static char backupcmd[] = "backup\n";
50 static char storaddr[] = "storage address=%s port=%d ssl=%d\n";
52 /* Responses received from File daemon */
53 static char OKbackup[] = "2000 OK backup\n";
54 static char OKstore[] = "2000 OK storage\n";
55 static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
56 "ReadBytes=%llu JobBytes=%llu Errors=%u "
57 "VSS=%d Encrypt=%d\n";
58 /* Pre 1.39.29 (04Dec06) EndJob */
59 static char OldEndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
60 "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
62 * Called here before the job is run to do the job
65 bool do_backup_init(JCR *jcr)
68 if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
69 return do_vbackup_init(jcr);
71 free_rstorage(jcr); /* we don't read so release */
73 if (!get_or_create_fileset_record(jcr)) {
78 * Get definitive Job level and since time
80 get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
82 apply_pool_overrides(jcr);
84 if (!allow_duplicate_job(jcr)) {
88 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
89 if (jcr->jr.PoolId == 0) {
93 /* If pool storage specified, use it instead of job storage */
94 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
97 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
101 create_clones(jcr); /* run any clone jobs */
106 /* Take all base jobs from job resource and find the
109 static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids)
116 if (!jcr->job->base) {
117 return false; /* no base job, stop accurate */
120 memset(&jr, 0, sizeof(JOB_DBR));
121 jr.StartTime = jcr->jr.StartTime;
123 foreach_alist(job, jcr->job->base) {
124 bstrncpy(jr.Name, job->name(), sizeof(jr.Name));
125 db_get_base_jobid(jcr, jcr->db, &jr, &id);
129 pm_strcat(jobids->list, ",");
131 pm_strcat(jobids->list, edit_uint64(id, str_jobid));
136 return jobids->count > 0;
140 * Foreach files in currrent list, send "/path/fname\0LStat\0MD5" to FD
142 static int accurate_list_handler(void *ctx, int num_fields, char **row)
144 JCR *jcr = (JCR *)ctx;
146 if (job_canceled(jcr)) {
150 if (row[2] == 0) { /* discard when file_index == 0 */
154 /* sending with checksum */
155 if (jcr->use_accurate_chksum
157 && row[5][0] /* skip checksum = '0' */
160 jcr->file_bsock->fsend("%s%s%c%s%c%s",
161 row[0], row[1], 0, row[4], 0, row[5]);
163 jcr->file_bsock->fsend("%s%s%c%s",
164 row[0], row[1], 0, row[4]);
169 /* In this procedure, we check if the current fileset is using
170 * FileSet-> Include-> Options-> Accurate/Verify/BaseJob=checksum
172 static bool is_checksum_needed_by_fileset(JCR *jcr)
178 if (!jcr->job || !jcr->job->fileset) {
182 f = jcr->job->fileset;
184 for (int i=0; i < f->num_includes; i++) {
185 inc = f->include_items[i];
187 for (int j=0; j < inc->num_opts; j++) {
188 fopts = inc->opts_list[j];
190 for (char *k=fopts->opts; *k ; k++) {
192 case 'V': /* verify */
193 in_block = (jcr->get_JobType() == JT_VERIFY);
195 case 'J': /* Basejob */
196 in_block = (jcr->get_JobLevel() == L_FULL);
198 case 'C': /* Accurate */
199 in_block = (jcr->get_JobLevel() != L_FULL);
220 * Send current file list to FD
221 * DIR -> FD : accurate files=xxxx
222 * DIR -> FD : /path/to/file\0Lstat\0MD5
223 * DIR -> FD : /path/to/dir/\0Lstat\0MD5
227 bool send_accurate_current_files(JCR *jcr)
234 if (!jcr->accurate || job_canceled(jcr)) {
237 /* In base level, no previous job is used */
238 if (jcr->get_JobLevel() == L_BASE) {
242 /* Don't send and store the checksum if fileset doesn't require it */
243 jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
245 if (jcr->get_JobLevel() == L_FULL) {
246 /* On Full mode, if no previous base job, no accurate things */
247 if (!get_base_jobids(jcr, &jobids)) {
251 Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
254 /* For Incr/Diff level, we search for older jobs */
255 db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
257 /* We are in Incr/Diff, but no Full to build the accurate list... */
258 if (jobids.count == 0) {
260 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
265 if (jcr->JobId) { /* display the message only for real jobs */
266 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
269 /* to be able to allocate the right size for htable */
270 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
271 db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
272 Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
273 jcr->file_bsock->fsend("accurate files=%s\n", nb.list);
275 if (!db_open_batch_connexion(jcr, jcr->db)) {
276 Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
281 jcr->nb_base_files = str_to_int64(nb.list);
282 db_create_base_file_list(jcr, jcr->db, jobids.list);
283 db_get_base_file_list(jcr, jcr->db,
284 accurate_list_handler, (void *)jcr);
287 db_get_file_list(jcr, jcr->db_batch, jobids.list,
288 accurate_list_handler, (void *)jcr);
291 /* TODO: close the batch connexion ? (can be used very soon) */
293 jcr->file_bsock->signal(BNET_EOD);
300 * Do a backup of the specified FileSet
302 * Returns: false on failure
305 bool do_backup(JCR *jcr)
308 int tls_need = BNET_TLS_NONE;
313 if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
314 return do_vbackup(jcr);
317 /* Print Job Start message */
318 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
319 edit_uint64(jcr->JobId, ed1), jcr->Job);
321 set_jcr_job_status(jcr, JS_Running);
322 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
323 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
324 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
329 * Open a message channel connection with the Storage
330 * daemon. This is to let him know that our client
331 * will be contacting him for a backup session.
334 Dmsg0(110, "Open connection with storage daemon\n");
335 set_jcr_job_status(jcr, JS_WaitSD);
337 * Start conversation with Storage daemon
339 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
343 * Now start a job with the Storage daemon
345 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
350 * Start the job prior to starting the message thread below
351 * to avoid two threads from using the BSOCK structure at
354 if (!bnet_fsend(jcr->store_bsock, "run")) {
359 * Now start a Storage daemon message thread. Note,
360 * this thread is used to provide the catalog services
361 * for the backup job, including inserting the attributes
362 * into the catalog. See catalog_update() in catreq.c
364 if (!start_storage_daemon_message_thread(jcr)) {
367 Dmsg0(150, "Storage daemon connection OK\n");
369 set_jcr_job_status(jcr, JS_WaitFD);
370 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
374 set_jcr_job_status(jcr, JS_Running);
375 fd = jcr->file_bsock;
377 if (!send_include_list(jcr)) {
381 if (!send_exclude_list(jcr)) {
385 if (!send_level_command(jcr)) {
390 * send Storage daemon address to the File daemon
393 if (store->SDDport == 0) {
394 store->SDDport = store->SDport;
397 /* TLS Requirement */
398 if (store->tls_enable) {
399 if (store->tls_require) {
400 tls_need = BNET_TLS_REQUIRED;
402 tls_need = BNET_TLS_OK;
406 fd->fsend(storaddr, store->address, store->SDDport, tls_need);
407 if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
411 if (!send_runscripts_commands(jcr)) {
416 * We re-update the job start record so that the start
417 * time is set after the run before job. This avoids
418 * that any files created by the run before job will
419 * be saved twice. They will be backed up in the current
420 * job, but not in the next one unless they are changed.
421 * Without this, they will be backed up in this job and
422 * in the next job run because in that case, their date
423 * is after the start of this run.
425 jcr->start_time = time(NULL);
426 jcr->jr.StartTime = jcr->start_time;
427 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
428 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
432 * If backup is in accurate mode, we send the list of
435 if (!send_accurate_current_files(jcr)) {
439 /* Send backup command */
440 fd->fsend(backupcmd);
441 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
445 /* Pickup Job termination data */
446 stat = wait_for_job_termination(jcr);
447 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
450 !db_commit_base_file_attributes_record(jcr, jcr->db))
452 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
455 if (stat == JS_Terminated) {
456 backup_cleanup(jcr, stat);
461 /* Come here only after starting SD thread */
463 set_jcr_job_status(jcr, JS_ErrorTerminated);
464 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
466 wait_for_job_termination(jcr, FDConnectTimeout);
467 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
473 * Here we wait for the File daemon to signal termination,
474 * then we wait for the Storage daemon. When both
475 * are done, we return the job status.
476 * Also used by restore.c
478 int wait_for_job_termination(JCR *jcr, int timeout)
481 BSOCK *fd = jcr->file_bsock;
483 uint32_t JobFiles, JobErrors;
484 uint32_t JobWarnings = 0;
485 uint64_t ReadBytes = 0;
486 uint64_t JobBytes = 0;
491 set_jcr_job_status(jcr, JS_Running);
495 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
497 /* Wait for Client to terminate */
498 while ((n = bget_dirmsg(fd)) >= 0) {
500 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
501 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
502 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
503 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
505 set_jcr_job_status(jcr, jcr->FDJobStatus);
506 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
508 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
511 if (job_canceled(jcr)) {
516 stop_bsock_timer(tid);
519 if (is_bnet_error(fd)) {
520 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
521 job_type_to_str(jcr->get_JobType()), fd->bstrerror());
523 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
526 /* Force cancel in SD if failing */
527 if (job_canceled(jcr) || !fd_ok) {
528 cancel_storage_daemon_job(jcr);
531 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
532 wait_for_storage_daemon_termination(jcr);
534 /* Return values from FD */
536 jcr->JobFiles = JobFiles;
537 jcr->JobErrors += JobErrors; /* Keep total errors */
538 jcr->ReadBytes = ReadBytes;
539 jcr->JobBytes = JobBytes;
540 jcr->JobWarnings = JobWarnings;
542 jcr->Encrypt = Encrypt;
544 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
547 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
548 // jcr->JobStatus, jcr->SDJobStatus);
550 /* Return the first error status we find Dir, FD, or SD */
551 if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
552 jcr->FDJobStatus = JS_ErrorTerminated;
554 if (jcr->JobStatus != JS_Terminated) {
555 return jcr->JobStatus;
557 if (jcr->FDJobStatus != JS_Terminated) {
558 return jcr->FDJobStatus;
560 return jcr->SDJobStatus;
564 * Release resources allocated during backup.
566 void backup_cleanup(JCR *jcr, int TermCode)
568 char sdt[50], edt[50], schedt[50];
569 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
570 char ec6[30], ec7[30], ec8[30], elapsed[50];
571 char term_code[100], fd_term_msg[100], sd_term_msg[100];
572 const char *term_msg;
573 int msg_type = M_INFO;
576 double kbps, compression;
579 if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
580 vbackup_cleanup(jcr, TermCode);
584 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
585 memset(&mr, 0, sizeof(mr));
586 memset(&cr, 0, sizeof(cr));
588 update_job_end(jcr, TermCode);
590 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
591 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
592 db_strerror(jcr->db));
593 set_jcr_job_status(jcr, JS_ErrorTerminated);
596 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
597 if (!db_get_client_record(jcr, jcr->db, &cr)) {
598 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
599 db_strerror(jcr->db));
602 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
603 if (!db_get_media_record(jcr, jcr->db, &mr)) {
604 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
605 mr.VolumeName, db_strerror(jcr->db));
606 set_jcr_job_status(jcr, JS_ErrorTerminated);
609 update_bootstrap_file(jcr);
611 switch (jcr->JobStatus) {
613 if (jcr->JobErrors || jcr->SDErrors) {
614 term_msg = _("Backup OK -- with warnings");
616 term_msg = _("Backup OK");
620 term_msg = _("Backup OK -- with warnings");
623 case JS_ErrorTerminated:
624 term_msg = _("*** Backup Error ***");
625 msg_type = M_ERROR; /* Generate error message */
626 if (jcr->store_bsock) {
627 jcr->store_bsock->signal(BNET_TERMINATE);
628 if (jcr->SD_msg_chan) {
629 pthread_cancel(jcr->SD_msg_chan);
634 term_msg = _("Backup Canceled");
635 if (jcr->store_bsock) {
636 jcr->store_bsock->signal(BNET_TERMINATE);
637 if (jcr->SD_msg_chan) {
638 pthread_cancel(jcr->SD_msg_chan);
643 term_msg = term_code;
644 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
647 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
648 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
649 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
650 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
654 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
656 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
658 * Note, if the job has erred, most likely it did not write any
659 * tape, so suppress this "error" message since in that case
660 * it is normal. Or look at it the other way, only for a
661 * normal exit should we complain about this error.
663 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
664 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
666 jcr->VolumeName[0] = 0; /* none */
669 if (jcr->ReadBytes == 0) {
670 bstrncpy(compress, "None", sizeof(compress));
672 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
673 if (compression < 0.5) {
674 bstrncpy(compress, "None", sizeof(compress));
676 bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
679 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
680 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
683 Dmsg3(0, "Base files/Used files %lld/%lld=%.2f%%\n", jcr->nb_base_files,
684 jcr->nb_base_files_used,
685 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
687 // bmicrosleep(15, 0); /* for debugging SIGHUP */
689 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
690 " Build OS: %s %s %s\n"
693 " Backup Level: %s%s\n"
694 " Client: \"%s\" %s\n"
695 " FileSet: \"%s\" %s\n"
696 " Pool: \"%s\" (From %s)\n"
697 " Catalog: \"%s\" (From %s)\n"
698 " Storage: \"%s\" (From %s)\n"
699 " Scheduled time: %s\n"
702 " Elapsed time: %s\n"
704 " FD Files Written: %s\n"
705 " SD Files Written: %s\n"
706 " FD Bytes Written: %s (%sB)\n"
707 " SD Bytes Written: %s (%sB)\n"
709 " Software Compression: %s\n"
713 " Volume name(s): %s\n"
714 " Volume Session Id: %d\n"
715 " Volume Session Time: %d\n"
716 " Last Volume Bytes: %s (%sB)\n"
717 " Non-fatal FD errors: %d\n"
719 " FD termination status: %s\n"
720 " SD termination status: %s\n"
721 " Termination: %s\n\n"),
722 BACULA, my_name, VERSION, LSMDATE, edt,
723 HOST_OS, DISTNAME, DISTVER,
726 level_to_str(jcr->get_JobLevel()), jcr->since,
727 jcr->client->name(), cr.Uname,
728 jcr->fileset->name(), jcr->FSCreateTime,
729 jcr->pool->name(), jcr->pool_source,
730 jcr->catalog->name(), jcr->catalog_source,
731 jcr->wstore->name(), jcr->wstore_source,
735 edit_utime(RunTime, elapsed, sizeof(elapsed)),
737 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
738 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
739 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
740 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
741 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
742 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
745 jcr->VSS?_("yes"):_("no"),
746 jcr->Encrypt?_("yes"):_("no"),
747 jcr->accurate?_("yes"):_("no"),
751 edit_uint64_with_commas(mr.VolBytes, ec7),
752 edit_uint64_with_suffix(mr.VolBytes, ec8),
759 Dmsg0(100, "Leave backup_cleanup()\n");
762 void update_bootstrap_file(JCR *jcr)
764 /* Now update the bootstrap file if any */
765 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
766 jcr->job->WriteBootstrap) {
770 POOLMEM *fname = get_pool_memory(PM_FNAME);
771 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
773 VOL_PARAMS *VolParams = NULL;
775 char edt[50], ed1[50], ed2[50];
779 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
780 fd = bpipe ? bpipe->wfd : NULL;
782 /* ***FIXME*** handle BASE */
783 fd = fopen(fname, jcr->get_JobLevel()==L_FULL?"w+b":"a+b");
786 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
789 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
790 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
791 if (jcr->SDJobFiles != 0) {
792 set_jcr_job_status(jcr, JS_ErrorTerminated);
796 /* Start output with when and who wrote it */
797 bstrftimes(edt, sizeof(edt), time(NULL));
798 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
799 level_to_str(jcr->get_JobLevel()), jcr->since);
800 for (int i=0; i < VolCount; i++) {
801 /* Write the record */
802 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
803 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
804 if (VolParams[i].Slot > 0) {
805 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
807 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
808 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
809 fprintf(fd, "VolAddr=%s-%s\n",
810 edit_uint64(VolParams[i].StartAddr, ed1),
811 edit_uint64(VolParams[i].EndAddr, ed2));
812 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
813 VolParams[i].LastIndex);
825 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
826 "%s: ERR=%s\n"), fname, be.bstrerror());
827 set_jcr_job_status(jcr, JS_ErrorTerminated);
829 free_pool_memory(fname);