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
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->get_JobType() == 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->get_JobLevel() != 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->get_JobLevel() == L_BASE) {
252 if (jcr->get_JobLevel() == 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->get_JobLevel() == 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 set_jcr_job_status(jcr, 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 set_jcr_job_status(jcr, 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 set_jcr_job_status(jcr, JS_WaitFD);
380 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
384 set_jcr_job_status(jcr, 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 set_jcr_job_status(jcr, 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 set_jcr_job_status(jcr, 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 set_jcr_job_status(jcr, 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->get_JobType()), 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->get_JobLevel() == 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));
599 update_job_end(jcr, TermCode);
601 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
602 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
603 db_strerror(jcr->db));
604 set_jcr_job_status(jcr, JS_ErrorTerminated);
607 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
608 if (!db_get_client_record(jcr, jcr->db, &cr)) {
609 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
610 db_strerror(jcr->db));
613 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
614 if (!db_get_media_record(jcr, jcr->db, &mr)) {
615 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
616 mr.VolumeName, db_strerror(jcr->db));
617 set_jcr_job_status(jcr, JS_ErrorTerminated);
620 update_bootstrap_file(jcr);
622 switch (jcr->JobStatus) {
624 if (jcr->JobErrors || jcr->SDErrors) {
625 term_msg = _("Backup OK -- with warnings");
627 term_msg = _("Backup OK");
631 term_msg = _("Backup OK -- with warnings");
634 case JS_ErrorTerminated:
635 term_msg = _("*** Backup Error ***");
636 msg_type = M_ERROR; /* Generate error message */
637 if (jcr->store_bsock) {
638 jcr->store_bsock->signal(BNET_TERMINATE);
639 if (jcr->SD_msg_chan) {
640 pthread_cancel(jcr->SD_msg_chan);
645 term_msg = _("Backup Canceled");
646 if (jcr->store_bsock) {
647 jcr->store_bsock->signal(BNET_TERMINATE);
648 if (jcr->SD_msg_chan) {
649 pthread_cancel(jcr->SD_msg_chan);
654 term_msg = term_code;
655 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
658 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
659 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
660 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
661 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
665 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
667 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
669 * Note, if the job has erred, most likely it did not write any
670 * tape, so suppress this "error" message since in that case
671 * it is normal. Or look at it the other way, only for a
672 * normal exit should we complain about this error.
674 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
675 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
677 jcr->VolumeName[0] = 0; /* none */
680 if (jcr->ReadBytes == 0) {
681 bstrncpy(compress, "None", sizeof(compress));
683 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
684 if (compression < 0.5) {
685 bstrncpy(compress, "None", sizeof(compress));
687 bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
690 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
691 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
694 Mmsg(base_info, " Base files/Used files: %lld/%lld (%.2f%%)\n",
696 jcr->nb_base_files_used,
697 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
699 // bmicrosleep(15, 0); /* for debugging SIGHUP */
701 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
702 " Build OS: %s %s %s\n"
705 " Backup Level: %s%s\n"
706 " Client: \"%s\" %s\n"
707 " FileSet: \"%s\" %s\n"
708 " Pool: \"%s\" (From %s)\n"
709 " Catalog: \"%s\" (From %s)\n"
710 " Storage: \"%s\" (From %s)\n"
711 " Scheduled time: %s\n"
714 " Elapsed time: %s\n"
716 " FD Files Written: %s\n"
717 " SD Files Written: %s\n"
718 " FD Bytes Written: %s (%sB)\n"
719 " SD Bytes Written: %s (%sB)\n"
721 " Software Compression: %s\n"
722 "%s" /* Basefile info */
726 " Volume name(s): %s\n"
727 " Volume Session Id: %d\n"
728 " Volume Session Time: %d\n"
729 " Last Volume Bytes: %s (%sB)\n"
730 " Non-fatal FD errors: %d\n"
732 " FD termination status: %s\n"
733 " SD termination status: %s\n"
734 " Termination: %s\n\n"),
735 BACULA, my_name, VERSION, LSMDATE, edt,
736 HOST_OS, DISTNAME, DISTVER,
739 level_to_str(jcr->get_JobLevel()), jcr->since,
740 jcr->client->name(), cr.Uname,
741 jcr->fileset->name(), jcr->FSCreateTime,
742 jcr->pool->name(), jcr->pool_source,
743 jcr->catalog->name(), jcr->catalog_source,
744 jcr->wstore->name(), jcr->wstore_source,
748 edit_utime(RunTime, elapsed, sizeof(elapsed)),
750 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
751 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
752 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
753 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
754 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
755 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
759 jcr->VSS?_("yes"):_("no"),
760 jcr->Encrypt?_("yes"):_("no"),
761 jcr->accurate?_("yes"):_("no"),
765 edit_uint64_with_commas(mr.VolBytes, ec7),
766 edit_uint64_with_suffix(mr.VolBytes, ec8),
773 Dmsg0(100, "Leave backup_cleanup()\n");
776 void update_bootstrap_file(JCR *jcr)
778 /* Now update the bootstrap file if any */
779 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
780 jcr->job->WriteBootstrap) {
784 POOLMEM *fname = get_pool_memory(PM_FNAME);
785 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
787 VOL_PARAMS *VolParams = NULL;
789 char edt[50], ed1[50], ed2[50];
793 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
794 fd = bpipe ? bpipe->wfd : NULL;
796 /* ***FIXME*** handle BASE */
797 fd = fopen(fname, jcr->get_JobLevel()==L_FULL?"w+b":"a+b");
800 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
803 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
804 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
805 if (jcr->SDJobFiles != 0) {
806 set_jcr_job_status(jcr, JS_ErrorTerminated);
810 /* Start output with when and who wrote it */
811 bstrftimes(edt, sizeof(edt), time(NULL));
812 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
813 level_to_str(jcr->get_JobLevel()), jcr->since);
814 for (int i=0; i < VolCount; i++) {
815 /* Write the record */
816 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
817 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
818 if (VolParams[i].Slot > 0) {
819 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
821 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
822 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
823 fprintf(fd, "VolAddr=%s-%s\n",
824 edit_uint64(VolParams[i].StartAddr, ed1),
825 edit_uint64(VolParams[i].EndAddr, ed2));
826 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
827 VolParams[i].LastIndex);
839 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
840 "%s: ERR=%s\n"), fname, be.bstrerror());
841 set_jcr_job_status(jcr, JS_ErrorTerminated);
843 free_pool_memory(fname);