2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 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)
245 /* In base level, no previous job is used and no restart incomplete jobs */
246 if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
249 if (!jcr->accurate) {
253 if (jcr->is_JobLevel(L_FULL)) {
254 /* On Full mode, if no previous base job, no accurate things */
255 if (get_base_jobids(jcr, &jobids)) {
257 Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
262 /* For Incr/Diff level, we search for older jobs */
263 db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
265 /* We are in Incr/Diff, but no Full to build the accurate list... */
266 if (jobids.count == 0) {
267 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
268 return false; /* fail */
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");
287 return false; /* Fail */
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, jcr->use_accurate_chksum,
294 accurate_list_handler, (void *)jcr);
297 db_get_file_list(jcr, jcr->db_batch,
298 jobids.list, jcr->use_accurate_chksum, false /* no delta */,
299 accurate_list_handler, (void *)jcr);
302 /* TODO: close the batch connection ? (can be used very soon) */
304 jcr->file_bsock->signal(BNET_EOD);
309 * Do a backup of the specified FileSet
311 * Returns: false on failure
314 bool do_backup(JCR *jcr)
317 int tls_need = BNET_TLS_NONE;
324 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
325 return do_vbackup(jcr);
328 /* Print Job Start message */
329 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
330 edit_uint64(jcr->JobId, ed1), jcr->Job);
332 jcr->setJobStatus(JS_Running);
333 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
334 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
335 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
340 * Open a message channel connection with the Storage
341 * daemon. This is to let him know that our client
342 * will be contacting him for a backup session.
345 Dmsg0(110, "Open connection with storage daemon\n");
346 jcr->setJobStatus(JS_WaitSD);
348 * Start conversation with Storage daemon
350 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
354 * Now start a job with the Storage daemon
356 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
361 * Start the job prior to starting the message thread below
362 * to avoid two threads from using the BSOCK structure at
365 if (!jcr->store_bsock->fsend("run")) {
370 * Now start a Storage daemon message thread. Note,
371 * this thread is used to provide the catalog services
372 * for the backup job, including inserting the attributes
373 * into the catalog. See catalog_update() in catreq.c
375 if (!start_storage_daemon_message_thread(jcr)) {
378 Dmsg0(150, "Storage daemon connection OK\n");
380 jcr->setJobStatus(JS_WaitFD);
381 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
385 jcr->setJobStatus(JS_Running);
386 fd = jcr->file_bsock;
388 if (!send_include_list(jcr)) {
392 if (!send_exclude_list(jcr)) {
396 if (!send_level_command(jcr)) {
400 /* TODO: See priority with bandwidth parameter */
401 if (jcr->job->max_bandwidth > 0) {
402 jcr->max_bandwidth = jcr->job->max_bandwidth;
403 } else if (jcr->client->max_bandwidth > 0) {
404 jcr->max_bandwidth = jcr->client->max_bandwidth;
407 if (jcr->max_bandwidth > 0) {
408 send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
412 * send Storage daemon address to the File daemon
415 if (store->SDDport == 0) {
416 store->SDDport = store->SDport;
419 /* TLS Requirement */
420 if (store->tls_enable) {
421 if (store->tls_require) {
422 tls_need = BNET_TLS_REQUIRED;
424 tls_need = BNET_TLS_OK;
428 fd->fsend(storaddr, store->address, store->SDDport, tls_need);
429 if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
433 if (!send_runscripts_commands(jcr)) {
438 * We re-update the job start record so that the start
439 * time is set after the run before job. This avoids
440 * that any files created by the run before job will
441 * be saved twice. They will be backed up in the current
442 * job, but not in the next one unless they are changed.
443 * Without this, they will be backed up in this job and
444 * in the next job run because in that case, their date
445 * is after the start of this run.
447 jcr->start_time = time(NULL);
448 jcr->jr.StartTime = jcr->start_time;
449 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
450 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
454 * If backup is in accurate mode, we send the list of
457 if (!send_accurate_current_files(jcr)) {
458 goto bail_out; /* error */
461 /* Send backup command */
462 fd->fsend(backupcmd, jcr->JobFiles);
463 Dmsg1(100, ">filed: %s", fd->msg);
464 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
468 /* Pickup Job termination data */
469 stat = wait_for_job_termination(jcr);
470 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
472 if (jcr->HasBase && !db_commit_base_file_attributes_record(jcr, jcr->db)) {
473 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
476 if (stat == JS_Terminated) {
477 backup_cleanup(jcr, stat);
482 /* Come here only after starting SD thread */
484 jcr->setJobStatus(JS_ErrorTerminated);
485 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
487 wait_for_job_termination(jcr, FDConnectTimeout);
488 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
494 * Here we wait for the File daemon to signal termination,
495 * then we wait for the Storage daemon. When both
496 * are done, we return the job status.
497 * Also used by restore.c
499 int wait_for_job_termination(JCR *jcr, int timeout)
502 BSOCK *fd = jcr->file_bsock;
504 uint32_t JobFiles, JobErrors;
505 uint32_t JobWarnings = 0;
506 uint64_t ReadBytes = 0;
507 uint64_t JobBytes = 0;
512 jcr->setJobStatus(JS_Running);
516 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
518 /* Wait for Client to terminate */
519 while ((n = bget_dirmsg(fd)) >= 0) {
521 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
522 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
523 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
524 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
526 jcr->setJobStatus(jcr->FDJobStatus);
527 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
529 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
532 if (job_canceled(jcr)) {
537 stop_bsock_timer(tid);
540 if (is_bnet_error(fd)) {
542 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
543 job_type_to_str(jcr->getJobType()), fd->bstrerror());
544 while (i++ < 10 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
549 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
553 * Force cancel in SD if failing, but not for Incomplete jobs
554 * so that we let the SD despool.
556 Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
557 jcr->JobStatus, jcr->SDJobStatus);
558 if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
559 Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
560 jcr->JobStatus, jcr->SDJobStatus);
561 cancel_storage_daemon_job(jcr);
564 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
565 wait_for_storage_daemon_termination(jcr);
567 /* Return values from FD */
569 jcr->JobFiles = JobFiles;
570 jcr->JobErrors += JobErrors; /* Keep total errors */
571 jcr->ReadBytes = ReadBytes;
572 jcr->JobBytes = JobBytes;
573 jcr->JobWarnings = JobWarnings;
575 jcr->Encrypt = Encrypt;
577 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
580 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
581 // jcr->JobStatus, jcr->SDJobStatus);
583 /* Return the first error status we find Dir, FD, or SD */
584 if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
585 jcr->FDJobStatus = JS_ErrorTerminated;
587 if (jcr->JobStatus != JS_Terminated) {
588 return jcr->JobStatus;
590 if (jcr->FDJobStatus != JS_Terminated) {
591 return jcr->FDJobStatus;
593 return jcr->SDJobStatus;
597 * Release resources allocated during backup.
599 void backup_cleanup(JCR *jcr, int TermCode)
601 char sdt[50], edt[50], schedt[50];
602 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
603 char ec6[30], ec7[30], ec8[30], elapsed[50];
604 char term_code[100], fd_term_msg[100], sd_term_msg[100];
605 const char *term_msg;
606 int msg_type = M_INFO;
609 double kbps, compression;
613 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
614 vbackup_cleanup(jcr, TermCode);
618 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
619 memset(&mr, 0, sizeof(mr));
620 memset(&cr, 0, sizeof(cr));
623 if (jcr->getJobStatus() == JS_Terminated &&
624 (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
625 TermCode = JS_Warnings;
629 update_job_end(jcr, TermCode);
631 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
632 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
633 db_strerror(jcr->db));
634 jcr->setJobStatus(JS_ErrorTerminated);
637 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
638 if (!db_get_client_record(jcr, jcr->db, &cr)) {
639 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
640 db_strerror(jcr->db));
643 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
644 if (!db_get_media_record(jcr, jcr->db, &mr)) {
645 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
646 mr.VolumeName, db_strerror(jcr->db));
647 jcr->setJobStatus(JS_ErrorTerminated);
650 update_bootstrap_file(jcr);
652 switch (jcr->JobStatus) {
654 if (jcr->JobErrors || jcr->SDErrors) {
655 term_msg = _("Backup OK -- with warnings");
657 term_msg = _("Backup OK");
661 term_msg = _("Backup failed -- incomplete");
664 term_msg = _("Backup OK -- with warnings");
667 case JS_ErrorTerminated:
668 term_msg = _("*** Backup Error ***");
669 msg_type = M_ERROR; /* Generate error message */
670 if (jcr->store_bsock) {
671 jcr->store_bsock->signal(BNET_TERMINATE);
672 if (jcr->SD_msg_chan) {
673 pthread_cancel(jcr->SD_msg_chan);
678 term_msg = _("Backup Canceled");
679 if (jcr->store_bsock) {
680 jcr->store_bsock->signal(BNET_TERMINATE);
681 if (jcr->SD_msg_chan) {
682 pthread_cancel(jcr->SD_msg_chan);
687 term_msg = term_code;
688 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
691 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
692 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
693 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
694 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
698 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
700 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
702 * Note, if the job has erred, most likely it did not write any
703 * tape, so suppress this "error" message since in that case
704 * it is normal. Or look at it the other way, only for a
705 * normal exit should we complain about this error.
707 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
708 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
710 jcr->VolumeName[0] = 0; /* none */
713 if (jcr->ReadBytes == 0) {
714 bstrncpy(compress, "None", sizeof(compress));
716 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
717 if (compression < 0.5) {
718 bstrncpy(compress, "None", sizeof(compress));
720 bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
723 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
724 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
727 Mmsg(base_info, " Base files/Used files: %lld/%lld (%.2f%%)\n",
729 jcr->nb_base_files_used,
730 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
732 // bmicrosleep(15, 0); /* for debugging SIGHUP */
734 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
735 " Build OS: %s %s %s\n"
738 " Backup Level: %s%s\n"
739 " Client: \"%s\" %s\n"
740 " FileSet: \"%s\" %s\n"
741 " Pool: \"%s\" (From %s)\n"
742 " Catalog: \"%s\" (From %s)\n"
743 " Storage: \"%s\" (From %s)\n"
744 " Scheduled time: %s\n"
747 " Elapsed time: %s\n"
749 " FD Files Written: %s\n"
750 " SD Files Written: %s\n"
751 " FD Bytes Written: %s (%sB)\n"
752 " SD Bytes Written: %s (%sB)\n"
754 " Software Compression: %s\n"
755 "%s" /* Basefile info */
759 " Volume name(s): %s\n"
760 " Volume Session Id: %d\n"
761 " Volume Session Time: %d\n"
762 " Last Volume Bytes: %s (%sB)\n"
763 " Non-fatal FD errors: %d\n"
765 " FD termination status: %s\n"
766 " SD termination status: %s\n"
767 " Termination: %s\n\n"),
768 BACULA, my_name, VERSION, LSMDATE,
769 HOST_OS, DISTNAME, DISTVER,
772 level_to_str(jcr->getJobLevel()), jcr->since,
773 jcr->client->name(), cr.Uname,
774 jcr->fileset->name(), jcr->FSCreateTime,
775 jcr->pool->name(), jcr->pool_source,
776 jcr->catalog->name(), jcr->catalog_source,
777 jcr->wstore->name(), jcr->wstore_source,
781 edit_utime(RunTime, elapsed, sizeof(elapsed)),
783 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
784 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
785 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
786 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
787 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
788 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
792 jcr->VSS?_("yes"):_("no"),
793 jcr->Encrypt?_("yes"):_("no"),
794 jcr->accurate?_("yes"):_("no"),
798 edit_uint64_with_commas(mr.VolBytes, ec7),
799 edit_uint64_with_suffix(mr.VolBytes, ec8),
806 Dmsg0(100, "Leave backup_cleanup()\n");
809 void update_bootstrap_file(JCR *jcr)
811 /* Now update the bootstrap file if any */
812 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
813 jcr->job->WriteBootstrap) {
817 POOLMEM *fname = get_pool_memory(PM_FNAME);
818 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
820 VOL_PARAMS *VolParams = NULL;
822 char edt[50], ed1[50], ed2[50];
826 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
827 fd = bpipe ? bpipe->wfd : NULL;
829 /* ***FIXME*** handle BASE */
830 fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
833 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
836 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
837 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
838 if (jcr->SDJobFiles != 0) {
839 jcr->setJobStatus(JS_ErrorTerminated);
843 /* Start output with when and who wrote it */
844 bstrftimes(edt, sizeof(edt), time(NULL));
845 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
846 level_to_str(jcr->getJobLevel()), jcr->since);
847 for (int i=0; i < VolCount; i++) {
848 /* Write the record */
849 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
850 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
851 if (VolParams[i].Slot > 0) {
852 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
854 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
855 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
856 fprintf(fd, "VolAddr=%s-%s\n",
857 edit_uint64(VolParams[i].StartAddr, ed1),
858 edit_uint64(VolParams[i].EndAddr, ed2));
859 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
860 VolParams[i].LastIndex);
872 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
873 "%s: ERR=%s\n"), fname, be.bstrerror());
874 jcr->setJobStatus(JS_ErrorTerminated);
876 free_pool_memory(fname);