/*
Bacula® - The Network Backup Solution
- Copyright (C) 2008-2008 Free Software Foundation Europe e.V.
+ Copyright (C) 2008-2012 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
+ modify it under the terms of version three of the GNU Affero General Public
License as published by the Free Software Foundation and included
in the file LICENSE.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
* to do the backup.
* When the File daemon finishes the job, update the DB.
*
- * Version $Id: $
*/
#include "bacula.h"
static const int dbglevel = 10;
-static char OKbootstrap[] = "3000 OK bootstrap\n";
-
-static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids);
+static bool create_bootstrap_file(JCR *jcr, char *jobids);
void vbackup_cleanup(JCR *jcr, int TermCode);
/*
*/
bool do_vbackup_init(JCR *jcr)
{
- /* ***FIXME*** remove when implemented in job.c */
- if (!jcr->rpool_source) {
- jcr->rpool_source = get_pool_memory(PM_MESSAGE);
- pm_strcpy(jcr->rpool_source, _("unknown source"));
- }
-
+
if (!get_or_create_fileset_record(jcr)) {
Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId);
return false;
return false;
}
+ jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
+ if (jcr->jr.PoolId == 0) {
+ Dmsg1(dbglevel, "JobId=%d no PoolId\n", (int)jcr->JobId);
+ Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
+ return false;
+ }
/*
* Note, at this point, pool is the pool for this job. We
* transfer it to rpool (read pool), and a bit later,
jcr->rpool = jcr->pool; /* save read pool */
pm_strcpy(jcr->rpool_source, jcr->pool_source);
+ /* If pool storage specified, use it for restore */
+ copy_rstorage(jcr, jcr->pool->storage, _("Pool resource"));
Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source);
- POOLMEM *jobids = get_pool_memory(PM_FNAME);
- db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids);
- Dmsg1(000, "Accurate jobids=%s\n", jobids);
- if (*jobids == 0) {
- free_pool_memory(jobids);
- Jmsg(jcr, M_FATAL, 0, _("Cannot find previous JobIds.\n"));
- return false;
+ jcr->start_time = time(NULL);
+ jcr->jr.StartTime = jcr->start_time;
+ jcr->jr.JobLevel = L_FULL; /* we want this to appear as a Full backup */
+ if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
+ Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
}
- if (!create_bootstrap_file(jcr, jobids)) {
- Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
- free_pool_memory(jobids);
- return false;
- }
- free_pool_memory(jobids);
/*
* If the original backup pool has a NextPool, make sure a
return false;
}
}
- /* ***FIXME*** this is probably not needed */
if (!set_migration_wstorage(jcr, jcr->pool)) {
return false;
}
+ jcr->pool = jcr->pool->NextPool;
pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource"));
Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->pool->name(), jcr->rpool->name());
- create_clones(jcr);
+// create_clones(jcr);
return true;
}
/*
- * Do a backup of the specified FileSet
+ * Do a virtual backup, which consolidates all previous backups into
+ * a sort of synthetic Full.
*
* Returns: false on failure
* true on success
{
char ed1[100];
BSOCK *sd;
+ char *p;
+ db_list_ctx jobids;
+
+ Dmsg2(100, "rstorage=%p wstorage=%p\n", jcr->rstorage, jcr->wstorage);
+ Dmsg2(100, "Read store=%s, write store=%s\n",
+ ((STORE *)jcr->rstorage->first())->name(),
+ ((STORE *)jcr->wstorage->first())->name());
+
+ jcr->wasVirtualFull = true; /* remember where we came from */
/* Print Job Start message */
- Jmsg(jcr, M_INFO, 0, _("Start Vbackup JobId %s, Job=%s\n"),
+ Jmsg(jcr, M_INFO, 0, _("Start Virtual Backup JobId %s, Job=%s\n"),
edit_uint64(jcr->JobId, ed1), jcr->Job);
+ if (!jcr->accurate) {
+ Jmsg(jcr, M_WARNING, 0,
+_("This Job is not an Accurate backup so is not equivalent to a Full backup.\n"));
+ }
+
+ jcr->jr.JobLevel = L_VIRTUAL_FULL;
+ db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
+ Dmsg1(10, "Accurate jobids=%s\n", jobids.list);
+ if (jobids.count == 0) {
+ Jmsg(jcr, M_FATAL, 0, _("No previous Jobs found.\n"));
+ return false;
+ }
+
+ jcr->jr.JobLevel = L_FULL;
+
+ /*
+ * Now we find the last job that ran and store it's info in
+ * the previous_jr record. We will set our times to the
+ * values from that job so that anything changed after that
+ * time will be picked up on the next backup.
+ */
+ p = strrchr(jobids.list, ','); /* find last jobid */
+ if (p != NULL) {
+ p++;
+ } else {
+ p = jobids.list;
+ }
+ memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr));
+ jcr->previous_jr.JobId = str_to_int64(p);
+ Dmsg1(10, "Previous JobId=%s\n", p);
+ if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
+ Jmsg(jcr, M_FATAL, 0, _("Error getting Job record for previous Job: ERR=%s"),
+ db_strerror(jcr->db));
+ return false;
+ }
+
+ if (!create_bootstrap_file(jcr, jobids.list)) {
+ Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
+ return false;
+ }
/*
* Open a message channel connection with the Storage
*
*/
Dmsg0(110, "Open connection with storage daemon\n");
- set_jcr_job_status(jcr, JS_WaitSD);
+ jcr->setJobStatus(JS_WaitSD);
/*
* Start conversation with Storage daemon
*/
return false;
}
sd = jcr->store_bsock;
+
/*
* Now start a job with the Storage daemon
*/
- Dmsg2(000, "Read store=%s, write store=%s\n",
- ((STORE *)jcr->rstorage->first())->name(),
- ((STORE *)jcr->wstorage->first())->name());
- if (((STORE *)jcr->rstorage->first())->name() == ((STORE *)jcr->wstorage->first())->name()) {
- Jmsg(jcr, M_FATAL, 0, _("Read storage \"%s\" same as write storage.\n"),
- ((STORE *)jcr->rstorage->first())->name());
- return false;
- }
- if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
- return false;
- }
- Dmsg0(150, "Storage daemon connection OK\n");
-
- if (!send_bootstrap_file(jcr, sd) ||
- !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
+ if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage, /*send_bsr*/true)) {
return false;
}
+ Dmsg0(100, "Storage daemon connection OK\n");
/*
* We re-update the job start record so that the start
jcr->start_time = time(NULL);
jcr->jr.StartTime = jcr->start_time;
jcr->jr.JobTDate = jcr->start_time;
- set_jcr_job_status(jcr, JS_Running);
+ jcr->setJobStatus(JS_Running);
- /* Update job start record for this migration control job */
+ /* Update job start record */
if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
return false;
return false;
}
-
- set_jcr_job_status(jcr, JS_Running);
+ jcr->setJobStatus(JS_Running);
/* Pickup Job termination data */
- /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
+ /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
wait_for_storage_daemon_termination(jcr);
- set_jcr_job_status(jcr, jcr->SDJobStatus);
+ jcr->setJobStatus(jcr->SDJobStatus);
db_write_batch_file_records(jcr); /* used by bulk batch file insert */
if (jcr->JobStatus != JS_Terminated) {
return false;
void vbackup_cleanup(JCR *jcr, int TermCode)
{
char sdt[50], edt[50], schedt[50];
- char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
- char ec6[30], ec7[30], ec8[30], elapsed[50];
- char term_code[100], fd_term_msg[100], sd_term_msg[100];
+ char ec1[30], ec3[30], ec4[30], compress[50];
+ char ec7[30], ec8[30], elapsed[50];
+ char term_code[100], sd_term_msg[100];
const char *term_msg;
int msg_type = M_INFO;
MEDIA_DBR mr;
CLIENT_DBR cr;
double kbps, compression;
utime_t RunTime;
+ POOL_MEM query(PM_MESSAGE);
Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
- memset(&mr, 0, sizeof(mr));
memset(&cr, 0, sizeof(cr));
+ jcr->setJobLevel(L_FULL); /* we want this to appear as a Full backup */
+ jcr->jr.JobLevel = L_FULL; /* we want this to appear as a Full backup */
+ jcr->JobFiles = jcr->SDJobFiles;
+ jcr->JobBytes = jcr->SDJobBytes;
update_job_end(jcr, TermCode);
+ /* Update final items to set them to the previous job's values */
+ Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
+ "JobTDate=%s WHERE JobId=%s",
+ jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
+ edit_uint64(jcr->previous_jr.JobTDate, ec1),
+ edit_uint64(jcr->JobId, ec3));
+ db_sql_query(jcr->db, query.c_str(), NULL, NULL);
+
+ /* Get the fully updated job record */
if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
db_strerror(jcr->db));
- set_jcr_job_status(jcr, JS_ErrorTerminated);
+ jcr->setJobStatus(JS_ErrorTerminated);
}
bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
if (!db_get_media_record(jcr, jcr->db, &mr)) {
Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
mr.VolumeName, db_strerror(jcr->db));
- set_jcr_job_status(jcr, JS_ErrorTerminated);
+ jcr->setJobStatus(JS_ErrorTerminated);
}
update_bootstrap_file(jcr);
switch (jcr->JobStatus) {
case JS_Terminated:
- if (jcr->Errors || jcr->SDErrors) {
+ if (jcr->JobErrors || jcr->SDErrors) {
term_msg = _("Backup OK -- with warnings");
} else {
term_msg = _("Backup OK");
bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
}
}
- jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
-// bmicrosleep(15, 0); /* for debugging SIGHUP */
-
- Jmsg(jcr, msg_type, 0, _("Bacula %s %s (%s): %s\n"
+ Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
" Build OS: %s %s %s\n"
" JobId: %d\n"
" Job: %s\n"
-" Backup Level: %s%s\n"
+" Backup Level: Virtual Full\n"
" Client: \"%s\" %s\n"
" FileSet: \"%s\" %s\n"
" Pool: \"%s\" (From %s)\n"
" End time: %s\n"
" Elapsed time: %s\n"
" Priority: %d\n"
-" FD Files Written: %s\n"
" SD Files Written: %s\n"
-" FD Bytes Written: %s (%sB)\n"
" SD Bytes Written: %s (%sB)\n"
" Rate: %.1f KB/s\n"
-" Software Compression: %s\n"
-" VSS: %s\n"
-" Encryption: %s\n"
-" Accurate: %s\n"
" Volume name(s): %s\n"
" Volume Session Id: %d\n"
" Volume Session Time: %d\n"
" Last Volume Bytes: %s (%sB)\n"
-" Non-fatal FD errors: %d\n"
" SD Errors: %d\n"
-" FD termination status: %s\n"
" SD termination status: %s\n"
" Termination: %s\n\n"),
- my_name, VERSION, LSMDATE, edt,
+ BACULA, my_name, VERSION, LSMDATE,
HOST_OS, DISTNAME, DISTVER,
jcr->jr.JobId,
jcr->jr.Job,
- level_to_str(jcr->JobLevel), jcr->since,
jcr->client->name(), cr.Uname,
jcr->fileset->name(), jcr->FSCreateTime,
jcr->pool->name(), jcr->pool_source,
edit_utime(RunTime, elapsed, sizeof(elapsed)),
jcr->JobPriority,
edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
- edit_uint64_with_commas(jcr->SDJobFiles, ec2),
edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
- edit_uint64_with_commas(jcr->SDJobBytes, ec5),
- edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
kbps,
- compress,
- jcr->VSS?_("yes"):_("no"),
- jcr->Encrypt?_("yes"):_("no"),
- jcr->accurate?_("yes"):_("no"),
jcr->VolumeName,
jcr->VolSessionId,
jcr->VolSessionTime,
edit_uint64_with_commas(mr.VolBytes, ec7),
edit_uint64_with_suffix(mr.VolBytes, ec8),
- jcr->Errors,
jcr->SDErrors,
- fd_term_msg,
sd_term_msg,
term_msg);
}
-static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids)
+static bool create_bootstrap_file(JCR *jcr, char *jobids)
{
RESTORE_CTX rx;
UAContext *ua;
#define new_get_file_list
#ifdef new_get_file_list
- if (!db_get_file_list(jcr, ua->db, jobids, insert_bootstrap_handler, (void *)rx.bsr)) {
- Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(ua->db));
+ if (!db_open_batch_connexion(jcr, jcr->db)) {
+ Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
+ return false;
+ }
+
+ if (!db_get_file_list(jcr, jcr->db_batch, jobids, false /* don't use md5 */,
+ true /* use delta */,
+ insert_bootstrap_handler, (void *)rx.bsr))
+ {
+ Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db_batch));
}
#else
char *p;
* Find files for this JobId and insert them in the tree
*/
Mmsg(rx.query, uar_sel_files, edit_int64(JobId, ed1));
- Dmsg1(000, "uar_sel_files=%s\n", rx.query);
+ Dmsg1(100, "uar_sel_files=%s\n", rx.query);
if (!db_sql_query(ua->db, rx.query, insert_bootstrap_handler, (void *)rx.bsr)) {
Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(ua->db));
}
#endif
complete_bsr(ua, rx.bsr);
- Dmsg0(000, "Print bsr\n");
- print_bsr(ua, rx.bsr);
-
jcr->ExpectedFiles = write_bsr_file(ua, rx);
- Dmsg1(000, "Found %d files to consolidate.\n", jcr->ExpectedFiles);
+ if (debug_level >= 10) {
+ Dmsg1(000, "Found %d files to consolidate.\n", jcr->ExpectedFiles);
+ }
if (jcr->ExpectedFiles == 0) {
free_ua_context(ua);
free_bsr(rx.bsr);