2 Bacula® - The Network Backup Solution
4 Copyright (C) 2008-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- vbackup.c -- responsible for doing virtual
31 * backup jobs or in other words, consolidation or synthetic
34 * Kern Sibbald, July MMVIII
36 * Basic tasks done here:
37 * Open DB and create records for this job.
38 * Figure out what Jobs to copy.
39 * Open Message Channel with Storage daemon to tell him a job will be starting.
40 * Open connection with File daemon and pass him commands
42 * When the File daemon finishes the job, update the DB.
51 static const int dbglevel = 10;
53 static char OKbootstrap[] = "3000 OK bootstrap\n";
55 static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids);
56 void vbackup_cleanup(JCR *jcr, int TermCode);
59 * Called here before the job is run to do the job
62 bool do_vbackup_init(JCR *jcr)
64 /* ***FIXME*** remove when implemented in job.c */
65 if (!jcr->rpool_source) {
66 jcr->rpool_source = get_pool_memory(PM_MESSAGE);
67 pm_strcpy(jcr->rpool_source, _("unknown source"));
70 if (!get_or_create_fileset_record(jcr)) {
71 Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId);
75 apply_pool_overrides(jcr);
77 if (!allow_duplicate_job(jcr)) {
82 * Note, at this point, pool is the pool for this job. We
83 * transfer it to rpool (read pool), and a bit later,
84 * pool will be changed to point to the write pool,
85 * which comes from pool->NextPool.
87 jcr->rpool = jcr->pool; /* save read pool */
88 pm_strcpy(jcr->rpool_source, jcr->pool_source);
91 Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source);
93 POOLMEM *jobids = get_pool_memory(PM_FNAME);
94 db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids);
95 Dmsg1(000, "Accurate jobids=%s\n", jobids);
97 free_pool_memory(jobids);
98 Jmsg(jcr, M_FATAL, 0, _("Cannot find previous JobIds.\n"));
102 if (!create_bootstrap_file(jcr, jobids)) {
103 Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
104 free_pool_memory(jobids);
107 free_pool_memory(jobids);
110 * If the original backup pool has a NextPool, make sure a
111 * record exists in the database. Note, in this case, we
112 * will be backing up from pool to pool->NextPool.
114 if (jcr->pool->NextPool) {
115 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->NextPool->name());
116 if (jcr->jr.PoolId == 0) {
120 /* ***FIXME*** this is probably not needed */
121 if (!set_migration_wstorage(jcr, jcr->pool)) {
124 pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource"));
126 Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->pool->name(), jcr->rpool->name());
134 * Do a backup of the specified FileSet
136 * Returns: false on failure
139 bool do_vbackup(JCR *jcr)
143 JCR *mig_jcr = jcr->mig_jcr; /* newly backed up job */
146 * If mig_jcr is NULL, there is nothing to do for this job,
147 * so set a normal status, cleanup and return OK.
150 set_jcr_job_status(jcr, JS_Terminated);
151 vbackup_cleanup(jcr, jcr->JobStatus);
155 /* Print Job Start message */
156 Jmsg(jcr, M_INFO, 0, _("Start Vbackup JobId %s, Job=%s\n"),
157 edit_uint64(jcr->JobId, ed1), jcr->Job);
162 * Open a message channel connection with the Storage
163 * daemon. This is to let him know that our client
164 * will be contacting him for a backup session.
167 Dmsg0(110, "Open connection with storage daemon\n");
168 set_jcr_job_status(jcr, JS_WaitSD);
169 set_jcr_job_status(mig_jcr, JS_WaitSD);
171 * Start conversation with Storage daemon
173 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
176 sd = jcr->store_bsock;
178 * Now start a job with the Storage daemon
180 Dmsg2(dbglevel, "Read store=%s, write store=%s\n",
181 ((STORE *)jcr->rstorage->first())->name(),
182 ((STORE *)jcr->wstorage->first())->name());
183 if (((STORE *)jcr->rstorage->first())->name() == ((STORE *)jcr->wstorage->first())->name()) {
184 Jmsg(jcr, M_FATAL, 0, _("Read storage \"%s\" same as write storage.\n"),
185 ((STORE *)jcr->rstorage->first())->name());
188 if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
191 Dmsg0(150, "Storage daemon connection OK\n");
193 if (!send_bootstrap_file(jcr, sd) ||
194 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
199 * We re-update the job start record so that the start
200 * time is set after the run before job. This avoids
201 * that any files created by the run before job will
202 * be saved twice. They will be backed up in the current
203 * job, but not in the next one unless they are changed.
204 * Without this, they will be backed up in this job and
205 * in the next job run because in that case, their date
206 * is after the start of this run.
208 jcr->start_time = time(NULL);
209 jcr->jr.StartTime = jcr->start_time;
210 jcr->jr.JobTDate = jcr->start_time;
211 set_jcr_job_status(jcr, JS_Running);
213 /* Update job start record for this migration control job */
214 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
215 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
220 mig_jcr->start_time = time(NULL);
221 mig_jcr->jr.StartTime = mig_jcr->start_time;
222 mig_jcr->jr.JobTDate = mig_jcr->start_time;
223 set_jcr_job_status(mig_jcr, JS_Running);
225 /* Update job start record for the real migration backup job */
226 if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
227 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(mig_jcr->db));
231 Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
232 mig_jcr->jr.Name, (int)mig_jcr->jr.JobId,
233 mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
237 * Start the job prior to starting the message thread below
238 * to avoid two threads from using the BSOCK structure at
241 if (!sd->fsend("run")) {
246 * Now start a Storage daemon message thread
248 if (!start_storage_daemon_message_thread(jcr)) {
253 set_jcr_job_status(jcr, JS_Running);
254 set_jcr_job_status(mig_jcr, JS_Running);
256 /* Pickup Job termination data */
257 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
258 wait_for_storage_daemon_termination(jcr);
259 set_jcr_job_status(jcr, jcr->SDJobStatus);
260 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
261 if (jcr->JobStatus != JS_Terminated) {
265 vbackup_cleanup(jcr, jcr->JobStatus);
268 UAContext *ua = new_ua_context(jcr);
269 edit_uint64(jcr->previous_jr.JobId, jobid);
270 /* Purge all old file records, but leave Job record */
271 purge_files_from_jobs(ua, jobid);
279 * Release resources allocated during backup.
281 void vbackup_cleanup(JCR *jcr, int TermCode)
283 char sdt[50], edt[50], schedt[50];
284 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
285 char ec6[30], ec7[30], ec8[30], elapsed[50];
286 char term_code[100], fd_term_msg[100], sd_term_msg[100];
287 const char *term_msg;
288 int msg_type = M_INFO;
291 double kbps, compression;
294 Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
295 memset(&mr, 0, sizeof(mr));
296 memset(&cr, 0, sizeof(cr));
298 update_job_end(jcr, TermCode);
300 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
301 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
302 db_strerror(jcr->db));
303 set_jcr_job_status(jcr, JS_ErrorTerminated);
306 bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
307 if (!db_get_client_record(jcr, jcr->db, &cr)) {
308 Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
309 db_strerror(jcr->db));
312 bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
313 if (!db_get_media_record(jcr, jcr->db, &mr)) {
314 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
315 mr.VolumeName, db_strerror(jcr->db));
316 set_jcr_job_status(jcr, JS_ErrorTerminated);
319 update_bootstrap_file(jcr);
321 switch (jcr->JobStatus) {
323 if (jcr->Errors || jcr->SDErrors) {
324 term_msg = _("Backup OK -- with warnings");
326 term_msg = _("Backup OK");
330 case JS_ErrorTerminated:
331 term_msg = _("*** Backup Error ***");
332 msg_type = M_ERROR; /* Generate error message */
333 if (jcr->store_bsock) {
334 jcr->store_bsock->signal(BNET_TERMINATE);
335 if (jcr->SD_msg_chan) {
336 pthread_cancel(jcr->SD_msg_chan);
341 term_msg = _("Backup Canceled");
342 if (jcr->store_bsock) {
343 jcr->store_bsock->signal(BNET_TERMINATE);
344 if (jcr->SD_msg_chan) {
345 pthread_cancel(jcr->SD_msg_chan);
350 term_msg = term_code;
351 sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
354 bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
355 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
356 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
357 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
361 kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
363 if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
365 * Note, if the job has erred, most likely it did not write any
366 * tape, so suppress this "error" message since in that case
367 * it is normal. Or look at it the other way, only for a
368 * normal exit should we complain about this error.
370 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
371 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
373 jcr->VolumeName[0] = 0; /* none */
376 if (jcr->ReadBytes == 0) {
377 bstrncpy(compress, "None", sizeof(compress));
379 compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
380 if (compression < 0.5) {
381 bstrncpy(compress, "None", sizeof(compress));
383 bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
386 jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
387 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
389 // bmicrosleep(15, 0); /* for debugging SIGHUP */
391 Jmsg(jcr, msg_type, 0, _("Bacula %s %s (%s): %s\n"
392 " Build OS: %s %s %s\n"
395 " Backup Level: %s%s\n"
396 " Client: \"%s\" %s\n"
397 " FileSet: \"%s\" %s\n"
398 " Pool: \"%s\" (From %s)\n"
399 " Catalog: \"%s\" (From %s)\n"
400 " Storage: \"%s\" (From %s)\n"
401 " Scheduled time: %s\n"
404 " Elapsed time: %s\n"
406 " FD Files Written: %s\n"
407 " SD Files Written: %s\n"
408 " FD Bytes Written: %s (%sB)\n"
409 " SD Bytes Written: %s (%sB)\n"
411 " Software Compression: %s\n"
415 " Volume name(s): %s\n"
416 " Volume Session Id: %d\n"
417 " Volume Session Time: %d\n"
418 " Last Volume Bytes: %s (%sB)\n"
419 " Non-fatal FD errors: %d\n"
421 " FD termination status: %s\n"
422 " SD termination status: %s\n"
423 " Termination: %s\n\n"),
424 my_name, VERSION, LSMDATE, edt,
425 HOST_OS, DISTNAME, DISTVER,
428 level_to_str(jcr->JobLevel), jcr->since,
429 jcr->client->name(), cr.Uname,
430 jcr->fileset->name(), jcr->FSCreateTime,
431 jcr->pool->name(), jcr->pool_source,
432 jcr->catalog->name(), jcr->catalog_source,
433 jcr->wstore->name(), jcr->wstore_source,
437 edit_utime(RunTime, elapsed, sizeof(elapsed)),
439 edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
440 edit_uint64_with_commas(jcr->SDJobFiles, ec2),
441 edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
442 edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
443 edit_uint64_with_commas(jcr->SDJobBytes, ec5),
444 edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
447 jcr->VSS?_("yes"):_("no"),
448 jcr->Encrypt?_("yes"):_("no"),
449 jcr->accurate?_("yes"):_("no"),
453 edit_uint64_with_commas(mr.VolBytes, ec7),
454 edit_uint64_with_suffix(mr.VolBytes, ec8),
461 Dmsg0(100, "Leave vbackup_cleanup()\n");
465 * This callback routine is responsible for inserting the
466 * items it gets into the bootstrap structure. For each JobId selected
467 * this routine is called once for each file. We do not allow
468 * duplicate filenames, but instead keep the info from the most
469 * recent file entered (i.e. the JobIds are assumed to be sorted)
471 * See uar_sel_files in sql_cmds.c for query that calls us.
472 * row[0]=Path, row[1]=Filename, row[2]=FileIndex
473 * row[3]=JobId row[4]=LStat
475 int insert_bootstrap_handler(void *ctx, int num_fields, char **row)
479 RBSR *bsr = (RBSR *)ctx;
481 JobId = str_to_int64(row[3]);
482 FileIndex = str_to_int64(row[2]);
483 add_findex(bsr, JobId, FileIndex);
488 static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids)
493 memset(&rx, 0, sizeof(rx));
495 ua = new_ua_context(jcr);
498 #define new_get_file_list
499 #ifdef new_get_file_list
500 if (!db_get_file_list(jcr, ua->db, jobids, insert_bootstrap_handler, (void *)rx.bsr)) {
501 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(ua->db));
505 JobId_t JobId, last_JobId = 0;
506 rx.query = get_pool_memory(PM_MESSAGE);
507 for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
510 if (JobId == last_JobId) {
511 continue; /* eliminate duplicate JobIds */
515 * Find files for this JobId and insert them in the tree
517 Mmsg(rx.query, uar_sel_files, edit_int64(JobId, ed1));
518 Dmsg1(000, "uar_sel_files=%s\n", rx.query);
519 if (!db_sql_query(ua->db, rx.query, insert_bootstrap_handler, (void *)rx.bsr)) {
520 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(ua->db));
522 free_pool_memory(rx.query);
527 complete_bsr(ua, rx.bsr);
528 Dmsg0(000, "Print bsr\n");
529 print_bsr(ua, rx.bsr);
531 jcr->ExpectedFiles = write_bsr_file(ua, rx);
532 Dmsg1(000, "Found %d files to consolidate.\n", jcr->ExpectedFiles);
533 if (jcr->ExpectedFiles == 0) {