2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 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.
21 * Bacula Director -- backup.c -- responsible for doing backup jobs
23 * Kern Sibbald, March MM
25 * Basic tasks done here:
26 * Open DB and create records for this job.
27 * Open Message Channel with Storage daemon to tell him a job will be starting.
28 * Open connection with File daemon and pass him commands
30 * When the File daemon finishes the job, update the DB.
38 /* Commands sent to File daemon */
39 static char backupcmd[] = "backup FileIndex=%ld\n";
40 static char storaddr[] = "storage address=%s port=%d ssl=%d\n";
42 /* Responses received from File daemon */
43 static char OKbackup[] = "2000 OK backup\n";
44 static char OKstore[] = "2000 OK storage\n";
45 static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
46 "ReadBytes=%llu JobBytes=%llu Errors=%u "
47 "VSS=%d Encrypt=%d\n";
48 /* Pre 1.39.29 (04Dec06) EndJob */
49 static char OldEndJob[] = "2800 End Job TermCode=%d JobFiles=%u "
50 "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
52 /* Commands sent to Storage daemon */
53 static char clientaddr[] = "client address=%s port=%d ssl=%d\n";
55 /* Commands received from Storage daemon */
56 static char OKclient[] = "3000 OK client command\n";
59 * Called here before the job is run to do the job
62 bool do_backup_init(JCR *jcr)
65 jcr->RescheduleIncompleteJobs = jcr->job->RescheduleIncompleteJobs;
67 if (!get_or_create_fileset_record(jcr)) {
68 Dmsg1(100, "JobId=%d no FileSet\n", (int)jcr->JobId);
73 * Get definitive Job level and since time
74 * unless it's a virtual full. In that case
77 if (!jcr->is_JobLevel(L_VIRTUAL_FULL)) {
78 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) {
89 Dmsg1(100, "JobId=%d no PoolId\n", (int)jcr->JobId);
90 Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
95 * If we are a virtual full job or got upgraded to one
96 * then we divert at this point and call the virtual full
99 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
100 return do_vbackup_init(jcr);
103 free_rstorage(jcr); /* we don't read so release */
105 /* If pool storage specified, use it instead of job storage */
106 copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
108 if (!jcr->wstorage) {
109 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
113 create_clones(jcr); /* run any clone jobs */
118 /* Take all base jobs from job resource and find the
121 static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids)
128 if (!jcr->job->base) {
129 return false; /* no base job, stop accurate */
132 memset(&jr, 0, sizeof(JOB_DBR));
133 jr.StartTime = jcr->jr.StartTime;
135 foreach_alist(job, jcr->job->base) {
136 bstrncpy(jr.Name, job->name(), sizeof(jr.Name));
137 db_get_base_jobid(jcr, jcr->db, &jr, &id);
141 pm_strcat(jobids->list, ",");
143 pm_strcat(jobids->list, edit_uint64(id, str_jobid));
148 return jobids->count > 0;
152 * Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
153 * row[0]=Path, row[1]=Filename, row[2]=FileIndex
154 * row[3]=JobId row[4]=LStat row[5]=DeltaSeq row[6]=MD5
156 static int accurate_list_handler(void *ctx, int num_fields, char **row)
158 JCR *jcr = (JCR *)ctx;
160 if (job_canceled(jcr)) {
164 if (row[2][0] == '0') { /* discard when file_index == 0 */
168 /* sending with checksum */
169 if (jcr->use_accurate_chksum
171 && row[6][0] /* skip checksum = '0' */
174 jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s",
175 row[0], row[1], 0, row[4], 0, row[6], 0, row[5]);
177 jcr->file_bsock->fsend("%s%s%c%s%c%c%s",
178 row[0], row[1], 0, row[4], 0, 0, row[5]);
183 /* In this procedure, we check if the current fileset is using checksum
184 * FileSet-> Include-> Options-> Accurate/Verify/BaseJob=checksum
185 * This procedure uses jcr->HasBase, so it must be call after the initialization
187 static bool is_checksum_needed_by_fileset(JCR *jcr)
193 bool have_basejob_option=false;
194 if (!jcr->job || !jcr->job->fileset) {
198 f = jcr->job->fileset;
200 for (int i=0; i < f->num_includes; i++) { /* Parse all Include {} */
201 inc = f->include_items[i];
203 for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
204 fopts = inc->opts_list[j];
206 for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
208 case 'V': /* verify */
209 in_block = (jcr->getJobType() == JT_VERIFY); /* not used now */
211 case 'J': /* Basejob keyword */
212 have_basejob_option = in_block = jcr->HasBase;
214 case 'C': /* Accurate keyword */
215 in_block = !jcr->is_JobLevel(L_FULL);
217 case ':': /* End of keyword */
223 Dmsg0(50, "Checksum will be sent to FD\n");
234 /* By default for BaseJobs, we send the checksum */
235 if (!have_basejob_option && jcr->HasBase) {
239 Dmsg0(50, "Checksum will be sent to FD\n");
244 * Send current file list to FD
245 * DIR -> FD : accurate files=xxxx
246 * DIR -> FD : /path/to/file\0Lstat\0MD5\0Delta
247 * DIR -> FD : /path/to/dir/\0Lstat\0MD5\0Delta
251 bool send_accurate_current_files(JCR *jcr)
258 /* In base level, no previous job is used and no restart incomplete jobs */
259 if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
262 if (!jcr->accurate && !jcr->rerunning) {
266 if (jcr->is_JobLevel(L_FULL)) {
267 /* On Full mode, if no previous base job, no accurate things */
268 if (get_base_jobids(jcr, &jobids)) {
270 Jmsg(jcr, M_INFO, 0, _("Using Base JobId(s): %s\n"), jobids.list);
271 } else if (!jcr->rerunning) {
275 /* For Incr/Diff level, we search for older jobs */
276 db_get_accurate_jobids(jcr, jcr->db, &jcr->jr, &jobids);
278 /* We are in Incr/Diff, but no Full to build the accurate list... */
279 if (jobids.count == 0) {
280 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
281 return false; /* fail */
285 /* For incomplete Jobs, we add our own id */
286 if (jcr->rerunning) {
287 edit_int64(jcr->JobId, ed1);
291 /* Don't send and store the checksum if fileset doesn't require it */
292 jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
294 if (jcr->JobId) { /* display the message only for real jobs */
295 Jmsg(jcr, M_INFO, 0, _("Sending Accurate information to the FD.\n"));
298 /* to be able to allocate the right size for htable */
299 Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
300 db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
301 Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
302 jcr->file_bsock->fsend("accurate files=%s\n", nb.list);
304 if (!db_open_batch_connexion(jcr, jcr->db)) {
305 Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
306 return false; /* Fail */
310 jcr->nb_base_files = str_to_int64(nb.list);
311 if (!db_create_base_file_list(jcr, jcr->db, jobids.list)) {
312 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
315 if (!db_get_base_file_list(jcr, jcr->db, jcr->use_accurate_chksum,
316 accurate_list_handler, (void *)jcr)) {
317 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
322 if (!db_get_file_list(jcr, jcr->db_batch,
323 jobids.list, jcr->use_accurate_chksum, false /* no delta */,
324 accurate_list_handler, (void *)jcr)) {
325 Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db_batch));
330 /* TODO: close the batch connection ? (can be used very soon) */
331 jcr->file_bsock->signal(BNET_EOD);
335 bool send_store_addr_to_fd(JCR *jcr, STORE *store,
336 char *store_address, uint32_t store_port)
338 int tls_need = BNET_TLS_NONE;
340 /* TLS Requirement */
341 if (store->tls_enable) {
342 if (store->tls_require) {
343 tls_need = BNET_TLS_REQUIRED;
345 tls_need = BNET_TLS_OK;
350 * Send Storage address to the FD
352 jcr->file_bsock->fsend(storaddr, store_address, store_port, tls_need);
353 if (!response(jcr, jcr->file_bsock, OKstore, "Storage", DISPLAY_ERROR)) {
359 bool send_client_addr_to_sd(JCR *jcr)
361 int tls_need = BNET_TLS_NONE;
362 BSOCK *sd = jcr->store_bsock;
364 /* TLS Requirement for the client */
365 if (jcr->client->tls_enable) {
366 if (jcr->client->tls_require) {
367 tls_need = BNET_TLS_REQUIRED;
369 tls_need = BNET_TLS_OK;
373 * Send Client address to the SD
375 sd->fsend(clientaddr, jcr->client->address, jcr->client->FDport, tls_need);
376 if (!response(jcr, sd, OKclient, "Client", DISPLAY_ERROR)) {
383 * Allow to specify the address used by the Client to
384 * connect to the storage daemon in the Client resource
385 * or in the Storage resource.
387 char *get_storage_address(CLIENT *client, STORE *store)
391 if (client && client->fd_storage_address) {
392 Dmsg0(10, "Using Client resource FD Storage Address to contact the Storage\n");
393 store_address = client->fd_storage_address;
395 } else if (store->fd_storage_address) {
396 Dmsg0(10, "Using Storage resource FD Storage Address to contact the Storage\n");
397 store_address = store->fd_storage_address;
400 Dmsg0(10, "Using default Storage address\n");
401 store_address = store->address;
403 return store_address;
406 bool run_storage_and_start_message_thread(JCR *jcr, BSOCK *sd)
409 * Start the job prior to starting the message thread below
410 * to avoid two threads from using the BSOCK structure at
413 if (!sd->fsend("run")) {
418 * Now start a Storage daemon message thread. Note,
419 * this thread is used to provide the catalog services
420 * for the backup job, including inserting the attributes
421 * into the catalog. See catalog_update() in catreq.c
423 if (!start_storage_daemon_message_thread(jcr)) {
426 Dmsg0(150, "Storage daemon connection OK\n");
431 * Do a backup of the specified FileSet
433 * Returns: false on failure
436 bool do_backup(JCR *jcr)
447 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
448 return do_vbackup(jcr);
451 /* Print Job Start message */
452 if (jcr->rerunning) {
453 Jmsg(jcr, M_INFO, 0, _("Restart Incomplete Backup JobId %s, Job=%s\n"),
454 edit_uint64(jcr->JobId, ed1), jcr->Job);
456 Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
457 edit_uint64(jcr->JobId, ed1), jcr->Job);
460 jcr->setJobStatus(JS_Running);
461 Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
462 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
463 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
467 /* For incomplete Jobs, we add our own id */
468 if (jcr->rerunning) {
469 edit_int64(jcr->JobId, ed1);
470 Mmsg(buf, "SELECT max(FileIndex) FROM File WHERE JobId=%s", ed1);
471 if (db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
472 Jmsg(jcr, M_INFO, 0, _("Found %ld files from prior incomplete Job.\n"),
475 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
478 jcr->JobFiles = job.value;
479 Dmsg1(100, "==== FI=%ld\n", jcr->JobFiles);
480 Mmsg(buf, "SELECT VolSessionId FROM Job WHERE JobId=%s", ed1);
481 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
482 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
485 jcr->VolSessionId = job.value;
486 Mmsg(buf, "SELECT VolSessionTime FROM Job WHERE JobId=%s", ed1);
487 if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
488 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
491 jcr->VolSessionTime = job.value;
492 Dmsg4(100, "JobId=%s JobFiles=%ld VolSessionId=%ld VolSessionTime=%ld\n", ed1,
493 jcr->JobFiles, jcr->VolSessionId, jcr->VolSessionTime);
497 * Open a message channel connection with the Storage
498 * daemon. This is to let him know that our client
499 * will be contacting him for a backup session.
502 Dmsg0(110, "Open connection with storage daemon\n");
503 jcr->setJobStatus(JS_WaitSD);
505 * Start conversation with Storage daemon
507 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
511 * Now start a job with the Storage daemon
513 if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
516 sd = jcr->store_bsock;
518 jcr->sd_calls_client = jcr->client->sd_calls_client;
521 * Note startup sequence of SD/FD is different depending on
522 * whether the SD listens (normal) or the SD calls the FD.
524 if (!jcr->sd_calls_client) {
525 if (!run_storage_and_start_message_thread(jcr, sd)) {
529 jcr->setJobStatus(JS_WaitFD);
530 if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
534 jcr->setJobStatus(JS_Running);
535 fd = jcr->file_bsock;
537 if (!send_level_command(jcr)) {
541 if (!send_include_list(jcr)) {
545 if (!send_exclude_list(jcr)) {
549 /* TODO: See priority with bandwidth parameter */
550 if (jcr->job->max_bandwidth > 0) {
551 jcr->max_bandwidth = jcr->job->max_bandwidth;
552 } else if (jcr->client->max_bandwidth > 0) {
553 jcr->max_bandwidth = jcr->client->max_bandwidth;
556 if (jcr->max_bandwidth > 0) {
557 send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
560 send_snapshot_retention(jcr, jcr->snapshot_retention);
564 if (jcr->sd_calls_client) {
565 if (jcr->FDVersion < 10) {
566 Jmsg(jcr, M_FATAL, 0, _("The File daemon does not support SDCallsClient.\n"));
569 if (!send_client_addr_to_sd(jcr)) {
573 if (!run_storage_and_start_message_thread(jcr, sd)) {
577 store_address = jcr->wstore->address; /* dummy */
578 store_port = 0; /* flag that SD calls FD */
581 * send Storage daemon address to the File daemon
583 if (store->SDDport == 0) {
584 store->SDDport = store->SDport;
587 store_address = get_storage_address(jcr->client, store);
588 store_port = store->SDDport;
591 if (!send_store_addr_to_fd(jcr, store, store_address, store_port)) {
595 /* Declare the job started to start the MaxRunTime check */
596 jcr->setJobStarted();
598 /* Send and run the RunBefore */
599 if (!send_runscripts_commands(jcr)) {
604 * We re-update the job start record so that the start
605 * time is set after the run before job. This avoids
606 * that any files created by the run before job will
607 * be saved twice. They will be backed up in the current
608 * job, but not in the next one unless they are changed.
609 * Without this, they will be backed up in this job and
610 * in the next job run because in that case, their date
611 * is after the start of this run.
613 jcr->start_time = time(NULL);
614 jcr->jr.StartTime = jcr->start_time;
615 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
616 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
620 * If backup is in accurate mode, we send the list of
623 if (!send_accurate_current_files(jcr)) {
624 goto bail_out; /* error */
627 /* Send backup command */
628 fd->fsend(backupcmd, jcr->JobFiles);
629 Dmsg1(100, ">filed: %s", fd->msg);
630 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
634 /* Pickup Job termination data */
635 stat = wait_for_job_termination(jcr);
636 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
639 db_commit_base_file_attributes_record(jcr, jcr->db);
640 /* Any error already printed */
643 if (!jcr->is_canceled() && stat == JS_Terminated) {
644 backup_cleanup(jcr, stat);
649 /* Come here only after starting SD thread */
651 jcr->setJobStatus(JS_ErrorTerminated);
652 Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
654 wait_for_job_termination(jcr, FDConnectTimeout);
655 Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
661 * Here we wait for the File daemon to signal termination,
662 * then we wait for the Storage daemon. When both
663 * are done, we return the job status.
664 * Also used by restore.c
666 int wait_for_job_termination(JCR *jcr, int timeout)
669 BSOCK *fd = jcr->file_bsock;
671 uint32_t JobFiles, JobErrors;
672 uint32_t JobWarnings = 0;
673 uint64_t ReadBytes = 0;
674 uint64_t JobBytes = 0;
675 int VSS = 0; /* or Snapshot on Unix */
681 tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
683 /* Wait for Client to terminate */
684 while ((n = bget_dirmsg(fd)) >= 0) {
686 (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
687 &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
688 sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
689 &ReadBytes, &JobBytes, &JobErrors) == 5)) {
691 jcr->setJobStatus(jcr->FDJobStatus);
692 Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
694 Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
697 if (job_canceled(jcr)) {
702 stop_bsock_timer(tid);
705 if (fd->is_error() && jcr->getJobStatus() != JS_Canceled) {
707 Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
708 job_type_to_str(jcr->getJobType()), fd->bstrerror());
709 while (i++ < 20 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
713 fd->signal(BNET_TERMINATE); /* tell Client we are terminating */
717 * Force cancel in SD if failing, but not for Incomplete jobs
718 * so that we let the SD despool.
720 Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
721 jcr->JobStatus, jcr->SDJobStatus);
722 if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
723 Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
724 jcr->JobStatus, jcr->SDJobStatus);
725 cancel_storage_daemon_job(jcr);
728 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
729 wait_for_storage_daemon_termination(jcr);
731 /* Return values from FD */
733 jcr->JobFiles = JobFiles;
734 jcr->JobErrors += JobErrors; /* Keep total errors */
735 jcr->ReadBytes = ReadBytes;
736 jcr->JobBytes = JobBytes;
737 jcr->JobWarnings = JobWarnings;
739 jcr->Encrypt = Encrypt;
740 } else if (jcr->getJobStatus() != JS_Canceled) {
741 Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
744 /* Return the first error status we find Dir, FD, or SD */
745 if (!fd_ok || fd->is_error()) { /* if fd not set, that use !fd_ok */
746 if (jcr->getJobStatus() == JS_Canceled) {
747 jcr->FDJobStatus = JS_Canceled;
749 jcr->FDJobStatus = JS_ErrorTerminated;
752 if (jcr->JobStatus != JS_Terminated) {
753 return jcr->JobStatus;
755 if (jcr->FDJobStatus != JS_Terminated) {
756 return jcr->FDJobStatus;
758 return jcr->SDJobStatus;
762 * Release resources allocated during backup.
764 void backup_cleanup(JCR *jcr, int TermCode)
766 char sdt[50], edt[50], schedt[50];
767 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30];
768 char ec6[30], ec7[30], ec8[30], ec9[30], ec10[30], elapsed[50];
769 char data_compress[200];
770 char term_code[100], fd_term_msg[100], sd_term_msg[100];
771 const char *term_msg;
772 int msg_type = M_INFO;
775 double kbps, compression, ratio;
780 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
781 vbackup_cleanup(jcr, TermCode);
785 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
786 memset(&cr, 0, sizeof(cr));
788 update_job_end(jcr, TermCode);
790 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
791 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
792 db_strerror(jcr->db));
793 jcr->setJobStatus(JS_ErrorTerminated);
796 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
797 if (!db_get_client_record(jcr, jcr->db, &cr)) {
798 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
799 db_strerror(jcr->db));
802 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
803 if (!db_get_media_record(jcr, jcr->db, &mr)) {
804 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
805 mr.VolumeName, db_strerror(jcr->db));
806 jcr->setJobStatus(JS_ErrorTerminated);
809 update_bootstrap_file(jcr);
811 switch (jcr->JobStatus) {
813 if (jcr->JobErrors || jcr->SDErrors) {
814 term_msg = _("Backup OK -- with warnings");
816 term_msg = _("Backup OK");
820 term_msg = _("Backup failed -- incomplete");
823 term_msg = _("Backup OK -- with warnings");
826 case JS_ErrorTerminated:
827 term_msg = _("*** Backup Error ***");
828 msg_type = M_ERROR; /* Generate error message */
829 if (jcr->store_bsock) {
830 jcr->store_bsock->signal(BNET_TERMINATE);
831 if (jcr->SD_msg_chan_started) {
832 pthread_cancel(jcr->SD_msg_chan);
837 term_msg = _("Backup Canceled");
838 if (jcr->store_bsock) {
839 jcr->store_bsock->signal(BNET_TERMINATE);
840 if (jcr->SD_msg_chan_started) {
841 pthread_cancel(jcr->SD_msg_chan);
846 term_msg = term_code;
847 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
850 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
851 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
852 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
853 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
857 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
859 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
861 * Note, if the job has erred, most likely it did not write any
862 * tape, so suppress this "error" message since in that case
863 * it is normal. Or look at it the other way, only for a
864 * normal exit should we complain about this error.
866 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
867 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
869 jcr->VolumeName[0] = 0; /* none */
872 if (jcr->ReadBytes == 0) {
873 bstrncpy(data_compress, "None", sizeof(data_compress));
875 compression = (double)100 - 100.0 * ((double)jcr->SDJobBytes / (double)jcr->ReadBytes);
876 if (compression < 0.5) {
877 bstrncpy(data_compress, "None", sizeof(data_compress));
879 if (jcr->SDJobBytes > 0) {
880 ratio = (double)jcr->ReadBytes / (double)jcr->SDJobBytes;
884 bsnprintf(data_compress, sizeof(data_compress), "%.1f%% %.1f:1",
888 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
889 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
892 Mmsg(base_info, _(" Base files/Used files: %lld/%lld (%.2f%%)\n"),
894 jcr->nb_base_files_used,
895 jcr->nb_base_files_used*100.0/jcr->nb_base_files);
897 /* Edit string for last volume size */
898 if (mr.VolABytes != 0) {
899 Mmsg(vol_info, _("meta: %s (%sB) aligned: %s (%sB)"),
900 edit_uint64_with_commas(mr.VolBytes, ec7),
901 edit_uint64_with_suffix(mr.VolBytes, ec8),
902 edit_uint64_with_commas(mr.VolABytes, ec9),
903 edit_uint64_with_suffix(mr.VolABytes, ec10));
905 Mmsg(vol_info, _("%s (%sB)"),
906 edit_uint64_with_commas(mr.VolBytes, ec7),
907 edit_uint64_with_suffix(mr.VolBytes, ec8));
910 // bmicrosleep(15, 0); /* for debugging SIGHUP */
912 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
913 " Build OS: %s %s %s\n"
916 " Backup Level: %s%s\n"
917 " Client: \"%s\" %s\n"
918 " FileSet: \"%s\" %s\n"
919 " Pool: \"%s\" (From %s)\n"
920 " Catalog: \"%s\" (From %s)\n"
921 " Storage: \"%s\" (From %s)\n"
922 " Scheduled time: %s\n"
925 " Elapsed time: %s\n"
927 " FD Files Written: %s\n"
928 " SD Files Written: %s\n"
929 " FD Bytes Written: %s (%sB)\n"
930 " SD Bytes Written: %s (%sB)\n"
932 " Software Compression: %s\n"
933 "%s" /* Basefile info */
934 " Snapshot/VSS: %s\n"
937 " Volume name(s): %s\n"
938 " Volume Session Id: %d\n"
939 " Volume Session Time: %d\n"
940 " Last Volume Bytes: %s\n"
941 " Non-fatal FD errors: %d\n"
943 " FD termination status: %s\n"
944 " SD termination status: %s\n"
945 " Termination: %s\n\n"),
946 BACULA, my_name, VERSION, LSMDATE,
947 HOST_OS, DISTNAME, DISTVER,
950 level_to_str(jcr->getJobLevel()), jcr->since,
951 jcr->client->name(), cr.Uname,
952 jcr->fileset->name(), jcr->FSCreateTime,
953 jcr->pool->name(), jcr->pool_source,
954 jcr->catalog->name(), jcr->catalog_source,
955 jcr->wstore->name(), jcr->wstore_source,
959 edit_utime(RunTime, elapsed, sizeof(elapsed)),
961 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
962 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
963 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
964 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
965 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
966 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
970 jcr->Snapshot?_("yes"):_("no"),
971 jcr->Encrypt?_("yes"):_("no"),
972 jcr->accurate?_("yes"):_("no"),
983 Dmsg0(100, "Leave backup_cleanup()\n");
986 void update_bootstrap_file(JCR *jcr)
988 /* Now update the bootstrap file if any */
989 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
990 jcr->job->WriteBootstrap) {
994 POOLMEM *fname = get_pool_memory(PM_FNAME);
995 fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
997 VOL_PARAMS *VolParams = NULL;
999 char edt[50], ed1[50], ed2[50];
1001 if (*fname == '|') {
1003 bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
1004 fd = bpipe ? bpipe->wfd : NULL;
1006 /* ***FIXME*** handle BASE */
1007 fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
1010 VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
1012 if (VolCount == 0) {
1013 Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
1014 "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
1015 if (jcr->SDJobFiles != 0) {
1016 jcr->setJobStatus(JS_ErrorTerminated);
1020 /* Start output with when and who wrote it */
1021 bstrftimes(edt, sizeof(edt), time(NULL));
1022 fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
1023 level_to_str(jcr->getJobLevel()), jcr->since);
1024 for (int i=0; i < VolCount; i++) {
1025 /* Write the record */
1026 fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
1027 fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
1028 if (VolParams[i].Slot > 0) {
1029 fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
1031 fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
1032 fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
1033 fprintf(fd, "VolAddr=%s-%s\n",
1034 edit_uint64(VolParams[i].StartAddr, ed1),
1035 edit_uint64(VolParams[i].EndAddr, ed2));
1036 fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
1037 VolParams[i].LastIndex);
1049 Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
1050 "%s: ERR=%s\n"), fname, be.bstrerror());
1051 jcr->setJobStatus(JS_ErrorTerminated);
1053 free_pool_memory(fname);