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;
361 /* TLS Requirement for the client */
362 if (jcr->client->tls_enable) {
363 if (jcr->client->tls_require) {
364 tls_need = BNET_TLS_REQUIRED;
366 tls_need = BNET_TLS_OK;
370 * Send Client address to the SD
372 sd->fsend(clientaddr, jcr->client->address(buf.addr()), jcr->client->FDport, tls_need);
373 if (!response(jcr, sd, OKclient, "Client", DISPLAY_ERROR)) {
380 * Allow to specify the address used by the Client to
381 * connect to the storage daemon in the Client resource
382 * or in the Storage resource.
384 char *get_storage_address(CLIENT *client, STORE *store)
388 if (client && client->fd_storage_address) {
389 Dmsg0(10, "Using Client resource FD Storage Address to contact the Storage\n");
390 store_address = client->fd_storage_address;
392 } else if (store->fd_storage_address) {
393 Dmsg0(10, "Using Storage resource FD Storage Address to contact the Storage\n");
394 store_address = store->fd_storage_address;
397 Dmsg0(10, "Using default Storage address\n");
398 store_address = store->address;
400 return store_address;
403 bool run_storage_and_start_message_thread(JCR *jcr, BSOCK *sd)
406 * Start the job prior to starting the message thread below
407 * to avoid two threads from using the BSOCK structure at
410 if (!sd->fsend("run")) {
415 * Now start a Storage daemon message thread. Note,
416 * this thread is used to provide the catalog services
417 * for the backup job, including inserting the attributes
418 * into the catalog. See catalog_update() in catreq.c
420 if (!start_storage_daemon_message_thread(jcr)) {
423 Dmsg0(150, "Storage daemon connection OK\n");
428 * Do a backup of the specified FileSet
430 * Returns: false on failure
433 bool do_backup(JCR *jcr)
444 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
445 return do_vbackup(jcr);
448 /* Print Job Start message */
449 if (jcr->rerunning) {
450 Jmsg(jcr, M_INFO, 0, _("Restart Incomplete Backup JobId %s, Job=%s\n"),
451 edit_uint64(jcr->JobId, ed1), jcr->Job);
453 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
454 edit_uint64(jcr->JobId, ed1), jcr->Job);
457 jcr->setJobStatus(JS_Running);
458 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
459 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
460 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
464 /* For incomplete Jobs, we add our own id */
465 if (jcr->rerunning) {
466 edit_int64(jcr->JobId, ed1);
467 Mmsg(buf, "SELECT max(FileIndex) FROM File WHERE JobId=%s", ed1);
468 if (db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
469 Jmsg(jcr, M_INFO, 0, _("Found %ld files from prior incomplete Job.\n"),
472 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
475 jcr->JobFiles = job.value;
476 Dmsg1(100, "==== FI=%ld\n", jcr->JobFiles);
477 Mmsg(buf, "SELECT VolSessionId FROM Job WHERE JobId=%s", ed1);
478 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
479 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
482 jcr->VolSessionId = job.value;
483 Mmsg(buf, "SELECT VolSessionTime FROM Job WHERE JobId=%s", ed1);
484 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
485 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
488 jcr->VolSessionTime = job.value;
489 Dmsg4(100, "JobId=%s JobFiles=%ld VolSessionId=%ld VolSessionTime=%ld\n", ed1,
490 jcr->JobFiles, jcr->VolSessionId, jcr->VolSessionTime);
494 * Open a message channel connection with the Storage
495 * daemon. This is to let him know that our client
496 * will be contacting him for a backup session.
499 Dmsg0(110, "Open connection with storage daemon\n");
500 jcr->setJobStatus(JS_WaitSD);
502 * Start conversation with Storage daemon
504 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
508 * Now start a job with the Storage daemon
510 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
513 sd = jcr->store_bsock;
515 jcr->sd_calls_client = jcr->client->sd_calls_client;
518 * Note startup sequence of SD/FD is different depending on
519 * whether the SD listens (normal) or the SD calls the FD.
521 if (!jcr->sd_calls_client) {
522 if (!run_storage_and_start_message_thread(jcr, sd)) {
526 jcr->setJobStatus(JS_WaitFD);
527 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
531 jcr->setJobStatus(JS_Running);
532 fd = jcr->file_bsock;
534 if (!send_level_command(jcr)) {
538 if (!send_include_list(jcr)) {
542 if (!send_exclude_list(jcr)) {
546 /* TODO: See priority with bandwidth parameter */
547 if (jcr->job->max_bandwidth > 0) {
548 jcr->max_bandwidth = jcr->job->max_bandwidth;
549 } else if (jcr->client->max_bandwidth > 0) {
550 jcr->max_bandwidth = jcr->client->max_bandwidth;
553 if (jcr->max_bandwidth > 0) {
554 send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
557 send_snapshot_retention(jcr, jcr->snapshot_retention);
561 if (jcr->sd_calls_client) {
562 if (jcr->FDVersion < 10) {
563 Jmsg(jcr, M_FATAL, 0, _("The File daemon does not support SDCallsClient.\n"));
566 if (!send_client_addr_to_sd(jcr)) {
570 if (!run_storage_and_start_message_thread(jcr, sd)) {
574 store_address = jcr->wstore->address; /* dummy */
575 store_port = 0; /* flag that SD calls FD */
578 * send Storage daemon address to the File daemon
580 if (store->SDDport == 0) {
581 store->SDDport = store->SDport;
584 store_address = get_storage_address(jcr->client, store);
585 store_port = store->SDDport;
588 if (!send_store_addr_to_fd(jcr, store, store_address, store_port)) {
592 /* Declare the job started to start the MaxRunTime check */
593 jcr->setJobStarted();
595 /* Send and run the RunBefore */
596 if (!send_runscripts_commands(jcr)) {
601 * We re-update the job start record so that the start
602 * time is set after the run before job. This avoids
603 * that any files created by the run before job will
604 * be saved twice. They will be backed up in the current
605 * job, but not in the next one unless they are changed.
606 * Without this, they will be backed up in this job and
607 * in the next job run because in that case, their date
608 * is after the start of this run.
610 jcr->start_time = time(NULL);
611 jcr->jr.StartTime = jcr->start_time;
612 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
613 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
617 * If backup is in accurate mode, we send the list of
620 if (!send_accurate_current_files(jcr)) {
621 goto bail_out; /* error */
624 /* Send backup command */
625 fd->fsend(backupcmd, jcr->JobFiles);
626 Dmsg1(100, ">filed: %s", fd->msg);
627 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
631 /* Pickup Job termination data */
632 stat = wait_for_job_termination(jcr);
633 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
636 db_commit_base_file_attributes_record(jcr, jcr->db);
637 /* Any error already printed */
640 if (!jcr->is_canceled() && stat == JS_Terminated) {
641 backup_cleanup(jcr, stat);
646 /* Come here only after starting SD thread */
648 jcr->setJobStatus(JS_ErrorTerminated);
649 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
651 wait_for_job_termination(jcr, FDConnectTimeout);
652 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
658 * Here we wait for the File daemon to signal termination,
659 * then we wait for the Storage daemon. When both
660 * are done, we return the job status.
661 * Also used by restore.c
663 int wait_for_job_termination(JCR *jcr, int timeout)
666 BSOCK *fd = jcr->file_bsock;
668 uint32_t JobFiles, JobErrors;
669 uint32_t JobWarnings = 0;
670 uint64_t ReadBytes = 0;
671 uint64_t JobBytes = 0;
672 uint64_t CommBytes = 0;
673 uint64_t CommCompressedBytes = 0;
674 int VSS = 0; /* or Snapshot on Unix */
680 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
682 /* Wait for Client to terminate */
683 while ((n = bget_dirmsg(fd)) >= 0) {
685 (sscanf(fd->msg, newEndJob, &jcr->FDJobStatus, &JobFiles,
686 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt,
687 &CommBytes, &CommCompressedBytes) == 9 ||
688 sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
689 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
690 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
691 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
693 jcr->setJobStatus(jcr->FDJobStatus);
694 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
696 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
699 if (job_canceled(jcr)) {
704 stop_bsock_timer(tid);
707 if (fd->is_error() && jcr->getJobStatus() != JS_Canceled) {
709 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
710 job_type_to_str(jcr->getJobType()), fd->bstrerror());
711 while (i++ < 20 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
715 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
719 * Force cancel in SD if failing, but not for Incomplete jobs
720 * so that we let the SD despool.
722 Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
723 jcr->JobStatus, jcr->SDJobStatus);
724 if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
725 Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
726 jcr->JobStatus, jcr->SDJobStatus);
727 cancel_storage_daemon_job(jcr);
730 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
731 wait_for_storage_daemon_termination(jcr);
733 /* Return values from FD */
735 jcr->JobFiles = JobFiles;
736 jcr->JobErrors += JobErrors; /* Keep total errors */
737 jcr->ReadBytes = ReadBytes;
738 jcr->JobBytes = JobBytes;
739 jcr->JobWarnings = JobWarnings;
740 jcr->CommBytes = CommBytes;
741 jcr->CommCompressedBytes = CommCompressedBytes;
743 jcr->Encrypt = Encrypt;
744 } else if (jcr->getJobStatus() != JS_Canceled) {
745 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
748 /* Return the first error status we find Dir, FD, or SD */
749 if (!fd_ok || fd->is_error()) { /* if fd not set, that use !fd_ok */
750 if (jcr->getJobStatus() == JS_Canceled) {
751 jcr->FDJobStatus = JS_Canceled;
753 jcr->FDJobStatus = JS_ErrorTerminated;
756 if (jcr->JobStatus != JS_Terminated) {
757 return jcr->JobStatus;
759 if (jcr->FDJobStatus != JS_Terminated) {
760 return jcr->FDJobStatus;
762 return jcr->SDJobStatus;
766 * Release resources allocated during backup.
768 void backup_cleanup(JCR *jcr, int TermCode)
770 char sdt[50], edt[50], schedt[50];
771 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30];
772 char ec6[30], ec7[30], ec8[30], ec9[30], ec10[30], elapsed[50];
773 char data_compress[200], comm_compress[200];
774 char fd_term_msg[100], sd_term_msg[100];
776 int msg_type = M_INFO;
779 double kbps, compression, ratio;
784 remove_dummy_jobmedia_records(jcr);
786 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
787 vbackup_cleanup(jcr, TermCode);
791 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
792 memset(&cr, 0, sizeof(cr));
795 /* The current implementation of the JS_Warning status is not
796 * completed. SQL part looks to be ok, but the code is using
797 * JS_Terminated almost everywhere instead of (JS_Terminated || JS_Warning)
798 * as we do with is_canceled()
800 if (jcr->getJobStatus() == JS_Terminated &&
801 (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
802 TermCode = JS_Warnings;
806 update_job_end(jcr, TermCode);
808 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
809 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
810 db_strerror(jcr->db));
811 jcr->setJobStatus(JS_ErrorTerminated);
814 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
815 if (!db_get_client_record(jcr, jcr->db, &cr)) {
816 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
817 db_strerror(jcr->db));
820 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
821 if (!db_get_media_record(jcr, jcr->db, &mr)) {
822 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
823 mr.VolumeName, db_strerror(jcr->db));
824 jcr->setJobStatus(JS_ErrorTerminated);
827 update_bootstrap_file(jcr);
829 switch (jcr->JobStatus) {
831 if (jcr->JobErrors || jcr->SDErrors) {
832 Mmsg(term_msg, _("Backup OK -- %s"), jcr->StatusErrMsg[0] ? jcr->StatusErrMsg : _("with warnings"));
835 Mmsg(term_msg, _("Backup OK"));
839 Mmsg(term_msg, _("Backup failed -- incomplete"));
842 Mmsg(term_msg, _("Backup OK -- %s"), jcr->StatusErrMsg[0] ? jcr->StatusErrMsg : _("with warnings"));
845 case JS_ErrorTerminated:
846 Mmsg(term_msg, _("*** Backup Error ***"));
847 msg_type = M_ERROR; /* Generate error message */
848 if (jcr->store_bsock) {
849 jcr->store_bsock->signal(BNET_TERMINATE);
850 if (jcr->SD_msg_chan_started) {
851 pthread_cancel(jcr->SD_msg_chan);
856 Mmsg(term_msg, _("Backup Canceled"));
857 if (jcr->store_bsock) {
858 jcr->store_bsock->signal(BNET_TERMINATE);
859 if (jcr->SD_msg_chan_started) {
860 pthread_cancel(jcr->SD_msg_chan);
865 Mmsg(term_msg, _("Inappropriate term code: %c\n"), jcr->JobStatus);
868 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
869 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
870 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
871 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
875 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
876 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
878 * Note, if the job has erred, most likely it did not write any
879 * tape, so suppress this "error" message since in that case
880 * it is normal. Or look at it the other way, only for a
881 * normal exit should we complain about this error.
883 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
884 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
886 jcr->VolumeName[0] = 0; /* none */
889 if (jcr->ReadBytes == 0) {
890 bstrncpy(data_compress, "None", sizeof(data_compress));
892 compression = (double)100 - 100.0 * ((double)jcr->SDJobBytes / (double)jcr->ReadBytes);
893 if (compression < 0.5) {
894 bstrncpy(data_compress, "None", sizeof(data_compress));
896 if (jcr->SDJobBytes > 0) {
897 ratio = (double)jcr->ReadBytes / (double)jcr->SDJobBytes;
901 bsnprintf(data_compress, sizeof(data_compress), "%.1f%% %.1f:1",
905 if (jcr->CommBytes == 0 || jcr->CommCompressedBytes == 0) {
906 bstrncpy(comm_compress, "None", sizeof(comm_compress));
908 compression = (double)100 - 100.0 * ((double)jcr->CommCompressedBytes / (double)jcr->CommBytes);
909 if (compression < 0.5) {
910 bstrncpy(comm_compress, "None", sizeof(comm_compress));
912 ratio = (double)jcr->CommBytes / (double)jcr->CommCompressedBytes;
913 bsnprintf(comm_compress, sizeof(comm_compress), "%.1f%% %.1f:1",
916 Dmsg2(200, "=== CommCompressed=%lld CommBytes=%lld\n",
917 jcr->CommCompressedBytes, jcr->CommBytes);
919 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
920 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
923 Mmsg(base_info, _(" Base files/Used files: %lld/%lld (%.2f%%)\n"),
925 jcr->nb_base_files_used,
926 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
928 /* Edit string for last volume size */
929 if (mr.VolABytes != 0) {
930 Mmsg(vol_info, _("meta: %s (%sB) aligned: %s (%sB)"),
931 edit_uint64_with_commas(mr.VolBytes, ec7),
932 edit_uint64_with_suffix(mr.VolBytes, ec8),
933 edit_uint64_with_commas(mr.VolABytes, ec9),
934 edit_uint64_with_suffix(mr.VolABytes, ec10));
936 Mmsg(vol_info, _("%s (%sB)"),
937 edit_uint64_with_commas(mr.VolBytes, ec7),
938 edit_uint64_with_suffix(mr.VolBytes, ec8));
941 // bmicrosleep(15, 0); /* for debugging SIGHUP */
943 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
944 " Build OS: %s %s %s\n"
947 " Backup Level: %s%s\n"
948 " Client: \"%s\" %s\n"
949 " FileSet: \"%s\" %s\n"
950 " Pool: \"%s\" (From %s)\n"
951 " Catalog: \"%s\" (From %s)\n"
952 " Storage: \"%s\" (From %s)\n"
953 " Scheduled time: %s\n"
956 " Elapsed time: %s\n"
958 " FD Files Written: %s\n"
959 " SD Files Written: %s\n"
960 " FD Bytes Written: %s (%sB)\n"
961 " SD Bytes Written: %s (%sB)\n"
963 " Software Compression: %s\n"
964 " Comm Line Compression: %s\n"
965 "%s" /* Basefile info */
966 " Snapshot/VSS: %s\n"
969 " Volume name(s): %s\n"
970 " Volume Session Id: %d\n"
971 " Volume Session Time: %d\n"
972 " Last Volume Bytes: %s\n"
973 " Non-fatal FD errors: %d\n"
975 " FD termination status: %s\n"
976 " SD termination status: %s\n"
977 " Termination: %s\n\n"),
978 BACULA, my_name, VERSION, LSMDATE,
979 HOST_OS, DISTNAME, DISTVER,
982 level_to_str(jcr->getJobLevel()), jcr->since,
983 jcr->client->name(), cr.Uname,
984 jcr->fileset->name(), jcr->FSCreateTime,
985 jcr->pool->name(), jcr->pool_source,
986 jcr->catalog->name(), jcr->catalog_source,
987 jcr->wstore->name(), jcr->wstore_source,
991 edit_utime(RunTime, elapsed, sizeof(elapsed)),
993 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
994 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
995 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
996 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
997 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
998 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
1003 jcr->Snapshot?_("yes"):_("no"),
1004 jcr->Encrypt?_("yes"):_("no"),
1005 jcr->accurate?_("yes"):_("no"),
1008 jcr->VolSessionTime,
1016 Dmsg0(100, "Leave backup_cleanup()\n");
1019 void update_bootstrap_file(JCR *jcr)
1021 /* Now update the bootstrap file if any */
1022 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
1023 jcr->job->WriteBootstrap) {
1025 BPIPE *bpipe = NULL;
1027 POOLMEM *fname = get_pool_memory(PM_FNAME);
1028 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "",
1029 job_code_callback_director);
1031 VOL_PARAMS *VolParams = NULL;
1033 char edt[50], ed1[50], ed2[50];
1035 if (*fname == '|') {
1037 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
1038 fd = bpipe ? bpipe->wfd : NULL;
1040 /* ***FIXME*** handle BASE */
1041 fd = bfopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
1044 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
1046 if (VolCount == 0) {
1047 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
1048 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
1049 if (jcr->SDJobFiles != 0) {
1050 jcr->setJobStatus(JS_ErrorTerminated);
1054 /* Start output with when and who wrote it */
1055 bstrftimes(edt, sizeof(edt), time(NULL));
1056 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
1057 level_to_str(jcr->getJobLevel()), jcr->since);
1058 for (int i=0; i < VolCount; i++) {
1059 /* Write the record */
1060 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
1061 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
1062 if (VolParams[i].Slot > 0) {
1063 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
1065 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
1066 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
1067 fprintf(fd, "VolAddr=%s-%s\n",
1068 edit_uint64(VolParams[i].StartAddr, ed1),
1069 edit_uint64(VolParams[i].EndAddr, ed2));
1070 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
1071 VolParams[i].LastIndex);
1083 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
1084 "%s: ERR=%s\n"), fname, be.bstrerror());
1085 jcr->setJobStatus(JS_ErrorTerminated);
1087 free_pool_memory(fname);