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->getJobLevel() == 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
171 * This procedure uses jcr->HasBase, so it must be call after the initialization
173 static bool is_checksum_needed_by_fileset(JCR *jcr)
179 bool have_basejob_option=false;
180 if (!jcr->job || !jcr->job->fileset) {
184 f = jcr->job->fileset;
186 for (int i=0; i < f->num_includes; i++) { /* Parse all Include {} */
187 inc = f->include_items[i];
189 for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
190 fopts = inc->opts_list[j];
192 for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
194 case 'V': /* verify */
195 in_block = (jcr->getJobType() == JT_VERIFY); /* not used now */
197 case 'J': /* Basejob keyword */
198 have_basejob_option = in_block = jcr->HasBase;
200 case 'C': /* Accurate keyword */
201 in_block = (jcr->getJobLevel() != L_FULL);
203 case ':': /* End of keyword */
209 Dmsg0(50, "Checksum will be sent to FD\n");
220 /* By default for BaseJobs, we send the checksum */
221 if (!have_basejob_option && jcr->HasBase) {
225 Dmsg0(50, "Checksum will be sent to FD\n");
230 * Send current file list to FD
231 * DIR -> FD : accurate files=xxxx
232 * DIR -> FD : /path/to/file\0Lstat\0MD5
233 * DIR -> FD : /path/to/dir/\0Lstat\0MD5
237 bool send_accurate_current_files(JCR *jcr)
244 if (!jcr->accurate || job_canceled(jcr)) {
247 /* In base level, no previous job is used */
248 if (jcr->getJobLevel() == L_BASE) {
252 if (jcr->getJobLevel() == L_FULL) {
253 /* On Full mode, if no previous base job, no accurate things */
254 if (!get_base_jobids(jcr, &jobids)) {
258 Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
261 /* For Incr/Diff level, we search for older jobs */
262 db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
264 /* We are in Incr/Diff, but no Full to build the accurate list... */
265 if (jobids.count == 0) {
267 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
272 /* Don't send and store the checksum if fileset doesn't require it */
273 jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
275 if (jcr->JobId) { /* display the message only for real jobs */
276 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
279 /* to be able to allocate the right size for htable */
280 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
281 db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
282 Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
283 jcr->file_bsock->fsend("accurate files=%s\n", nb.list);
285 if (!db_open_batch_connexion(jcr, jcr->db)) {
286 Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
291 jcr->nb_base_files = str_to_int64(nb.list);
292 db_create_base_file_list(jcr, jcr->db, jobids.list);
293 db_get_base_file_list(jcr, jcr->db,
294 accurate_list_handler, (void *)jcr);
297 db_get_file_list(jcr, jcr->db_batch, jobids.list,
298 accurate_list_handler, (void *)jcr);
301 /* TODO: close the batch connexion ? (can be used very soon) */
303 jcr->file_bsock->signal(BNET_EOD);
310 * Do a backup of the specified FileSet
312 * Returns: false on failure
315 bool do_backup(JCR *jcr)
318 int tls_need = BNET_TLS_NONE;
323 if (jcr->getJobLevel() == L_VIRTUAL_FULL) {
324 return do_vbackup(jcr);
327 /* Print Job Start message */
328 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
329 edit_uint64(jcr->JobId, ed1), jcr->Job);
331 jcr->setJobStatus(JS_Running);
332 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
333 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
334 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
339 * Open a message channel connection with the Storage
340 * daemon. This is to let him know that our client
341 * will be contacting him for a backup session.
344 Dmsg0(110, "Open connection with storage daemon\n");
345 jcr->setJobStatus(JS_WaitSD);
347 * Start conversation with Storage daemon
349 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
353 * Now start a job with the Storage daemon
355 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
360 * Start the job prior to starting the message thread below
361 * to avoid two threads from using the BSOCK structure at
364 if (!bnet_fsend(jcr->store_bsock, "run")) {
369 * Now start a Storage daemon message thread. Note,
370 * this thread is used to provide the catalog services
371 * for the backup job, including inserting the attributes
372 * into the catalog. See catalog_update() in catreq.c
374 if (!start_storage_daemon_message_thread(jcr)) {
377 Dmsg0(150, "Storage daemon connection OK\n");
379 jcr->setJobStatus(JS_WaitFD);
380 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
384 jcr->setJobStatus(JS_Running);
385 fd = jcr->file_bsock;
387 if (!send_include_list(jcr)) {
391 if (!send_exclude_list(jcr)) {
395 if (!send_level_command(jcr)) {
400 * send Storage daemon address to the File daemon
403 if (store->SDDport == 0) {
404 store->SDDport = store->SDport;
407 /* TLS Requirement */
408 if (store->tls_enable) {
409 if (store->tls_require) {
410 tls_need = BNET_TLS_REQUIRED;
412 tls_need = BNET_TLS_OK;
416 fd->fsend(storaddr, store->address, store->SDDport, tls_need);
417 if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
421 if (!send_runscripts_commands(jcr)) {
426 * We re-update the job start record so that the start
427 * time is set after the run before job. This avoids
428 * that any files created by the run before job will
429 * be saved twice. They will be backed up in the current
430 * job, but not in the next one unless they are changed.
431 * Without this, they will be backed up in this job and
432 * in the next job run because in that case, their date
433 * is after the start of this run.
435 jcr->start_time = time(NULL);
436 jcr->jr.StartTime = jcr->start_time;
437 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
438 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
442 * If backup is in accurate mode, we send the list of
445 if (!send_accurate_current_files(jcr)) {
449 /* Send backup command */
450 fd->fsend(backupcmd);
451 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
455 /* Pickup Job termination data */
456 stat = wait_for_job_termination(jcr);
457 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
460 !db_commit_base_file_attributes_record(jcr, jcr->db))
462 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
465 if (stat == JS_Terminated) {
466 backup_cleanup(jcr, stat);
471 /* Come here only after starting SD thread */
473 jcr->setJobStatus(JS_ErrorTerminated);
474 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
476 wait_for_job_termination(jcr, FDConnectTimeout);
477 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
483 * Here we wait for the File daemon to signal termination,
484 * then we wait for the Storage daemon. When both
485 * are done, we return the job status.
486 * Also used by restore.c
488 int wait_for_job_termination(JCR *jcr, int timeout)
491 BSOCK *fd = jcr->file_bsock;
493 uint32_t JobFiles, JobErrors;
494 uint32_t JobWarnings = 0;
495 uint64_t ReadBytes = 0;
496 uint64_t JobBytes = 0;
501 jcr->setJobStatus(JS_Running);
505 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
507 /* Wait for Client to terminate */
508 while ((n = bget_dirmsg(fd)) >= 0) {
510 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
511 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
512 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
513 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
515 jcr->setJobStatus(jcr->FDJobStatus);
516 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
518 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
521 if (job_canceled(jcr)) {
526 stop_bsock_timer(tid);
529 if (is_bnet_error(fd)) {
530 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
531 job_type_to_str(jcr->getJobType()), fd->bstrerror());
533 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
536 /* Force cancel in SD if failing */
537 if (job_canceled(jcr) || !fd_ok) {
538 cancel_storage_daemon_job(jcr);
541 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
542 wait_for_storage_daemon_termination(jcr);
544 /* Return values from FD */
546 jcr->JobFiles = JobFiles;
547 jcr->JobErrors += JobErrors; /* Keep total errors */
548 jcr->ReadBytes = ReadBytes;
549 jcr->JobBytes = JobBytes;
550 jcr->JobWarnings = JobWarnings;
552 jcr->Encrypt = Encrypt;
554 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
557 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
558 // jcr->JobStatus, jcr->SDJobStatus);
560 /* Return the first error status we find Dir, FD, or SD */
561 if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
562 jcr->FDJobStatus = JS_ErrorTerminated;
564 if (jcr->JobStatus != JS_Terminated) {
565 return jcr->JobStatus;
567 if (jcr->FDJobStatus != JS_Terminated) {
568 return jcr->FDJobStatus;
570 return jcr->SDJobStatus;
574 * Release resources allocated during backup.
576 void backup_cleanup(JCR *jcr, int TermCode)
578 char sdt[50], edt[50], schedt[50];
579 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
580 char ec6[30], ec7[30], ec8[30], elapsed[50];
581 char term_code[100], fd_term_msg[100], sd_term_msg[100];
582 const char *term_msg;
583 int msg_type = M_INFO;
586 double kbps, compression;
590 if (jcr->getJobLevel() == L_VIRTUAL_FULL) {
591 vbackup_cleanup(jcr, TermCode);
595 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
596 memset(&mr, 0, sizeof(mr));
597 memset(&cr, 0, sizeof(cr));
600 if (jcr->getJobStatus() == JS_Terminated &&
601 (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
602 TermCode = JS_Warnings;
606 update_job_end(jcr, TermCode);
608 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
609 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
610 db_strerror(jcr->db));
611 jcr->setJobStatus(JS_ErrorTerminated);
614 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
615 if (!db_get_client_record(jcr, jcr->db, &cr)) {
616 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
617 db_strerror(jcr->db));
620 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
621 if (!db_get_media_record(jcr, jcr->db, &mr)) {
622 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
623 mr.VolumeName, db_strerror(jcr->db));
624 jcr->setJobStatus(JS_ErrorTerminated);
627 update_bootstrap_file(jcr);
629 switch (jcr->JobStatus) {
631 if (jcr->JobErrors || jcr->SDErrors) {
632 term_msg = _("Backup OK -- with warnings");
634 term_msg = _("Backup OK");
638 term_msg = _("Backup OK -- with warnings");
641 case JS_ErrorTerminated:
642 term_msg = _("*** Backup Error ***");
643 msg_type = M_ERROR; /* Generate error message */
644 if (jcr->store_bsock) {
645 jcr->store_bsock->signal(BNET_TERMINATE);
646 if (jcr->SD_msg_chan) {
647 pthread_cancel(jcr->SD_msg_chan);
652 term_msg = _("Backup Canceled");
653 if (jcr->store_bsock) {
654 jcr->store_bsock->signal(BNET_TERMINATE);
655 if (jcr->SD_msg_chan) {
656 pthread_cancel(jcr->SD_msg_chan);
661 term_msg = term_code;
662 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
665 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
666 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
667 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
668 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
672 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
674 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
676 * Note, if the job has erred, most likely it did not write any
677 * tape, so suppress this "error" message since in that case
678 * it is normal. Or look at it the other way, only for a
679 * normal exit should we complain about this error.
681 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
682 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
684 jcr->VolumeName[0] = 0; /* none */
687 if (jcr->ReadBytes == 0) {
688 bstrncpy(compress, "None", sizeof(compress));
690 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
691 if (compression < 0.5) {
692 bstrncpy(compress, "None", sizeof(compress));
694 bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
697 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
698 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
701 Mmsg(base_info, " Base files/Used files: %lld/%lld (%.2f%%)\n",
703 jcr->nb_base_files_used,
704 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
706 // bmicrosleep(15, 0); /* for debugging SIGHUP */
708 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
709 " Build OS: %s %s %s\n"
712 " Backup Level: %s%s\n"
713 " Client: \"%s\" %s\n"
714 " FileSet: \"%s\" %s\n"
715 " Pool: \"%s\" (From %s)\n"
716 " Catalog: \"%s\" (From %s)\n"
717 " Storage: \"%s\" (From %s)\n"
718 " Scheduled time: %s\n"
721 " Elapsed time: %s\n"
723 " FD Files Written: %s\n"
724 " SD Files Written: %s\n"
725 " FD Bytes Written: %s (%sB)\n"
726 " SD Bytes Written: %s (%sB)\n"
728 " Software Compression: %s\n"
729 "%s" /* Basefile info */
733 " Volume name(s): %s\n"
734 " Volume Session Id: %d\n"
735 " Volume Session Time: %d\n"
736 " Last Volume Bytes: %s (%sB)\n"
737 " Non-fatal FD errors: %d\n"
739 " FD termination status: %s\n"
740 " SD termination status: %s\n"
741 " Termination: %s\n\n"),
742 BACULA, my_name, VERSION, LSMDATE, edt,
743 HOST_OS, DISTNAME, DISTVER,
746 level_to_str(jcr->getJobLevel()), jcr->since,
747 jcr->client->name(), cr.Uname,
748 jcr->fileset->name(), jcr->FSCreateTime,
749 jcr->pool->name(), jcr->pool_source,
750 jcr->catalog->name(), jcr->catalog_source,
751 jcr->wstore->name(), jcr->wstore_source,
755 edit_utime(RunTime, elapsed, sizeof(elapsed)),
757 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
758 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
759 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
760 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
761 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
762 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
766 jcr->VSS?_("yes"):_("no"),
767 jcr->Encrypt?_("yes"):_("no"),
768 jcr->accurate?_("yes"):_("no"),
772 edit_uint64_with_commas(mr.VolBytes, ec7),
773 edit_uint64_with_suffix(mr.VolBytes, ec8),
780 Dmsg0(100, "Leave backup_cleanup()\n");
783 void update_bootstrap_file(JCR *jcr)
785 /* Now update the bootstrap file if any */
786 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
787 jcr->job->WriteBootstrap) {
791 POOLMEM *fname = get_pool_memory(PM_FNAME);
792 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
794 VOL_PARAMS *VolParams = NULL;
796 char edt[50], ed1[50], ed2[50];
800 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
801 fd = bpipe ? bpipe->wfd : NULL;
803 /* ***FIXME*** handle BASE */
804 fd = fopen(fname, jcr->getJobLevel()==L_FULL?"w+b":"a+b");
807 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
810 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
811 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
812 if (jcr->SDJobFiles != 0) {
813 jcr->setJobStatus(JS_ErrorTerminated);
817 /* Start output with when and who wrote it */
818 bstrftimes(edt, sizeof(edt), time(NULL));
819 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
820 level_to_str(jcr->getJobLevel()), jcr->since);
821 for (int i=0; i < VolCount; i++) {
822 /* Write the record */
823 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
824 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
825 if (VolParams[i].Slot > 0) {
826 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
828 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
829 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
830 fprintf(fd, "VolAddr=%s-%s\n",
831 edit_uint64(VolParams[i].StartAddr, ed1),
832 edit_uint64(VolParams[i].EndAddr, ed2));
833 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
834 VolParams[i].LastIndex);
846 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
847 "%s: ERR=%s\n"), fname, be.bstrerror());
848 jcr->setJobStatus(JS_ErrorTerminated);
850 free_pool_memory(fname);