2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2012 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 three of the GNU Affero 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 Affero 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.
47 /* Commands sent to File daemon */
48 static char backupcmd[] = "backup FileIndex=%ld\n";
49 static char storaddr[] = "storage address=%s port=%d ssl=%d\n";
51 /* Responses received from File daemon */
52 static char OKbackup[] = "2000 OK backup\n";
53 static char OKstore[] = "2000 OK storage\n";
54 static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
55 "ReadBytes=%llu JobBytes=%llu Errors=%u "
56 "VSS=%d Encrypt=%d\n";
57 /* Pre 1.39.29 (04Dec06) EndJob */
58 static char OldEndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
59 "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
61 * Called here before the job is run to do the job
64 bool do_backup_init(JCR *jcr)
67 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
68 return do_vbackup_init(jcr);
70 free_rstorage(jcr); /* we don't read so release */
72 if (!get_or_create_fileset_record(jcr)) {
77 * Get definitive Job level and since time
79 get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
81 apply_pool_overrides(jcr);
83 if (!allow_duplicate_job(jcr)) {
87 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
88 if (jcr->jr.PoolId == 0) {
92 /* If pool storage specified, use it instead of job storage */
93 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
96 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
100 create_clones(jcr); /* run any clone jobs */
105 /* Take all base jobs from job resource and find the
108 static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids)
115 if (!jcr->job->base) {
116 return false; /* no base job, stop accurate */
119 memset(&jr, 0, sizeof(JOB_DBR));
120 jr.StartTime = jcr->jr.StartTime;
122 foreach_alist(job, jcr->job->base) {
123 bstrncpy(jr.Name, job->name(), sizeof(jr.Name));
124 db_get_base_jobid(jcr, jcr->db, &jr, &id);
128 pm_strcat(jobids->list, ",");
130 pm_strcat(jobids->list, edit_uint64(id, str_jobid));
135 return jobids->count > 0;
139 * Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
140 * row[0]=Path, row[1]=Filename, row[2]=FileIndex
141 * row[3]=JobId row[4]=LStat row[5]=DeltaSeq row[6]=MD5
143 static int accurate_list_handler(void *ctx, int num_fields, char **row)
145 JCR *jcr = (JCR *)ctx;
147 if (job_canceled(jcr)) {
151 if (row[2][0] == '0') { /* discard when file_index == 0 */
155 /* sending with checksum */
156 if (jcr->use_accurate_chksum
158 && row[6][0] /* skip checksum = '0' */
161 jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s",
162 row[0], row[1], 0, row[4], 0, row[6], 0, row[5]);
164 jcr->file_bsock->fsend("%s%s%c%s%c%c%s",
165 row[0], row[1], 0, row[4], 0, 0, row[5]);
170 /* In this procedure, we check if the current fileset is using checksum
171 * FileSet-> Include-> Options-> Accurate/Verify/BaseJob=checksum
172 * This procedure uses jcr->HasBase, so it must be call after the initialization
174 static bool is_checksum_needed_by_fileset(JCR *jcr)
180 bool have_basejob_option=false;
181 if (!jcr->job || !jcr->job->fileset) {
185 f = jcr->job->fileset;
187 for (int i=0; i < f->num_includes; i++) { /* Parse all Include {} */
188 inc = f->include_items[i];
190 for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
191 fopts = inc->opts_list[j];
193 for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
195 case 'V': /* verify */
196 in_block = (jcr->getJobType() == JT_VERIFY); /* not used now */
198 case 'J': /* Basejob keyword */
199 have_basejob_option = in_block = jcr->HasBase;
201 case 'C': /* Accurate keyword */
202 in_block = !jcr->is_JobLevel(L_FULL);
204 case ':': /* End of keyword */
210 Dmsg0(50, "Checksum will be sent to FD\n");
221 /* By default for BaseJobs, we send the checksum */
222 if (!have_basejob_option && jcr->HasBase) {
226 Dmsg0(50, "Checksum will be sent to FD\n");
231 * Send current file list to FD
232 * DIR -> FD : accurate files=xxxx
233 * DIR -> FD : /path/to/file\0Lstat\0MD5\0Delta
234 * DIR -> FD : /path/to/dir/\0Lstat\0MD5\0Delta
238 bool send_accurate_current_files(JCR *jcr)
244 /* In base level, no previous job is used and no restart incomplete jobs */
245 if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
248 if (!jcr->accurate) {
252 if (jcr->is_JobLevel(L_FULL)) {
253 /* On Full mode, if no previous base job, no accurate things */
254 if (get_base_jobids(jcr, &jobids)) {
256 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) {
266 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
267 return false; /* fail */
271 /* Don't send and store the checksum if fileset doesn't require it */
272 jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
274 if (jcr->JobId) { /* display the message only for real jobs */
275 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
278 /* to be able to allocate the right size for htable */
279 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
280 db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
281 Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
282 jcr->file_bsock->fsend("accurate files=%s\n", nb.list);
284 if (!db_open_batch_connexion(jcr, jcr->db)) {
285 Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
286 return false; /* Fail */
290 jcr->nb_base_files = str_to_int64(nb.list);
291 db_create_base_file_list(jcr, jcr->db, jobids.list);
292 db_get_base_file_list(jcr, jcr->db, jcr->use_accurate_chksum,
293 accurate_list_handler, (void *)jcr);
296 db_get_file_list(jcr, jcr->db_batch,
297 jobids.list, jcr->use_accurate_chksum, false /* no delta */,
298 accurate_list_handler, (void *)jcr);
301 /* TODO: close the batch connection ? (can be used very soon) */
303 jcr->file_bsock->signal(BNET_EOD);
308 * Do a backup of the specified FileSet
310 * Returns: false on failure
313 bool do_backup(JCR *jcr)
316 int tls_need = BNET_TLS_NONE;
323 if (jcr->is_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 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 (!jcr->store_bsock->fsend("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_level_command(jcr)) {
391 if (!send_include_list(jcr)) {
395 if (!send_exclude_list(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 /* Declare the job started to start the MaxRunTime check */
422 jcr->setJobStarted();
424 /* Send and run the RunBefore */
425 if (!send_runscripts_commands(jcr)) {
430 * We re-update the job start record so that the start
431 * time is set after the run before job. This avoids
432 * that any files created by the run before job will
433 * be saved twice. They will be backed up in the current
434 * job, but not in the next one unless they are changed.
435 * Without this, they will be backed up in this job and
436 * in the next job run because in that case, their date
437 * is after the start of this run.
439 jcr->start_time = time(NULL);
440 jcr->jr.StartTime = jcr->start_time;
441 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
442 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
446 * If backup is in accurate mode, we send the list of
449 if (!send_accurate_current_files(jcr)) {
450 goto bail_out; /* error */
453 /* Send backup command */
454 fd->fsend(backupcmd, jcr->JobFiles);
455 Dmsg1(100, ">filed: %s", fd->msg);
456 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
460 /* Pickup Job termination data */
461 stat = wait_for_job_termination(jcr);
462 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
464 if (jcr->HasBase && !db_commit_base_file_attributes_record(jcr, jcr->db)) {
465 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
468 if (stat == JS_Terminated) {
469 backup_cleanup(jcr, stat);
474 /* Come here only after starting SD thread */
476 jcr->setJobStatus(JS_ErrorTerminated);
477 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
479 wait_for_job_termination(jcr, FDConnectTimeout);
480 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
486 * Here we wait for the File daemon to signal termination,
487 * then we wait for the Storage daemon. When both
488 * are done, we return the job status.
489 * Also used by restore.c
491 int wait_for_job_termination(JCR *jcr, int timeout)
494 BSOCK *fd = jcr->file_bsock;
496 uint32_t JobFiles, JobErrors;
497 uint32_t JobWarnings = 0;
498 uint64_t ReadBytes = 0;
499 uint64_t JobBytes = 0;
504 jcr->setJobStatus(JS_Running);
508 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
510 /* Wait for Client to terminate */
511 while ((n = bget_dirmsg(fd)) >= 0) {
513 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
514 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
515 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
516 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
518 jcr->setJobStatus(jcr->FDJobStatus);
519 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
521 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
524 if (job_canceled(jcr)) {
529 stop_bsock_timer(tid);
532 if (is_bnet_error(fd)) {
534 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
535 job_type_to_str(jcr->getJobType()), fd->bstrerror());
536 while (i++ < 10 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
541 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
545 * Force cancel in SD if failing, but not for Incomplete jobs
546 * so that we let the SD despool.
548 Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
549 jcr->JobStatus, jcr->SDJobStatus);
550 if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
551 Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
552 jcr->JobStatus, jcr->SDJobStatus);
553 cancel_storage_daemon_job(jcr);
556 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
557 wait_for_storage_daemon_termination(jcr);
559 /* Return values from FD */
561 jcr->JobFiles = JobFiles;
562 jcr->JobErrors += JobErrors; /* Keep total errors */
563 jcr->ReadBytes = ReadBytes;
564 jcr->JobBytes = JobBytes;
565 jcr->JobWarnings = JobWarnings;
567 jcr->Encrypt = Encrypt;
569 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
572 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
573 // jcr->JobStatus, jcr->SDJobStatus);
575 /* Return the first error status we find Dir, FD, or SD */
576 if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
577 jcr->FDJobStatus = JS_ErrorTerminated;
579 if (jcr->JobStatus != JS_Terminated) {
580 return jcr->JobStatus;
582 if (jcr->FDJobStatus != JS_Terminated) {
583 return jcr->FDJobStatus;
585 return jcr->SDJobStatus;
589 * Release resources allocated during backup.
591 void backup_cleanup(JCR *jcr, int TermCode)
593 char sdt[50], edt[50], schedt[50];
594 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
595 char ec6[30], ec7[30], ec8[30], elapsed[50];
596 char term_code[100], fd_term_msg[100], sd_term_msg[100];
597 const char *term_msg;
598 int msg_type = M_INFO;
601 double kbps, compression;
605 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
606 vbackup_cleanup(jcr, TermCode);
610 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
611 memset(&cr, 0, sizeof(cr));
614 /* The current implementation of the JS_Warning status is not
615 * completed. SQL part looks to be ok, but the code is using
616 * JS_Terminated almost everywhere instead of (JS_Terminated || JS_Warning)
617 * as we do with is_canceled()
619 if (jcr->getJobStatus() == JS_Terminated &&
620 (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
621 TermCode = JS_Warnings;
625 update_job_end(jcr, TermCode);
627 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
628 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
629 db_strerror(jcr->db));
630 jcr->setJobStatus(JS_ErrorTerminated);
633 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
634 if (!db_get_client_record(jcr, jcr->db, &cr)) {
635 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
636 db_strerror(jcr->db));
639 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
640 if (!db_get_media_record(jcr, jcr->db, &mr)) {
641 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
642 mr.VolumeName, db_strerror(jcr->db));
643 jcr->setJobStatus(JS_ErrorTerminated);
646 update_bootstrap_file(jcr);
648 switch (jcr->JobStatus) {
650 if (jcr->JobErrors || jcr->SDErrors) {
651 term_msg = _("Backup OK -- with warnings");
653 term_msg = _("Backup OK");
657 term_msg = _("Backup failed -- incomplete");
660 term_msg = _("Backup OK -- with warnings");
663 case JS_ErrorTerminated:
664 term_msg = _("*** Backup Error ***");
665 msg_type = M_ERROR; /* Generate error message */
666 if (jcr->store_bsock) {
667 jcr->store_bsock->signal(BNET_TERMINATE);
668 if (jcr->SD_msg_chan) {
669 pthread_cancel(jcr->SD_msg_chan);
674 term_msg = _("Backup Canceled");
675 if (jcr->store_bsock) {
676 jcr->store_bsock->signal(BNET_TERMINATE);
677 if (jcr->SD_msg_chan) {
678 pthread_cancel(jcr->SD_msg_chan);
683 term_msg = term_code;
684 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
687 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
688 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
689 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
690 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
694 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
696 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
698 * Note, if the job has erred, most likely it did not write any
699 * tape, so suppress this "error" message since in that case
700 * it is normal. Or look at it the other way, only for a
701 * normal exit should we complain about this error.
703 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
704 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
706 jcr->VolumeName[0] = 0; /* none */
709 if (jcr->ReadBytes == 0) {
710 bstrncpy(compress, "None", sizeof(compress));
712 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
713 if (compression < 0.5) {
714 bstrncpy(compress, "None", sizeof(compress));
716 bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
719 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
720 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
723 Mmsg(base_info, " Base files/Used files: %lld/%lld (%.2f%%)\n",
725 jcr->nb_base_files_used,
726 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
728 // bmicrosleep(15, 0); /* for debugging SIGHUP */
730 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
731 " Build OS: %s %s %s\n"
734 " Backup Level: %s%s\n"
735 " Client: \"%s\" %s\n"
736 " FileSet: \"%s\" %s\n"
737 " Pool: \"%s\" (From %s)\n"
738 " Catalog: \"%s\" (From %s)\n"
739 " Storage: \"%s\" (From %s)\n"
740 " Scheduled time: %s\n"
743 " Elapsed time: %s\n"
745 " FD Files Written: %s\n"
746 " SD Files Written: %s\n"
747 " FD Bytes Written: %s (%sB)\n"
748 " SD Bytes Written: %s (%sB)\n"
750 " Software Compression: %s\n"
751 "%s" /* Basefile info */
755 " Volume name(s): %s\n"
756 " Volume Session Id: %d\n"
757 " Volume Session Time: %d\n"
758 " Last Volume Bytes: %s (%sB)\n"
759 " Non-fatal FD errors: %d\n"
761 " FD termination status: %s\n"
762 " SD termination status: %s\n"
763 " Termination: %s\n\n"),
764 BACULA, my_name, VERSION, LSMDATE,
765 HOST_OS, DISTNAME, DISTVER,
768 level_to_str(jcr->getJobLevel()), jcr->since,
769 jcr->client->name(), cr.Uname,
770 jcr->fileset->name(), jcr->FSCreateTime,
771 jcr->pool->name(), jcr->pool_source,
772 jcr->catalog->name(), jcr->catalog_source,
773 jcr->wstore->name(), jcr->wstore_source,
777 edit_utime(RunTime, elapsed, sizeof(elapsed)),
779 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
780 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
781 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
782 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
783 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
784 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
788 jcr->VSS?_("yes"):_("no"),
789 jcr->Encrypt?_("yes"):_("no"),
790 jcr->accurate?_("yes"):_("no"),
794 edit_uint64_with_commas(mr.VolBytes, ec7),
795 edit_uint64_with_suffix(mr.VolBytes, ec8),
802 Dmsg0(100, "Leave backup_cleanup()\n");
805 void update_bootstrap_file(JCR *jcr)
807 /* Now update the bootstrap file if any */
808 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
809 jcr->job->WriteBootstrap) {
813 POOLMEM *fname = get_pool_memory(PM_FNAME);
814 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
816 VOL_PARAMS *VolParams = NULL;
818 char edt[50], ed1[50], ed2[50];
822 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
823 fd = bpipe ? bpipe->wfd : NULL;
825 /* ***FIXME*** handle BASE */
826 fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
829 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
832 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
833 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
834 if (jcr->SDJobFiles != 0) {
835 jcr->setJobStatus(JS_ErrorTerminated);
839 /* Start output with when and who wrote it */
840 bstrftimes(edt, sizeof(edt), time(NULL));
841 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
842 level_to_str(jcr->getJobLevel()), jcr->since);
843 for (int i=0; i < VolCount; i++) {
844 /* Write the record */
845 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
846 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
847 if (VolParams[i].Slot > 0) {
848 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
850 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
851 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
852 fprintf(fd, "VolAddr=%s-%s\n",
853 edit_uint64(VolParams[i].StartAddr, ed1),
854 edit_uint64(VolParams[i].EndAddr, ed2));
855 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
856 VolParams[i].LastIndex);
868 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
869 "%s: ERR=%s\n"), fname, be.bstrerror());
870 jcr->setJobStatus(JS_ErrorTerminated);
872 free_pool_memory(fname);