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 checksum
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++) { /* Parse all Include {} */
185 inc = f->include_items[i];
187 for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
188 fopts = inc->opts_list[j];
190 for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
192 case 'V': /* verify */
193 in_block = (jcr->get_JobType() == JT_VERIFY); /* not used now */
195 case 'J': /* Basejob keyword */
196 in_block = (jcr->get_JobLevel() == L_FULL);
198 case 'C': /* Accurate keyword */
199 in_block = (jcr->get_JobLevel() != L_FULL);
201 case ':': /* End of keyword */
207 Dmsg0(50, "Checksum will be sent to FD\n");
221 * Send current file list to FD
222 * DIR -> FD : accurate files=xxxx
223 * DIR -> FD : /path/to/file\0Lstat\0MD5
224 * DIR -> FD : /path/to/dir/\0Lstat\0MD5
228 bool send_accurate_current_files(JCR *jcr)
235 if (!jcr->accurate || job_canceled(jcr)) {
238 /* In base level, no previous job is used */
239 if (jcr->get_JobLevel() == L_BASE) {
243 /* Don't send and store the checksum if fileset doesn't require it */
244 jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
246 if (jcr->get_JobLevel() == L_FULL) {
247 /* On Full mode, if no previous base job, no accurate things */
248 if (!get_base_jobids(jcr, &jobids)) {
252 Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
255 /* For Incr/Diff level, we search for older jobs */
256 db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
258 /* We are in Incr/Diff, but no Full to build the accurate list... */
259 if (jobids.count == 0) {
261 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
266 if (jcr->JobId) { /* display the message only for real jobs */
267 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
270 /* to be able to allocate the right size for htable */
271 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
272 db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
273 Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
274 jcr->file_bsock->fsend("accurate files=%s\n", nb.list);
276 if (!db_open_batch_connexion(jcr, jcr->db)) {
277 Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
282 jcr->nb_base_files = str_to_int64(nb.list);
283 db_create_base_file_list(jcr, jcr->db, jobids.list);
284 db_get_base_file_list(jcr, jcr->db,
285 accurate_list_handler, (void *)jcr);
288 db_get_file_list(jcr, jcr->db_batch, jobids.list,
289 accurate_list_handler, (void *)jcr);
292 /* TODO: close the batch connexion ? (can be used very soon) */
294 jcr->file_bsock->signal(BNET_EOD);
301 * Do a backup of the specified FileSet
303 * Returns: false on failure
306 bool do_backup(JCR *jcr)
309 int tls_need = BNET_TLS_NONE;
314 if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
315 return do_vbackup(jcr);
318 /* Print Job Start message */
319 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
320 edit_uint64(jcr->JobId, ed1), jcr->Job);
322 set_jcr_job_status(jcr, JS_Running);
323 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
324 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
325 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
330 * Open a message channel connection with the Storage
331 * daemon. This is to let him know that our client
332 * will be contacting him for a backup session.
335 Dmsg0(110, "Open connection with storage daemon\n");
336 set_jcr_job_status(jcr, JS_WaitSD);
338 * Start conversation with Storage daemon
340 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
344 * Now start a job with the Storage daemon
346 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
351 * Start the job prior to starting the message thread below
352 * to avoid two threads from using the BSOCK structure at
355 if (!bnet_fsend(jcr->store_bsock, "run")) {
360 * Now start a Storage daemon message thread. Note,
361 * this thread is used to provide the catalog services
362 * for the backup job, including inserting the attributes
363 * into the catalog. See catalog_update() in catreq.c
365 if (!start_storage_daemon_message_thread(jcr)) {
368 Dmsg0(150, "Storage daemon connection OK\n");
370 set_jcr_job_status(jcr, JS_WaitFD);
371 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
375 set_jcr_job_status(jcr, JS_Running);
376 fd = jcr->file_bsock;
378 if (!send_include_list(jcr)) {
382 if (!send_exclude_list(jcr)) {
386 if (!send_level_command(jcr)) {
391 * send Storage daemon address to the File daemon
394 if (store->SDDport == 0) {
395 store->SDDport = store->SDport;
398 /* TLS Requirement */
399 if (store->tls_enable) {
400 if (store->tls_require) {
401 tls_need = BNET_TLS_REQUIRED;
403 tls_need = BNET_TLS_OK;
407 fd->fsend(storaddr, store->address, store->SDDport, tls_need);
408 if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
412 if (!send_runscripts_commands(jcr)) {
417 * We re-update the job start record so that the start
418 * time is set after the run before job. This avoids
419 * that any files created by the run before job will
420 * be saved twice. They will be backed up in the current
421 * job, but not in the next one unless they are changed.
422 * Without this, they will be backed up in this job and
423 * in the next job run because in that case, their date
424 * is after the start of this run.
426 jcr->start_time = time(NULL);
427 jcr->jr.StartTime = jcr->start_time;
428 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
429 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
433 * If backup is in accurate mode, we send the list of
436 if (!send_accurate_current_files(jcr)) {
440 /* Send backup command */
441 fd->fsend(backupcmd);
442 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
446 /* Pickup Job termination data */
447 stat = wait_for_job_termination(jcr);
448 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
451 !db_commit_base_file_attributes_record(jcr, jcr->db))
453 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
456 if (stat == JS_Terminated) {
457 backup_cleanup(jcr, stat);
462 /* Come here only after starting SD thread */
464 set_jcr_job_status(jcr, JS_ErrorTerminated);
465 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
467 wait_for_job_termination(jcr, FDConnectTimeout);
468 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
474 * Here we wait for the File daemon to signal termination,
475 * then we wait for the Storage daemon. When both
476 * are done, we return the job status.
477 * Also used by restore.c
479 int wait_for_job_termination(JCR *jcr, int timeout)
482 BSOCK *fd = jcr->file_bsock;
484 uint32_t JobFiles, JobErrors;
485 uint32_t JobWarnings = 0;
486 uint64_t ReadBytes = 0;
487 uint64_t JobBytes = 0;
492 set_jcr_job_status(jcr, JS_Running);
496 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
498 /* Wait for Client to terminate */
499 while ((n = bget_dirmsg(fd)) >= 0) {
501 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
502 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
503 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
504 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
506 set_jcr_job_status(jcr, jcr->FDJobStatus);
507 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
509 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
512 if (job_canceled(jcr)) {
517 stop_bsock_timer(tid);
520 if (is_bnet_error(fd)) {
521 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
522 job_type_to_str(jcr->get_JobType()), fd->bstrerror());
524 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
527 /* Force cancel in SD if failing */
528 if (job_canceled(jcr) || !fd_ok) {
529 cancel_storage_daemon_job(jcr);
532 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
533 wait_for_storage_daemon_termination(jcr);
535 /* Return values from FD */
537 jcr->JobFiles = JobFiles;
538 jcr->JobErrors += JobErrors; /* Keep total errors */
539 jcr->ReadBytes = ReadBytes;
540 jcr->JobBytes = JobBytes;
541 jcr->JobWarnings = JobWarnings;
543 jcr->Encrypt = Encrypt;
545 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
548 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
549 // jcr->JobStatus, jcr->SDJobStatus);
551 /* Return the first error status we find Dir, FD, or SD */
552 if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
553 jcr->FDJobStatus = JS_ErrorTerminated;
555 if (jcr->JobStatus != JS_Terminated) {
556 return jcr->JobStatus;
558 if (jcr->FDJobStatus != JS_Terminated) {
559 return jcr->FDJobStatus;
561 return jcr->SDJobStatus;
565 * Release resources allocated during backup.
567 void backup_cleanup(JCR *jcr, int TermCode)
569 char sdt[50], edt[50], schedt[50];
570 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
571 char ec6[30], ec7[30], ec8[30], elapsed[50];
572 char term_code[100], fd_term_msg[100], sd_term_msg[100];
573 const char *term_msg;
574 int msg_type = M_INFO;
577 double kbps, compression;
580 if (jcr->get_JobLevel() == L_VIRTUAL_FULL) {
581 vbackup_cleanup(jcr, TermCode);
585 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
586 memset(&mr, 0, sizeof(mr));
587 memset(&cr, 0, sizeof(cr));
589 update_job_end(jcr, TermCode);
591 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
592 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
593 db_strerror(jcr->db));
594 set_jcr_job_status(jcr, JS_ErrorTerminated);
597 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
598 if (!db_get_client_record(jcr, jcr->db, &cr)) {
599 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
600 db_strerror(jcr->db));
603 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
604 if (!db_get_media_record(jcr, jcr->db, &mr)) {
605 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
606 mr.VolumeName, db_strerror(jcr->db));
607 set_jcr_job_status(jcr, JS_ErrorTerminated);
610 update_bootstrap_file(jcr);
612 switch (jcr->JobStatus) {
614 if (jcr->JobErrors || jcr->SDErrors) {
615 term_msg = _("Backup OK -- with warnings");
617 term_msg = _("Backup OK");
621 term_msg = _("Backup OK -- with warnings");
624 case JS_ErrorTerminated:
625 term_msg = _("*** Backup Error ***");
626 msg_type = M_ERROR; /* Generate error message */
627 if (jcr->store_bsock) {
628 jcr->store_bsock->signal(BNET_TERMINATE);
629 if (jcr->SD_msg_chan) {
630 pthread_cancel(jcr->SD_msg_chan);
635 term_msg = _("Backup Canceled");
636 if (jcr->store_bsock) {
637 jcr->store_bsock->signal(BNET_TERMINATE);
638 if (jcr->SD_msg_chan) {
639 pthread_cancel(jcr->SD_msg_chan);
644 term_msg = term_code;
645 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
648 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
649 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
650 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
651 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
655 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
657 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
659 * Note, if the job has erred, most likely it did not write any
660 * tape, so suppress this "error" message since in that case
661 * it is normal. Or look at it the other way, only for a
662 * normal exit should we complain about this error.
664 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
665 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
667 jcr->VolumeName[0] = 0; /* none */
670 if (jcr->ReadBytes == 0) {
671 bstrncpy(compress, "None", sizeof(compress));
673 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
674 if (compression < 0.5) {
675 bstrncpy(compress, "None", sizeof(compress));
677 bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
680 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
681 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
684 Dmsg3(0, "Base files/Used files %lld/%lld=%.2f%%\n", jcr->nb_base_files,
685 jcr->nb_base_files_used,
686 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
688 // bmicrosleep(15, 0); /* for debugging SIGHUP */
690 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
691 " Build OS: %s %s %s\n"
694 " Backup Level: %s%s\n"
695 " Client: \"%s\" %s\n"
696 " FileSet: \"%s\" %s\n"
697 " Pool: \"%s\" (From %s)\n"
698 " Catalog: \"%s\" (From %s)\n"
699 " Storage: \"%s\" (From %s)\n"
700 " Scheduled time: %s\n"
703 " Elapsed time: %s\n"
705 " FD Files Written: %s\n"
706 " SD Files Written: %s\n"
707 " FD Bytes Written: %s (%sB)\n"
708 " SD Bytes Written: %s (%sB)\n"
710 " Software Compression: %s\n"
714 " Volume name(s): %s\n"
715 " Volume Session Id: %d\n"
716 " Volume Session Time: %d\n"
717 " Last Volume Bytes: %s (%sB)\n"
718 " Non-fatal FD errors: %d\n"
720 " FD termination status: %s\n"
721 " SD termination status: %s\n"
722 " Termination: %s\n\n"),
723 BACULA, my_name, VERSION, LSMDATE, edt,
724 HOST_OS, DISTNAME, DISTVER,
727 level_to_str(jcr->get_JobLevel()), jcr->since,
728 jcr->client->name(), cr.Uname,
729 jcr->fileset->name(), jcr->FSCreateTime,
730 jcr->pool->name(), jcr->pool_source,
731 jcr->catalog->name(), jcr->catalog_source,
732 jcr->wstore->name(), jcr->wstore_source,
736 edit_utime(RunTime, elapsed, sizeof(elapsed)),
738 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
739 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
740 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
741 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
742 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
743 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
746 jcr->VSS?_("yes"):_("no"),
747 jcr->Encrypt?_("yes"):_("no"),
748 jcr->accurate?_("yes"):_("no"),
752 edit_uint64_with_commas(mr.VolBytes, ec7),
753 edit_uint64_with_suffix(mr.VolBytes, ec8),
760 Dmsg0(100, "Leave backup_cleanup()\n");
763 void update_bootstrap_file(JCR *jcr)
765 /* Now update the bootstrap file if any */
766 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
767 jcr->job->WriteBootstrap) {
771 POOLMEM *fname = get_pool_memory(PM_FNAME);
772 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
774 VOL_PARAMS *VolParams = NULL;
776 char edt[50], ed1[50], ed2[50];
780 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
781 fd = bpipe ? bpipe->wfd : NULL;
783 /* ***FIXME*** handle BASE */
784 fd = fopen(fname, jcr->get_JobLevel()==L_FULL?"w+b":"a+b");
787 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
790 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
791 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
792 if (jcr->SDJobFiles != 0) {
793 set_jcr_job_status(jcr, JS_ErrorTerminated);
797 /* Start output with when and who wrote it */
798 bstrftimes(edt, sizeof(edt), time(NULL));
799 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
800 level_to_str(jcr->get_JobLevel()), jcr->since);
801 for (int i=0; i < VolCount; i++) {
802 /* Write the record */
803 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
804 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
805 if (VolParams[i].Slot > 0) {
806 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
808 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
809 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
810 fprintf(fd, "VolAddr=%s-%s\n",
811 edit_uint64(VolParams[i].StartAddr, ed1),
812 edit_uint64(VolParams[i].EndAddr, ed2));
813 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
814 VolParams[i].LastIndex);
826 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
827 "%s: ERR=%s\n"), fname, be.bstrerror());
828 set_jcr_job_status(jcr, JS_ErrorTerminated);
830 free_pool_memory(fname);