2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
18 * Bacula Director -- backup.c -- responsible for doing backup jobs
20 * Kern Sibbald, March MM
22 * Basic tasks done here:
23 * Open DB and create records for this job.
24 * Open Message Channel with Storage daemon to tell him a job will be starting.
25 * Open connection with File daemon and pass him commands
27 * When the File daemon finishes the job, update the DB.
35 /* Commands sent to File daemon */
36 static char backupcmd[] = "backup FileIndex=%ld\n";
37 static char storaddr[] = "storage address=%s port=%d ssl=%d\n";
39 /* Responses received from File daemon */
40 static char OKbackup[] = "2000 OK backup\n";
41 static char OKstore[] = "2000 OK storage\n";
43 static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
44 "ReadBytes=%llu JobBytes=%llu Errors=%u "
45 "VSS=%d Encrypt=%d\n";
46 /* Pre 1.39.29 (04Dec06) EndJob */
47 static char OldEndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
48 "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
50 /* Commands sent to Storage daemon */
51 static char clientaddr[] = "client address=%s port=%d ssl=%d\n";
53 /* Commands received from Storage daemon */
54 static char OKclient[] = "3000 OK client command\n";
57 * Called here before the job is run to do the job
60 bool do_backup_init(JCR *jcr)
63 jcr->RescheduleIncompleteJobs = jcr->job->RescheduleIncompleteJobs;
65 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
66 return do_vbackup_init(jcr);
68 free_rstorage(jcr); /* we don't read so release */
70 if (!get_or_create_fileset_record(jcr)) {
75 * Get definitive Job level and since time
77 get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
79 apply_pool_overrides(jcr);
81 if (!allow_duplicate_job(jcr)) {
85 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
86 if (jcr->jr.PoolId == 0) {
90 /* If pool storage specified, use it instead of job storage */
91 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
94 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
98 create_clones(jcr); /* run any clone jobs */
103 /* Take all base jobs from job resource and find the
106 static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids)
113 if (!jcr->job->base) {
114 return false; /* no base job, stop accurate */
117 memset(&jr, 0, sizeof(JOB_DBR));
118 jr.StartTime = jcr->jr.StartTime;
120 foreach_alist(job, jcr->job->base) {
121 bstrncpy(jr.Name, job->name(), sizeof(jr.Name));
122 db_get_base_jobid(jcr, jcr->db, &jr, &id);
126 pm_strcat(jobids->list, ",");
128 pm_strcat(jobids->list, edit_uint64(id, str_jobid));
133 return jobids->count > 0;
137 * Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
138 * row[0]=Path, row[1]=Filename, row[2]=FileIndex
139 * row[3]=JobId row[4]=LStat row[5]=DeltaSeq row[6]=MD5
141 static int accurate_list_handler(void *ctx, int num_fields, char **row)
143 JCR *jcr = (JCR *)ctx;
145 if (job_canceled(jcr)) {
149 if (row[2][0] == '0') { /* discard when file_index == 0 */
153 /* sending with checksum */
154 if (jcr->use_accurate_chksum
156 && row[6][0] /* skip checksum = '0' */
159 jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s",
160 row[0], row[1], 0, row[4], 0, row[6], 0, row[5]);
162 jcr->file_bsock->fsend("%s%s%c%s%c%c%s",
163 row[0], row[1], 0, row[4], 0, 0, row[5]);
168 /* In this procedure, we check if the current fileset is using checksum
169 * FileSet-> Include-> Options-> Accurate/Verify/BaseJob=checksum
170 * This procedure uses jcr->HasBase, so it must be call after the initialization
172 static bool is_checksum_needed_by_fileset(JCR *jcr)
178 bool have_basejob_option=false;
179 if (!jcr->job || !jcr->job->fileset) {
183 f = jcr->job->fileset;
185 for (int i=0; i < f->num_includes; i++) { /* Parse all Include {} */
186 inc = f->include_items[i];
188 for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
189 fopts = inc->opts_list[j];
191 for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
193 case 'V': /* verify */
194 in_block = (jcr->getJobType() == JT_VERIFY); /* not used now */
196 case 'J': /* Basejob keyword */
197 have_basejob_option = in_block = jcr->HasBase;
199 case 'C': /* Accurate keyword */
200 in_block = !jcr->is_JobLevel(L_FULL);
202 case ':': /* End of keyword */
208 Dmsg0(50, "Checksum will be sent to FD\n");
219 /* By default for BaseJobs, we send the checksum */
220 if (!have_basejob_option && jcr->HasBase) {
224 Dmsg0(50, "Checksum will be sent to FD\n");
229 * Send current file list to FD
230 * DIR -> FD : accurate files=xxxx
231 * DIR -> FD : /path/to/file\0Lstat\0MD5\0Delta
232 * DIR -> FD : /path/to/dir/\0Lstat\0MD5\0Delta
236 bool send_accurate_current_files(JCR *jcr)
243 if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
246 if (!jcr->accurate && !jcr->rerunning) {
250 if (jcr->is_JobLevel(L_FULL)) {
251 /* On Full mode, if no previous base job, no accurate things */
252 if (get_base_jobids(jcr, &jobids)) {
254 Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
255 } else if (!jcr->rerunning) {
259 /* For Incr/Diff level, we search for older jobs */
260 db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
262 /* We are in Incr/Diff, but no Full to build the accurate list... */
263 if (jobids.count == 0) {
264 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
265 return false; /* fail */
269 if (jcr->rerunning) {
270 edit_int64(jcr->JobId, ed1);
274 /* Don't send and store the checksum if fileset doesn't require it */
275 jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
277 if (jcr->JobId) { /* display the message only for real jobs */
278 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information to the FD.\n"));
281 /* to be able to allocate the right size for htable */
282 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
283 db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
284 Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
285 jcr->file_bsock->fsend("accurate files=%s\n", nb.list);
287 if (!db_open_batch_connexion(jcr, jcr->db)) {
288 Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
289 return false; /* Fail */
293 jcr->nb_base_files = str_to_int64(nb.list);
294 if (!db_create_base_file_list(jcr, jcr->db, jobids.list)) {
295 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
298 if (!db_get_base_file_list(jcr, jcr->db, jcr->use_accurate_chksum,
299 accurate_list_handler, (void *)jcr)) {
300 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
305 if (!db_get_file_list(jcr, jcr->db_batch,
306 jobids.list, jcr->use_accurate_chksum, false /* no delta */,
307 accurate_list_handler, (void *)jcr)) {
308 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
313 /* TODO: close the batch connection ? (can be used very soon) */
314 jcr->file_bsock->signal(BNET_EOD);
318 bool send_store_addr_to_fd(JCR *jcr, STORE *store,
319 char *store_address, uint32_t store_port)
321 int tls_need = BNET_TLS_NONE;
323 /* TLS Requirement */
324 if (store->tls_enable) {
325 if (store->tls_require) {
326 tls_need = BNET_TLS_REQUIRED;
328 tls_need = BNET_TLS_OK;
333 * Send Storage address to the FD
335 jcr->file_bsock->fsend(storaddr, store_address, store_port, tls_need);
336 if (!response(jcr, jcr->file_bsock, OKstore, "Storage", DISPLAY_ERROR)) {
342 bool send_client_addr_to_sd(JCR *jcr)
344 int tls_need = BNET_TLS_NONE;
345 BSOCK *sd = jcr->store_bsock;
347 /* TLS Requirement for the client */
348 if (jcr->client->tls_enable) {
349 if (jcr->client->tls_require) {
350 tls_need = BNET_TLS_REQUIRED;
352 tls_need = BNET_TLS_OK;
356 * Send Client address to the SD
358 sd->fsend(clientaddr, jcr->client->address, jcr->client->FDport, tls_need);
359 if (!response(jcr, sd, OKclient, "Client", DISPLAY_ERROR)) {
366 * Allow to specify the address used by the Client to
367 * connect to the storage daemon in the Client resource
368 * or in the Storage resource.
370 char *get_storage_address(CLIENT *client, STORE *store)
374 if (client && client->fd_storage_address) {
375 Dmsg0(10, "Using Client resource FD Storage Address to contact the Storage\n");
376 store_address = client->fd_storage_address;
378 } else if (store->fd_storage_address) {
379 Dmsg0(10, "Using Storage resource FD Storage Address to contact the Storage\n");
380 store_address = store->fd_storage_address;
383 Dmsg0(10, "Using default Storage address\n");
384 store_address = store->address;
386 return store_address;
389 bool run_storage_and_start_message_thread(JCR *jcr, BSOCK *sd)
392 * Start the job prior to starting the message thread below
393 * to avoid two threads from using the BSOCK structure at
396 if (!sd->fsend("run")) {
401 * Now start a Storage daemon message thread. Note,
402 * this thread is used to provide the catalog services
403 * for the backup job, including inserting the attributes
404 * into the catalog. See catalog_update() in catreq.c
406 if (!start_storage_daemon_message_thread(jcr)) {
409 Dmsg0(150, "Storage daemon connection OK\n");
414 * Do a backup of the specified FileSet
416 * Returns: false on failure
419 bool do_backup(JCR *jcr)
430 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
431 return do_vbackup(jcr);
434 /* Print Job Start message */
435 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
436 edit_uint64(jcr->JobId, ed1), jcr->Job);
438 jcr->setJobStatus(JS_Running);
439 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
440 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
441 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
447 * Open a message channel connection with the Storage
448 * daemon. This is to let him know that our client
449 * will be contacting him for a backup session.
452 Dmsg0(110, "Open connection with storage daemon\n");
453 jcr->setJobStatus(JS_WaitSD);
455 * Start conversation with Storage daemon
457 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
461 * Now start a job with the Storage daemon
463 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
466 sd = jcr->store_bsock;
467 jcr->sd_calls_client = jcr->client->sd_calls_client;
469 * Note startup sequence of SD/FD is different depending on
470 * whether the SD listens (normal) or the SD calls the FD.
472 if (!jcr->sd_calls_client) {
473 if (!run_storage_and_start_message_thread(jcr, sd)) {
477 jcr->setJobStatus(JS_WaitFD);
478 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
482 jcr->setJobStatus(JS_Running);
483 fd = jcr->file_bsock;
485 if (!send_level_command(jcr)) {
489 if (!send_include_list(jcr)) {
493 if (!send_exclude_list(jcr)) {
497 /* TODO: See priority with bandwidth parameter */
498 if (jcr->job->max_bandwidth > 0) {
499 jcr->max_bandwidth = jcr->job->max_bandwidth;
500 } else if (jcr->client->max_bandwidth > 0) {
501 jcr->max_bandwidth = jcr->client->max_bandwidth;
504 if (jcr->max_bandwidth > 0) {
505 send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
510 if (jcr->sd_calls_client) {
511 if (jcr->FDVersion < 5) {
512 Jmsg(jcr, M_FATAL, 0, _("The File daemon does not support SDCallsClient.\n"));
515 if (!send_client_addr_to_sd(jcr)) {
519 if (!run_storage_and_start_message_thread(jcr, sd)) {
523 store_address = jcr->wstore->address; /* dummy */
524 store_port = 0; /* flag that SD calls FD */
527 * send Storage daemon address to the File daemon
529 if (store->SDDport == 0) {
530 store->SDDport = store->SDport;
533 store_address = get_storage_address(jcr->client, store);
534 store_port = store->SDDport;
537 if (!send_store_addr_to_fd(jcr, store, store_address, store_port)) {
541 /* Declare the job started to start the MaxRunTime check */
542 jcr->setJobStarted();
544 /* Send and run the RunBefore */
545 if (!send_runscripts_commands(jcr)) {
550 * We re-update the job start record so that the start
551 * time is set after the run before job. This avoids
552 * that any files created by the run before job will
553 * be saved twice. They will be backed up in the current
554 * job, but not in the next one unless they are changed.
555 * Without this, they will be backed up in this job and
556 * in the next job run because in that case, their date
557 * is after the start of this run.
559 jcr->start_time = time(NULL);
560 jcr->jr.StartTime = jcr->start_time;
561 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
562 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
566 * If backup is in accurate mode, we send the list of
569 if (!send_accurate_current_files(jcr)) {
570 goto bail_out; /* error */
573 /* Send backup command */
574 fd->fsend(backupcmd, jcr->JobFiles);
575 Dmsg1(100, ">filed: %s", fd->msg);
576 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
580 /* Pickup Job termination data */
581 stat = wait_for_job_termination(jcr);
582 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
585 db_commit_base_file_attributes_record(jcr, jcr->db);
586 /* Any error already printed */
589 if (!jcr->is_canceled() && stat == JS_Terminated) {
590 backup_cleanup(jcr, stat);
595 /* Come here only after starting SD thread */
597 jcr->setJobStatus(JS_ErrorTerminated);
598 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
600 wait_for_job_termination(jcr, FDConnectTimeout);
601 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
607 * Here we wait for the File daemon to signal termination,
608 * then we wait for the Storage daemon. When both
609 * are done, we return the job status.
610 * Also used by restore.c
612 int wait_for_job_termination(JCR *jcr, int timeout)
615 BSOCK *fd = jcr->file_bsock;
617 uint32_t JobFiles, JobErrors;
618 uint32_t JobWarnings = 0;
619 uint64_t ReadBytes = 0;
620 uint64_t JobBytes = 0;
627 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
629 /* Wait for Client to terminate */
630 while ((n = bget_dirmsg(fd)) >= 0) {
632 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
633 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
634 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
635 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
637 jcr->setJobStatus(jcr->FDJobStatus);
638 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
640 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
643 if (job_canceled(jcr)) {
648 stop_bsock_timer(tid);
651 if (fd->is_error() && jcr->getJobStatus() != JS_Canceled) {
653 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
654 job_type_to_str(jcr->getJobType()), fd->bstrerror());
655 while (i++ < 20 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
659 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
663 * Force cancel in SD if failing,
664 * so that we let the SD despool.
666 Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
667 jcr->JobStatus, jcr->SDJobStatus);
668 if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
669 Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
670 jcr->JobStatus, jcr->SDJobStatus);
671 cancel_storage_daemon_job(jcr);
674 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
675 wait_for_storage_daemon_termination(jcr);
677 /* Return values from FD */
679 jcr->JobFiles = JobFiles;
680 jcr->JobErrors += JobErrors; /* Keep total errors */
681 jcr->ReadBytes = ReadBytes;
682 jcr->JobBytes = JobBytes;
683 jcr->JobWarnings = JobWarnings;
685 jcr->Encrypt = Encrypt;
686 } else if (jcr->getJobStatus() != JS_Canceled) {
687 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
690 /* Return the first error status we find Dir, FD, or SD */
691 if (!fd_ok || fd->is_error()) { /* if fd not set, that use !fd_ok */
692 if (jcr->getJobStatus() == JS_Canceled) {
693 jcr->FDJobStatus = JS_Canceled;
695 jcr->FDJobStatus = JS_ErrorTerminated;
698 if (jcr->JobStatus != JS_Terminated) {
699 return jcr->JobStatus;
701 if (jcr->FDJobStatus != JS_Terminated) {
702 return jcr->FDJobStatus;
704 return jcr->SDJobStatus;
708 * Release resources allocated during backup.
710 void backup_cleanup(JCR *jcr, int TermCode)
712 char sdt[50], edt[50], schedt[50];
713 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30];
714 char ec6[30], ec7[30], ec8[30], elapsed[50];
715 char data_compress[200];
716 char term_code[100], fd_term_msg[100], sd_term_msg[100];
717 const char *term_msg;
718 int msg_type = M_INFO;
721 double kbps, compression, ratio;
726 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
727 vbackup_cleanup(jcr, TermCode);
731 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
732 memset(&cr, 0, sizeof(cr));
735 /* The current implementation of the JS_Warning status is not
736 * completed. SQL part looks to be ok, but the code is using
737 * JS_Terminated almost everywhere instead of (JS_Terminated || JS_Warning)
738 * as we do with is_canceled()
740 if (jcr->getJobStatus() == JS_Terminated &&
741 (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
742 TermCode = JS_Warnings;
746 update_job_end(jcr, TermCode);
748 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
749 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
750 db_strerror(jcr->db));
751 jcr->setJobStatus(JS_ErrorTerminated);
754 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
755 if (!db_get_client_record(jcr, jcr->db, &cr)) {
756 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
757 db_strerror(jcr->db));
760 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
761 if (!db_get_media_record(jcr, jcr->db, &mr)) {
762 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
763 mr.VolumeName, db_strerror(jcr->db));
764 jcr->setJobStatus(JS_ErrorTerminated);
767 update_bootstrap_file(jcr);
769 switch (jcr->JobStatus) {
771 if (jcr->JobErrors || jcr->SDErrors) {
772 term_msg = _("Backup OK -- with warnings");
774 term_msg = _("Backup OK");
778 term_msg = _("Backup OK -- with warnings");
781 case JS_ErrorTerminated:
782 term_msg = _("*** Backup Error ***");
783 msg_type = M_ERROR; /* Generate error message */
784 if (jcr->store_bsock) {
785 jcr->store_bsock->signal(BNET_TERMINATE);
786 if (jcr->SD_msg_chan) {
787 pthread_cancel(jcr->SD_msg_chan);
792 term_msg = _("Backup Canceled");
793 if (jcr->store_bsock) {
794 jcr->store_bsock->signal(BNET_TERMINATE);
795 if (jcr->SD_msg_chan) {
796 pthread_cancel(jcr->SD_msg_chan);
801 term_msg = term_code;
802 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
805 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
806 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
807 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
808 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
812 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
814 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
816 * Note, if the job has erred, most likely it did not write any
817 * tape, so suppress this "error" message since in that case
818 * it is normal. Or look at it the other way, only for a
819 * normal exit should we complain about this error.
821 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
822 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
824 jcr->VolumeName[0] = 0; /* none */
827 if (jcr->ReadBytes == 0) {
828 bstrncpy(data_compress, "None", sizeof(data_compress));
830 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
831 if (compression < 0.5) {
832 bstrncpy(data_compress, "None", sizeof(data_compress));
834 if (jcr->JobBytes > 0) {
835 ratio = (double)jcr->ReadBytes / (double)jcr->JobBytes;
839 bsnprintf(data_compress, sizeof(data_compress), "%.1f%% %.1f:1",
843 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
844 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
847 Mmsg(base_info, _(" Base files/Used files: %lld/%lld (%.2f%%)\n"),
849 jcr->nb_base_files_used,
850 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
852 /* Edit string for last volume size */
853 Mmsg(vol_info, _("%s (%sB)"),
854 edit_uint64_with_commas(mr.VolBytes, ec7),
855 edit_uint64_with_suffix(mr.VolBytes, ec8));
858 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
859 " Build OS: %s %s %s\n"
862 " Backup Level: %s%s\n"
863 " Client: \"%s\" %s\n"
864 " FileSet: \"%s\" %s\n"
865 " Pool: \"%s\" (From %s)\n"
866 " Catalog: \"%s\" (From %s)\n"
867 " Storage: \"%s\" (From %s)\n"
868 " Scheduled time: %s\n"
871 " Elapsed time: %s\n"
873 " FD Files Written: %s\n"
874 " SD Files Written: %s\n"
875 " FD Bytes Written: %s (%sB)\n"
876 " SD Bytes Written: %s (%sB)\n"
878 " Software Compression: %s\n"
879 "%s" /* Basefile info */
883 " Volume name(s): %s\n"
884 " Volume Session Id: %d\n"
885 " Volume Session Time: %d\n"
886 " Last Volume Bytes: %s\n"
887 " Non-fatal FD errors: %d\n"
889 " FD termination status: %s\n"
890 " SD termination status: %s\n"
891 " Termination: %s\n\n"),
892 BACULA, my_name, VERSION, LSMDATE,
893 HOST_OS, DISTNAME, DISTVER,
896 level_to_str(jcr->getJobLevel()), jcr->since,
897 jcr->client->name(), cr.Uname,
898 jcr->fileset->name(), jcr->FSCreateTime,
899 jcr->pool->name(), jcr->pool_source,
900 jcr->catalog->name(), jcr->catalog_source,
901 jcr->wstore->name(), jcr->wstore_source,
905 edit_utime(RunTime, elapsed, sizeof(elapsed)),
907 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
908 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
909 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
910 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
911 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
912 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
916 jcr->VSS?_("yes"):_("no"),
917 jcr->Encrypt?_("yes"):_("no"),
918 jcr->accurate?_("yes"):_("no"),
929 Dmsg0(100, "Leave backup_cleanup()\n");
932 void update_bootstrap_file(JCR *jcr)
934 /* Now update the bootstrap file if any */
935 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
936 jcr->job->WriteBootstrap) {
940 POOLMEM *fname = get_pool_memory(PM_FNAME);
941 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
943 VOL_PARAMS *VolParams = NULL;
945 char edt[50], ed1[50], ed2[50];
949 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
950 fd = bpipe ? bpipe->wfd : NULL;
952 /* ***FIXME*** handle BASE */
953 fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
956 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
959 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
960 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
961 if (jcr->SDJobFiles != 0) {
962 jcr->setJobStatus(JS_ErrorTerminated);
966 /* Start output with when and who wrote it */
967 bstrftimes(edt, sizeof(edt), time(NULL));
968 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
969 level_to_str(jcr->getJobLevel()), jcr->since);
970 for (int i=0; i < VolCount; i++) {
971 /* Write the record */
972 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
973 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
974 if (VolParams[i].Slot > 0) {
975 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
977 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
978 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
979 fprintf(fd, "VolAddr=%s-%s\n",
980 edit_uint64(VolParams[i].StartAddr, ed1),
981 edit_uint64(VolParams[i].EndAddr, ed2));
982 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
983 VolParams[i].LastIndex);
995 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
996 "%s: ERR=%s\n"), fname, be.bstrerror());
997 jcr->setJobStatus(JS_ErrorTerminated);
999 free_pool_memory(fname);