2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many 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 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Director -- backup.c -- responsible for doing backup jobs
22 * Kern Sibbald, March MM
24 * Basic tasks done here:
25 * Open DB and create records for this job.
26 * Open Message Channel with Storage daemon to tell him a job will be starting.
27 * Open connection with File daemon and pass him commands
29 * When the File daemon finishes the job, update the DB.
36 /* Commands sent to File daemon */
37 static char backupcmd[] = "backup FileIndex=%ld\n";
38 static char storaddr[] = "storage address=%s port=%d ssl=%d\n";
40 /* Responses received from File daemon */
41 static char OKbackup[] = "2000 OK backup\n";
42 static char OKstore[] = "2000 OK storage\n";
43 /* After 17 Aug 2013 */
44 static char newEndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
45 "ReadBytes=%llu JobBytes=%llu Errors=%u "
47 "CommBytes=%lld CompressCommBytes=%lld\n";
49 static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
50 "ReadBytes=%llu JobBytes=%llu Errors=%u "
51 "VSS=%d Encrypt=%d\n";
52 /* Pre 1.39.29 (04Dec06) EndJob */
53 static char OldEndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
54 "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
56 /* Commands sent to Storage daemon */
57 static char clientaddr[] = "client address=%s port=%d ssl=%d\n";
59 /* Commands received from Storage daemon */
60 static char OKclient[] = "3000 OK client command\n";
63 * Called here before the job is run to do the job
66 bool do_backup_init(JCR *jcr)
69 jcr->RescheduleIncompleteJobs = jcr->job->RescheduleIncompleteJobs;
71 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
72 return do_vbackup_init(jcr);
74 free_rstorage(jcr); /* we don't read so release */
76 if (!get_or_create_fileset_record(jcr)) {
81 * Get definitive Job level and since time
83 get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
85 apply_pool_overrides(jcr);
87 if (!allow_duplicate_job(jcr)) {
91 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
92 if (jcr->jr.PoolId == 0) {
96 /* If pool storage specified, use it instead of job storage */
97 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
100 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
104 create_clones(jcr); /* run any clone jobs */
109 /* Take all base jobs from job resource and find the
112 static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids)
119 if (!jcr->job->base) {
120 return false; /* no base job, stop accurate */
123 memset(&jr, 0, sizeof(JOB_DBR));
124 jr.StartTime = jcr->jr.StartTime;
126 foreach_alist(job, jcr->job->base) {
127 bstrncpy(jr.Name, job->name(), sizeof(jr.Name));
128 db_get_base_jobid(jcr, jcr->db, &jr, &id);
132 pm_strcat(jobids->list, ",");
134 pm_strcat(jobids->list, edit_uint64(id, str_jobid));
139 return jobids->count > 0;
143 * Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
144 * row[0]=Path, row[1]=Filename, row[2]=FileIndex
145 * row[3]=JobId row[4]=LStat row[5]=DeltaSeq row[6]=MD5
147 static int accurate_list_handler(void *ctx, int num_fields, char **row)
149 JCR *jcr = (JCR *)ctx;
151 if (job_canceled(jcr)) {
155 if (row[2][0] == '0') { /* discard when file_index == 0 */
159 /* sending with checksum */
160 if (jcr->use_accurate_chksum
162 && row[6][0] /* skip checksum = '0' */
165 jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s",
166 row[0], row[1], 0, row[4], 0, row[6], 0, row[5]);
168 jcr->file_bsock->fsend("%s%s%c%s%c%c%s",
169 row[0], row[1], 0, row[4], 0, 0, row[5]);
174 /* In this procedure, we check if the current fileset is using checksum
175 * FileSet-> Include-> Options-> Accurate/Verify/BaseJob=checksum
176 * This procedure uses jcr->HasBase, so it must be call after the initialization
178 static bool is_checksum_needed_by_fileset(JCR *jcr)
184 bool have_basejob_option=false;
185 if (!jcr->job || !jcr->job->fileset) {
189 f = jcr->job->fileset;
191 for (int i=0; i < f->num_includes; i++) { /* Parse all Include {} */
192 inc = f->include_items[i];
194 for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
195 fopts = inc->opts_list[j];
197 for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
199 case 'V': /* verify */
200 in_block = (jcr->getJobType() == JT_VERIFY); /* not used now */
202 case 'J': /* Basejob keyword */
203 have_basejob_option = in_block = jcr->HasBase;
205 case 'C': /* Accurate keyword */
206 in_block = !jcr->is_JobLevel(L_FULL);
208 case ':': /* End of keyword */
214 Dmsg0(50, "Checksum will be sent to FD\n");
225 /* By default for BaseJobs, we send the checksum */
226 if (!have_basejob_option && jcr->HasBase) {
230 Dmsg0(50, "Checksum will be sent to FD\n");
235 * Send current file list to FD
236 * DIR -> FD : accurate files=xxxx
237 * DIR -> FD : /path/to/file\0Lstat\0MD5\0Delta
238 * DIR -> FD : /path/to/dir/\0Lstat\0MD5\0Delta
242 bool send_accurate_current_files(JCR *jcr)
249 /* In base level, no previous job is used and no restart incomplete jobs */
250 if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
253 if (!jcr->accurate && !jcr->rerunning) {
257 if (jcr->is_JobLevel(L_FULL)) {
258 /* On Full mode, if no previous base job, no accurate things */
259 if (get_base_jobids(jcr, &jobids)) {
261 Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
262 } else if (!jcr->rerunning) {
266 } else if (jcr->is_JobLevel(L_VERIFY_DATA)) {
268 jobids.add(edit_uint64(jcr->previous_jr.JobId, ed1));
271 /* For Incr/Diff level, we search for older jobs */
272 db_get_accurate_jobids(jcr, jcr->db, &jcr->jr, &jobids);
274 /* We are in Incr/Diff, but no Full to build the accurate list... */
275 if (jobids.count == 0) {
276 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
277 return false; /* fail */
281 /* For incomplete Jobs, we add our own id */
282 if (jcr->rerunning) {
283 edit_int64(jcr->JobId, ed1);
287 /* Don't send and store the checksum if fileset doesn't require it */
288 jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
290 if (jcr->JobId) { /* display the message only for real jobs */
291 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information to the FD.\n"));
294 /* to be able to allocate the right size for htable */
295 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
296 db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
297 Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
298 jcr->file_bsock->fsend("accurate files=%s\n", nb.list);
300 if (!db_open_batch_connexion(jcr, jcr->db)) {
301 Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
302 return false; /* Fail */
306 jcr->nb_base_files = str_to_int64(nb.list);
307 if (!db_create_base_file_list(jcr, jcr->db, jobids.list)) {
308 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
311 if (!db_get_base_file_list(jcr, jcr->db, jcr->use_accurate_chksum,
312 accurate_list_handler, (void *)jcr)) {
313 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
318 if (!db_get_file_list(jcr, jcr->db_batch,
319 jobids.list, jcr->use_accurate_chksum, false /* no delta */,
320 accurate_list_handler, (void *)jcr)) {
321 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db_batch));
326 /* TODO: close the batch connection ? (can be used very soon) */
327 jcr->file_bsock->signal(BNET_EOD);
331 bool send_store_addr_to_fd(JCR *jcr, STORE *store,
332 char *store_address, uint32_t store_port)
334 int tls_need = BNET_TLS_NONE;
336 /* TLS Requirement */
337 if (store->tls_enable) {
338 if (store->tls_require) {
339 tls_need = BNET_TLS_REQUIRED;
341 tls_need = BNET_TLS_OK;
346 * Send Storage address to the FD
348 jcr->file_bsock->fsend(storaddr, store_address, store_port, tls_need);
349 if (!response(jcr, jcr->file_bsock, OKstore, "Storage", DISPLAY_ERROR)) {
355 bool send_client_addr_to_sd(JCR *jcr)
357 int tls_need = BNET_TLS_NONE;
358 BSOCK *sd = jcr->store_bsock;
360 /* TLS Requirement for the client */
361 if (jcr->client->tls_enable) {
362 if (jcr->client->tls_require) {
363 tls_need = BNET_TLS_REQUIRED;
365 tls_need = BNET_TLS_OK;
369 * Send Client address to the SD
371 sd->fsend(clientaddr, jcr->client->address(), jcr->client->FDport, tls_need);
372 if (!response(jcr, sd, OKclient, "Client", DISPLAY_ERROR)) {
379 * Allow to specify the address used by the Client to
380 * connect to the storage daemon in the Client resource
381 * or in the Storage resource.
383 char *get_storage_address(CLIENT *client, STORE *store)
387 if (client && client->fd_storage_address) {
388 Dmsg0(10, "Using Client resource FD Storage Address to contact the Storage\n");
389 store_address = client->fd_storage_address;
391 } else if (store->fd_storage_address) {
392 Dmsg0(10, "Using Storage resource FD Storage Address to contact the Storage\n");
393 store_address = store->fd_storage_address;
396 Dmsg0(10, "Using default Storage address\n");
397 store_address = store->address;
399 return store_address;
402 bool run_storage_and_start_message_thread(JCR *jcr, BSOCK *sd)
405 * Start the job prior to starting the message thread below
406 * to avoid two threads from using the BSOCK structure at
409 if (!sd->fsend("run")) {
414 * Now start a Storage daemon message thread. Note,
415 * this thread is used to provide the catalog services
416 * for the backup job, including inserting the attributes
417 * into the catalog. See catalog_update() in catreq.c
419 if (!start_storage_daemon_message_thread(jcr)) {
422 Dmsg0(150, "Storage daemon connection OK\n");
427 * Do a backup of the specified FileSet
429 * Returns: false on failure
432 bool do_backup(JCR *jcr)
443 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
444 return do_vbackup(jcr);
447 /* Print Job Start message */
448 if (jcr->rerunning) {
449 Jmsg(jcr, M_INFO, 0, _("Restart Incomplete Backup JobId %s, Job=%s\n"),
450 edit_uint64(jcr->JobId, ed1), jcr->Job);
452 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
453 edit_uint64(jcr->JobId, ed1), jcr->Job);
456 jcr->setJobStatus(JS_Running);
457 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
458 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
459 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
463 /* For incomplete Jobs, we add our own id */
464 if (jcr->rerunning) {
465 edit_int64(jcr->JobId, ed1);
466 Mmsg(buf, "SELECT max(FileIndex) FROM File WHERE JobId=%s", ed1);
467 if (db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
468 Jmsg(jcr, M_INFO, 0, _("Found %ld files from prior incomplete Job.\n"),
471 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
474 jcr->JobFiles = job.value;
475 Dmsg1(100, "==== FI=%ld\n", jcr->JobFiles);
476 Mmsg(buf, "SELECT VolSessionId FROM Job WHERE JobId=%s", ed1);
477 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
478 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
481 jcr->VolSessionId = job.value;
482 Mmsg(buf, "SELECT VolSessionTime FROM Job WHERE JobId=%s", ed1);
483 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
484 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
487 jcr->VolSessionTime = job.value;
488 Dmsg4(100, "JobId=%s JobFiles=%ld VolSessionId=%ld VolSessionTime=%ld\n", ed1,
489 jcr->JobFiles, jcr->VolSessionId, jcr->VolSessionTime);
493 * Open a message channel connection with the Storage
494 * daemon. This is to let him know that our client
495 * will be contacting him for a backup session.
498 Dmsg0(110, "Open connection with storage daemon\n");
499 jcr->setJobStatus(JS_WaitSD);
501 * Start conversation with Storage daemon
503 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
507 * Now start a job with the Storage daemon
509 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
512 sd = jcr->store_bsock;
514 jcr->sd_calls_client = jcr->client->sd_calls_client;
517 * Note startup sequence of SD/FD is different depending on
518 * whether the SD listens (normal) or the SD calls the FD.
520 if (!jcr->sd_calls_client) {
521 if (!run_storage_and_start_message_thread(jcr, sd)) {
525 jcr->setJobStatus(JS_WaitFD);
526 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
530 jcr->setJobStatus(JS_Running);
531 fd = jcr->file_bsock;
533 if (!send_level_command(jcr)) {
537 if (!send_include_list(jcr)) {
541 if (!send_exclude_list(jcr)) {
545 /* TODO: See priority with bandwidth parameter */
546 if (jcr->job->max_bandwidth > 0) {
547 jcr->max_bandwidth = jcr->job->max_bandwidth;
548 } else if (jcr->client->max_bandwidth > 0) {
549 jcr->max_bandwidth = jcr->client->max_bandwidth;
552 if (jcr->max_bandwidth > 0) {
553 send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
556 send_snapshot_retention(jcr, jcr->snapshot_retention);
560 if (jcr->sd_calls_client) {
561 if (jcr->FDVersion < 10) {
562 Jmsg(jcr, M_FATAL, 0, _("The File daemon does not support SDCallsClient.\n"));
565 if (!send_client_addr_to_sd(jcr)) {
569 if (!run_storage_and_start_message_thread(jcr, sd)) {
573 store_address = jcr->wstore->address; /* dummy */
574 store_port = 0; /* flag that SD calls FD */
577 * send Storage daemon address to the File daemon
579 if (store->SDDport == 0) {
580 store->SDDport = store->SDport;
583 store_address = get_storage_address(jcr->client, store);
584 store_port = store->SDDport;
587 if (!send_store_addr_to_fd(jcr, store, store_address, store_port)) {
591 /* Declare the job started to start the MaxRunTime check */
592 jcr->setJobStarted();
594 /* Send and run the RunBefore */
595 if (!send_runscripts_commands(jcr)) {
600 * We re-update the job start record so that the start
601 * time is set after the run before job. This avoids
602 * that any files created by the run before job will
603 * be saved twice. They will be backed up in the current
604 * job, but not in the next one unless they are changed.
605 * Without this, they will be backed up in this job and
606 * in the next job run because in that case, their date
607 * is after the start of this run.
609 jcr->start_time = time(NULL);
610 jcr->jr.StartTime = jcr->start_time;
611 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
612 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
616 * If backup is in accurate mode, we send the list of
619 if (!send_accurate_current_files(jcr)) {
620 goto bail_out; /* error */
623 /* Send backup command */
624 fd->fsend(backupcmd, jcr->JobFiles);
625 Dmsg1(100, ">filed: %s", fd->msg);
626 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
630 /* Pickup Job termination data */
631 stat = wait_for_job_termination(jcr);
632 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
635 db_commit_base_file_attributes_record(jcr, jcr->db);
636 /* Any error already printed */
639 if (!jcr->is_canceled() && stat == JS_Terminated) {
640 backup_cleanup(jcr, stat);
645 /* Come here only after starting SD thread */
647 jcr->setJobStatus(JS_ErrorTerminated);
648 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
650 wait_for_job_termination(jcr, FDConnectTimeout);
651 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
657 * Here we wait for the File daemon to signal termination,
658 * then we wait for the Storage daemon. When both
659 * are done, we return the job status.
660 * Also used by restore.c
662 int wait_for_job_termination(JCR *jcr, int timeout)
665 BSOCK *fd = jcr->file_bsock;
667 uint32_t JobFiles, JobErrors;
668 uint32_t JobWarnings = 0;
669 uint64_t ReadBytes = 0;
670 uint64_t JobBytes = 0;
671 uint64_t CommBytes = 0;
672 uint64_t CommCompressedBytes = 0;
673 int VSS = 0; /* or Snapshot on Unix */
679 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
681 /* Wait for Client to terminate */
682 while ((n = bget_dirmsg(fd)) >= 0) {
684 (sscanf(fd->msg, newEndJob, &jcr->FDJobStatus, &JobFiles,
685 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt,
686 &CommBytes, &CommCompressedBytes) == 9 ||
687 sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
688 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
689 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
690 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
692 jcr->setJobStatus(jcr->FDJobStatus);
693 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
695 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
698 if (job_canceled(jcr)) {
703 stop_bsock_timer(tid);
706 if (fd->is_error() && jcr->getJobStatus() != JS_Canceled) {
708 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
709 job_type_to_str(jcr->getJobType()), fd->bstrerror());
710 while (i++ < 20 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
714 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
718 * Force cancel in SD if failing, but not for Incomplete jobs
719 * so that we let the SD despool.
721 Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
722 jcr->JobStatus, jcr->SDJobStatus);
723 if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
724 Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
725 jcr->JobStatus, jcr->SDJobStatus);
726 cancel_storage_daemon_job(jcr);
729 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
730 wait_for_storage_daemon_termination(jcr);
732 /* Return values from FD */
734 jcr->JobFiles = JobFiles;
735 jcr->JobErrors += JobErrors; /* Keep total errors */
736 jcr->ReadBytes = ReadBytes;
737 jcr->JobBytes = JobBytes;
738 jcr->JobWarnings = JobWarnings;
739 jcr->CommBytes = CommBytes;
740 jcr->CommCompressedBytes = CommCompressedBytes;
742 jcr->Encrypt = Encrypt;
743 } else if (jcr->getJobStatus() != JS_Canceled) {
744 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
747 /* Return the first error status we find Dir, FD, or SD */
748 if (!fd_ok || fd->is_error()) { /* if fd not set, that use !fd_ok */
749 if (jcr->getJobStatus() == JS_Canceled) {
750 jcr->FDJobStatus = JS_Canceled;
752 jcr->FDJobStatus = JS_ErrorTerminated;
755 if (jcr->JobStatus != JS_Terminated) {
756 return jcr->JobStatus;
758 if (jcr->FDJobStatus != JS_Terminated) {
759 return jcr->FDJobStatus;
761 return jcr->SDJobStatus;
765 * Release resources allocated during backup.
767 void backup_cleanup(JCR *jcr, int TermCode)
769 char sdt[50], edt[50], schedt[50];
770 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30];
771 char ec6[30], ec7[30], ec8[30], ec9[30], ec10[30], elapsed[50];
772 char data_compress[200], comm_compress[200];
773 char fd_term_msg[100], sd_term_msg[100];
775 int msg_type = M_INFO;
778 double kbps, compression, ratio;
783 remove_dummy_jobmedia_records(jcr);
785 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
786 vbackup_cleanup(jcr, TermCode);
790 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
791 memset(&cr, 0, sizeof(cr));
794 /* The current implementation of the JS_Warning status is not
795 * completed. SQL part looks to be ok, but the code is using
796 * JS_Terminated almost everywhere instead of (JS_Terminated || JS_Warning)
797 * as we do with is_canceled()
799 if (jcr->getJobStatus() == JS_Terminated &&
800 (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
801 TermCode = JS_Warnings;
805 update_job_end(jcr, TermCode);
807 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
808 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
809 db_strerror(jcr->db));
810 jcr->setJobStatus(JS_ErrorTerminated);
813 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
814 if (!db_get_client_record(jcr, jcr->db, &cr)) {
815 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
816 db_strerror(jcr->db));
819 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
820 if (!db_get_media_record(jcr, jcr->db, &mr)) {
821 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
822 mr.VolumeName, db_strerror(jcr->db));
823 jcr->setJobStatus(JS_ErrorTerminated);
826 update_bootstrap_file(jcr);
828 switch (jcr->JobStatus) {
830 if (jcr->JobErrors || jcr->SDErrors) {
831 Mmsg(term_msg, _("Backup OK -- %s"), jcr->StatusErrMsg[0] ? jcr->StatusErrMsg : _("with warnings"));
834 Mmsg(term_msg, _("Backup OK"));
838 Mmsg(term_msg, _("Backup failed -- incomplete"));
841 Mmsg(term_msg, _("Backup OK -- %s"), jcr->StatusErrMsg[0] ? jcr->StatusErrMsg : _("with warnings"));
844 case JS_ErrorTerminated:
845 Mmsg(term_msg, _("*** Backup Error ***"));
846 msg_type = M_ERROR; /* Generate error message */
847 if (jcr->store_bsock) {
848 jcr->store_bsock->signal(BNET_TERMINATE);
849 if (jcr->SD_msg_chan_started) {
850 pthread_cancel(jcr->SD_msg_chan);
855 Mmsg(term_msg, _("Backup Canceled"));
856 if (jcr->store_bsock) {
857 jcr->store_bsock->signal(BNET_TERMINATE);
858 if (jcr->SD_msg_chan_started) {
859 pthread_cancel(jcr->SD_msg_chan);
864 Mmsg(term_msg, _("Inappropriate term code: %c\n"), jcr->JobStatus);
867 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
868 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
869 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
870 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
874 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
875 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
877 * Note, if the job has erred, most likely it did not write any
878 * tape, so suppress this "error" message since in that case
879 * it is normal. Or look at it the other way, only for a
880 * normal exit should we complain about this error.
882 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
883 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
885 jcr->VolumeName[0] = 0; /* none */
888 if (jcr->ReadBytes == 0) {
889 bstrncpy(data_compress, "None", sizeof(data_compress));
891 compression = (double)100 - 100.0 * ((double)jcr->SDJobBytes / (double)jcr->ReadBytes);
892 if (compression < 0.5) {
893 bstrncpy(data_compress, "None", sizeof(data_compress));
895 if (jcr->SDJobBytes > 0) {
896 ratio = (double)jcr->ReadBytes / (double)jcr->SDJobBytes;
900 bsnprintf(data_compress, sizeof(data_compress), "%.1f%% %.1f:1",
904 if (jcr->CommBytes == 0 || jcr->CommCompressedBytes == 0) {
905 bstrncpy(comm_compress, "None", sizeof(comm_compress));
907 compression = (double)100 - 100.0 * ((double)jcr->CommCompressedBytes / (double)jcr->CommBytes);
908 if (compression < 0.5) {
909 bstrncpy(comm_compress, "None", sizeof(comm_compress));
911 ratio = (double)jcr->CommBytes / (double)jcr->CommCompressedBytes;
912 bsnprintf(comm_compress, sizeof(comm_compress), "%.1f%% %.1f:1",
915 Dmsg2(200, "=== CommCompressed=%lld CommBytes=%lld\n",
916 jcr->CommCompressedBytes, jcr->CommBytes);
918 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
919 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
922 Mmsg(base_info, _(" Base files/Used files: %lld/%lld (%.2f%%)\n"),
924 jcr->nb_base_files_used,
925 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
927 /* Edit string for last volume size */
928 if (mr.VolABytes != 0) {
929 Mmsg(vol_info, _("meta: %s (%sB) aligned: %s (%sB)"),
930 edit_uint64_with_commas(mr.VolBytes, ec7),
931 edit_uint64_with_suffix(mr.VolBytes, ec8),
932 edit_uint64_with_commas(mr.VolABytes, ec9),
933 edit_uint64_with_suffix(mr.VolABytes, ec10));
935 Mmsg(vol_info, _("%s (%sB)"),
936 edit_uint64_with_commas(mr.VolBytes, ec7),
937 edit_uint64_with_suffix(mr.VolBytes, ec8));
940 // bmicrosleep(15, 0); /* for debugging SIGHUP */
942 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
943 " Build OS: %s %s %s\n"
946 " Backup Level: %s%s\n"
947 " Client: \"%s\" %s\n"
948 " FileSet: \"%s\" %s\n"
949 " Pool: \"%s\" (From %s)\n"
950 " Catalog: \"%s\" (From %s)\n"
951 " Storage: \"%s\" (From %s)\n"
952 " Scheduled time: %s\n"
955 " Elapsed time: %s\n"
957 " FD Files Written: %s\n"
958 " SD Files Written: %s\n"
959 " FD Bytes Written: %s (%sB)\n"
960 " SD Bytes Written: %s (%sB)\n"
962 " Software Compression: %s\n"
963 " Comm Line Compression: %s\n"
964 "%s" /* Basefile info */
965 " Snapshot/VSS: %s\n"
968 " Volume name(s): %s\n"
969 " Volume Session Id: %d\n"
970 " Volume Session Time: %d\n"
971 " Last Volume Bytes: %s\n"
972 " Non-fatal FD errors: %d\n"
974 " FD termination status: %s\n"
975 " SD termination status: %s\n"
976 " Termination: %s\n\n"),
977 BACULA, my_name, VERSION, LSMDATE,
978 HOST_OS, DISTNAME, DISTVER,
981 level_to_str(jcr->getJobLevel()), jcr->since,
982 jcr->client->name(), cr.Uname,
983 jcr->fileset->name(), jcr->FSCreateTime,
984 jcr->pool->name(), jcr->pool_source,
985 jcr->catalog->name(), jcr->catalog_source,
986 jcr->wstore->name(), jcr->wstore_source,
990 edit_utime(RunTime, elapsed, sizeof(elapsed)),
992 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
993 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
994 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
995 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
996 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
997 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
1002 jcr->Snapshot?_("yes"):_("no"),
1003 jcr->Encrypt?_("yes"):_("no"),
1004 jcr->accurate?_("yes"):_("no"),
1007 jcr->VolSessionTime,
1015 Dmsg0(100, "Leave backup_cleanup()\n");
1018 void update_bootstrap_file(JCR *jcr)
1020 /* Now update the bootstrap file if any */
1021 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
1022 jcr->job->WriteBootstrap) {
1024 BPIPE *bpipe = NULL;
1026 POOLMEM *fname = get_pool_memory(PM_FNAME);
1027 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
1029 VOL_PARAMS *VolParams = NULL;
1031 char edt[50], ed1[50], ed2[50];
1033 if (*fname == '|') {
1035 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
1036 fd = bpipe ? bpipe->wfd : NULL;
1038 /* ***FIXME*** handle BASE */
1039 fd = bfopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
1042 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
1044 if (VolCount == 0) {
1045 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
1046 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
1047 if (jcr->SDJobFiles != 0) {
1048 jcr->setJobStatus(JS_ErrorTerminated);
1052 /* Start output with when and who wrote it */
1053 bstrftimes(edt, sizeof(edt), time(NULL));
1054 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
1055 level_to_str(jcr->getJobLevel()), jcr->since);
1056 for (int i=0; i < VolCount; i++) {
1057 /* Write the record */
1058 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
1059 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
1060 if (VolParams[i].Slot > 0) {
1061 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
1063 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
1064 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
1065 fprintf(fd, "VolAddr=%s-%s\n",
1066 edit_uint64(VolParams[i].StartAddr, ed1),
1067 edit_uint64(VolParams[i].EndAddr, ed2));
1068 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
1069 VolParams[i].LastIndex);
1081 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
1082 "%s: ERR=%s\n"), fname, be.bstrerror());
1083 jcr->setJobStatus(JS_ErrorTerminated);
1085 free_pool_memory(fname);