]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/vbackup.c
Tweaks + svn ignores
[bacula/bacula] / bacula / src / dird / vbackup.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2008-2008 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  *
30  *   Bacula Director -- vbackup.c -- responsible for doing virtual
31  *     backup jobs or in other words, consolidation or synthetic
32  *     backups.
33  *
34  *     Kern Sibbald, July MMVIII
35  *
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
41  *       to do the backup.
42  *     When the File daemon finishes the job, update the DB.
43  *
44  *   Version $Id: $
45  */
46
47 #include "bacula.h"
48 #include "dird.h"
49 #include "ua.h"
50
51 static const int dbglevel = 10;
52
53 static char OKbootstrap[] = "3000 OK bootstrap\n";
54
55 static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids);
56 void vbackup_cleanup(JCR *jcr, int TermCode);
57
58 /* 
59  * Called here before the job is run to do the job
60  *   specific setup.
61  */
62 bool do_vbackup_init(JCR *jcr)
63 {
64    char *p;
65
66    if (!get_or_create_fileset_record(jcr)) {
67       Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId);
68       return false;
69    }
70
71    apply_pool_overrides(jcr);
72
73    if (!allow_duplicate_job(jcr)) {
74       return false;
75    }
76
77    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
78    if (jcr->jr.PoolId == 0) {
79       Dmsg1(dbglevel, "JobId=%d no PoolId\n", (int)jcr->JobId);
80       Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
81       return false;
82    }
83    /*
84     * Note, at this point, pool is the pool for this job.  We
85     *  transfer it to rpool (read pool), and a bit later,
86     *  pool will be changed to point to the write pool, 
87     *  which comes from pool->NextPool.
88     */
89    jcr->rpool = jcr->pool;            /* save read pool */
90    pm_strcpy(jcr->rpool_source, jcr->pool_source);
91
92    /* If pool storage specified, use it for restore */
93    copy_rstorage(jcr, jcr->pool->storage, _("Pool resource"));
94
95    Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source);
96
97    jcr->start_time = time(NULL);
98    jcr->jr.StartTime = jcr->start_time;
99    jcr->jr.JobLevel = L_FULL;      /* we want this to appear as a Full backup */
100    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
101       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
102    }
103
104    POOLMEM *jobids = get_pool_memory(PM_FNAME);
105    jcr->jr.JobLevel = L_VIRTUAL_FULL;
106    db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids);
107    jcr->jr.JobLevel = L_FULL;
108    Dmsg1(10, "Accurate jobids=%s\n", jobids);
109    if (*jobids == 0) {
110       free_pool_memory(jobids);
111       Jmsg(jcr, M_FATAL, 0, _("Cannot find previous JobIds.\n"));
112       return false;
113    }
114
115    /*
116     * Now we find the last job that ran and store it's info in
117     *  the previous_jr record.  We will set our times to the
118     *  values from that job so that anything changed after that
119     *  time will be picked up on the next backup.
120     */
121    p = strrchr(jobids, ',');              /* find last jobid */
122    if (p != NULL) {
123       p++;
124    } else {
125       p = jobids;
126    }
127    memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr));
128    jcr->previous_jr.JobId = str_to_int64(p);
129    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
130       Jmsg(jcr, M_FATAL, 0, _("Error getting Job record for previous Job: ERR=%s"),
131                db_strerror(jcr->db));
132       return false;
133    }
134
135    if (!create_bootstrap_file(jcr, jobids)) {
136       Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
137       free_pool_memory(jobids);
138       return false;
139    }
140    free_pool_memory(jobids);
141
142    /*
143     * If the original backup pool has a NextPool, make sure a 
144     *  record exists in the database. Note, in this case, we
145     *  will be backing up from pool to pool->NextPool.
146     */
147    if (jcr->pool->NextPool) {
148       jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->NextPool->name());
149       if (jcr->jr.PoolId == 0) {
150          return false;
151       }
152    }
153    if (!set_migration_wstorage(jcr, jcr->pool)) {
154       return false;
155    }
156    jcr->pool = jcr->pool->NextPool;
157    pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource"));
158
159    Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->pool->name(), jcr->rpool->name());
160
161 // create_clones(jcr);
162
163    return true;
164 }
165
166 /*
167  * Do a virtual backup, which consolidates all previous backups into
168  *  a sort of synthetic Full.
169  *
170  *  Returns:  false on failure
171  *            true  on success
172  */
173 bool do_vbackup(JCR *jcr)
174 {
175    char ed1[100];
176    BSOCK *sd;
177
178    Dmsg2(100, "rstorage=%p wstorage=%p\n", jcr->rstorage, jcr->wstorage);
179    Dmsg2(100, "Read store=%s, write store=%s\n", 
180       ((STORE *)jcr->rstorage->first())->name(),
181       ((STORE *)jcr->wstorage->first())->name());
182    /* ***FIXME***  we really should simply verify that the pools are different */
183 #ifdef xxx
184    if (((STORE *)jcr->rstorage->first())->name() == ((STORE *)jcr->wstorage->first())->name()) {
185       Jmsg(jcr, M_FATAL, 0, _("Read storage \"%s\" same as write storage.\n"),
186            ((STORE *)jcr->rstorage->first())->name());
187       return false;
188    }
189 #endif
190
191    /* Print Job Start message */
192    Jmsg(jcr, M_INFO, 0, _("Start Virtual Backup JobId %s, Job=%s\n"),
193         edit_uint64(jcr->JobId, ed1), jcr->Job);
194    if (!jcr->accurate) {
195       Jmsg(jcr, M_WARNING, 0, 
196 _("This Job is not an Accurate backup so is not equivalent to a Full backup.\n"));
197    }
198
199    /*
200     * Open a message channel connection with the Storage
201     * daemon. This is to let him know that our client
202     * will be contacting him for a backup  session.
203     *
204     */
205    Dmsg0(110, "Open connection with storage daemon\n");
206    set_jcr_job_status(jcr, JS_WaitSD);
207    /*
208     * Start conversation with Storage daemon
209     */
210    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
211       return false;
212    }
213    sd = jcr->store_bsock;
214
215    /*
216     * Now start a job with the Storage daemon
217     */
218    if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
219       return false;
220    }
221    Dmsg0(100, "Storage daemon connection OK\n");
222
223    if (!send_bootstrap_file(jcr, sd) ||
224        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
225       return false;
226    }
227
228    /*    
229     * We re-update the job start record so that the start
230     *  time is set after the run before job.  This avoids 
231     *  that any files created by the run before job will
232     *  be saved twice.  They will be backed up in the current
233     *  job, but not in the next one unless they are changed.
234     *  Without this, they will be backed up in this job and
235     *  in the next job run because in that case, their date 
236     *   is after the start of this run.
237     */
238    jcr->start_time = time(NULL);
239    jcr->jr.StartTime = jcr->start_time;
240    jcr->jr.JobTDate = jcr->start_time;
241    set_jcr_job_status(jcr, JS_Running);
242
243    /* Update job start record */
244    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
245       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
246       return false;
247    }
248
249    /*
250     * Start the job prior to starting the message thread below
251     * to avoid two threads from using the BSOCK structure at
252     * the same time.
253     */
254    if (!sd->fsend("run")) {
255       return false;
256    }
257
258    /*
259     * Now start a Storage daemon message thread
260     */
261    if (!start_storage_daemon_message_thread(jcr)) {
262       return false;
263    }
264
265    set_jcr_job_status(jcr, JS_Running);
266
267    /* Pickup Job termination data */
268    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
269    wait_for_storage_daemon_termination(jcr);
270    set_jcr_job_status(jcr, jcr->SDJobStatus);
271    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
272    if (jcr->JobStatus != JS_Terminated) {
273       return false;
274    }
275
276    vbackup_cleanup(jcr, jcr->JobStatus);
277    return true;
278 }
279
280
281 /*
282  * Release resources allocated during backup.
283  */
284 void vbackup_cleanup(JCR *jcr, int TermCode)
285 {
286    char sdt[50], edt[50], schedt[50];
287    char ec1[30], ec3[30], ec4[30], compress[50];
288    char ec7[30], ec8[30], elapsed[50];
289    char term_code[100], sd_term_msg[100];
290    const char *term_msg;
291    int msg_type = M_INFO;
292    MEDIA_DBR mr;
293    CLIENT_DBR cr;
294    double kbps, compression;
295    utime_t RunTime;
296    POOL_MEM query(PM_MESSAGE);
297
298    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
299    memset(&mr, 0, sizeof(mr));
300    memset(&cr, 0, sizeof(cr));
301
302    jcr->set_JobLevel(L_FULL);         /* we want this to appear as a Full backup */
303    jcr->jr.JobLevel = L_FULL;         /* we want this to appear as a Full backup */
304    jcr->JobFiles = jcr->SDJobFiles;
305    jcr->JobBytes = jcr->SDJobBytes;
306    update_job_end(jcr, TermCode);
307
308    /* Update final items to set them to the previous job's values */
309    Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
310                "JobTDate=%s WHERE JobId=%s", 
311       jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
312       edit_uint64(jcr->previous_jr.JobTDate, ec1),
313       edit_uint64(jcr->JobId, ec3));
314    db_sql_query(jcr->db, query.c_str(), NULL, NULL);
315
316    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
317       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
318          db_strerror(jcr->db));
319       set_jcr_job_status(jcr, JS_ErrorTerminated);
320    }
321
322    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
323    if (!db_get_client_record(jcr, jcr->db, &cr)) {
324       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
325          db_strerror(jcr->db));
326    }
327
328    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
329    if (!db_get_media_record(jcr, jcr->db, &mr)) {
330       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
331          mr.VolumeName, db_strerror(jcr->db));
332       set_jcr_job_status(jcr, JS_ErrorTerminated);
333    }
334
335    update_bootstrap_file(jcr);
336
337    switch (jcr->JobStatus) {
338       case JS_Terminated:
339          if (jcr->Errors || jcr->SDErrors) {
340             term_msg = _("Backup OK -- with warnings");
341          } else {
342             term_msg = _("Backup OK");
343          }
344          break;
345       case JS_FatalError:
346       case JS_ErrorTerminated:
347          term_msg = _("*** Backup Error ***");
348          msg_type = M_ERROR;          /* Generate error message */
349          if (jcr->store_bsock) {
350             jcr->store_bsock->signal(BNET_TERMINATE);
351             if (jcr->SD_msg_chan) {
352                pthread_cancel(jcr->SD_msg_chan);
353             }
354          }
355          break;
356       case JS_Canceled:
357          term_msg = _("Backup Canceled");
358          if (jcr->store_bsock) {
359             jcr->store_bsock->signal(BNET_TERMINATE);
360             if (jcr->SD_msg_chan) {
361                pthread_cancel(jcr->SD_msg_chan);
362             }
363          }
364          break;
365       default:
366          term_msg = term_code;
367          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
368          break;
369    }
370    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
371    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
372    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
373    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
374    if (RunTime <= 0) {
375       kbps = 0;
376    } else {
377       kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
378    }
379    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
380       /*
381        * Note, if the job has erred, most likely it did not write any
382        *  tape, so suppress this "error" message since in that case
383        *  it is normal.  Or look at it the other way, only for a
384        *  normal exit should we complain about this error.
385        */
386       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
387          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
388       }
389       jcr->VolumeName[0] = 0;         /* none */
390    }
391
392    if (jcr->ReadBytes == 0) {
393       bstrncpy(compress, "None", sizeof(compress));
394    } else {
395       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
396       if (compression < 0.5) {
397          bstrncpy(compress, "None", sizeof(compress));
398       } else {
399          bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
400       }
401    }
402    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
403
404    Jmsg(jcr, msg_type, 0, _("Bacula %s %s (%s): %s\n"
405 "  Build OS:               %s %s %s\n"
406 "  JobId:                  %d\n"
407 "  Job:                    %s\n"
408 "  Backup Level:           Virtual Full\n"
409 "  Client:                 \"%s\" %s\n"
410 "  FileSet:                \"%s\" %s\n"
411 "  Pool:                   \"%s\" (From %s)\n"
412 "  Catalog:                \"%s\" (From %s)\n"
413 "  Storage:                \"%s\" (From %s)\n"
414 "  Scheduled time:         %s\n"
415 "  Start time:             %s\n"
416 "  End time:               %s\n"
417 "  Elapsed time:           %s\n"
418 "  Priority:               %d\n"
419 "  SD Files Written:       %s\n"
420 "  SD Bytes Written:       %s (%sB)\n"
421 "  Rate:                   %.1f KB/s\n"
422 "  Encryption:             %s\n"
423 "  Accurate:               %s\n"
424 "  Volume name(s):         %s\n"
425 "  Volume Session Id:      %d\n"
426 "  Volume Session Time:    %d\n"
427 "  Last Volume Bytes:      %s (%sB)\n"
428 "  SD Errors:              %d\n"
429 "  SD termination status:  %s\n"
430 "  Termination:            %s\n\n"),
431         my_name, VERSION, LSMDATE, edt,
432         HOST_OS, DISTNAME, DISTVER,
433         jcr->jr.JobId,
434         jcr->jr.Job,
435         jcr->client->name(), cr.Uname,
436         jcr->fileset->name(), jcr->FSCreateTime,
437         jcr->pool->name(), jcr->pool_source,
438         jcr->catalog->name(), jcr->catalog_source,
439         jcr->wstore->name(), jcr->wstore_source,
440         schedt,
441         sdt,
442         edt,
443         edit_utime(RunTime, elapsed, sizeof(elapsed)),
444         jcr->JobPriority,
445         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
446         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
447         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
448         kbps,
449         jcr->Encrypt?_("yes"):_("no"),
450         jcr->accurate?_("yes"):_("no"),
451         jcr->VolumeName,
452         jcr->VolSessionId,
453         jcr->VolSessionTime,
454         edit_uint64_with_commas(mr.VolBytes, ec7),
455         edit_uint64_with_suffix(mr.VolBytes, ec8),
456         jcr->SDErrors,
457         sd_term_msg,
458         term_msg);
459
460    Dmsg0(100, "Leave vbackup_cleanup()\n");
461 }
462
463 /*
464  * This callback routine is responsible for inserting the
465  *  items it gets into the bootstrap structure. For each JobId selected
466  *  this routine is called once for each file. We do not allow
467  *  duplicate filenames, but instead keep the info from the most
468  *  recent file entered (i.e. the JobIds are assumed to be sorted)
469  *
470  *   See uar_sel_files in sql_cmds.c for query that calls us.
471  *      row[0]=Path, row[1]=Filename, row[2]=FileIndex
472  *      row[3]=JobId row[4]=LStat
473  */
474 int insert_bootstrap_handler(void *ctx, int num_fields, char **row)
475 {
476    JobId_t JobId;
477    int FileIndex;
478    RBSR *bsr = (RBSR *)ctx;
479
480    JobId = str_to_int64(row[3]);
481    FileIndex = str_to_int64(row[2]);
482    add_findex(bsr, JobId, FileIndex);
483    return 0;
484 }
485
486
487 static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids)
488 {
489    RESTORE_CTX rx;
490    UAContext *ua;
491
492    memset(&rx, 0, sizeof(rx));
493    rx.bsr = new_bsr();
494    ua = new_ua_context(jcr);
495    rx.JobIds = jobids;
496
497 #define new_get_file_list
498 #ifdef new_get_file_list
499    if (!db_get_file_list(jcr, ua->db, jobids, insert_bootstrap_handler, (void *)rx.bsr)) {
500       Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(ua->db));
501    }
502 #else
503    char *p;
504    JobId_t JobId, last_JobId = 0;
505    rx.query = get_pool_memory(PM_MESSAGE);
506    for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
507       char ed1[50];
508
509       if (JobId == last_JobId) {
510          continue;                    /* eliminate duplicate JobIds */
511       }
512       last_JobId = JobId;
513       /*
514        * Find files for this JobId and insert them in the tree
515        */
516       Mmsg(rx.query, uar_sel_files, edit_int64(JobId, ed1));
517       Dmsg1(100, "uar_sel_files=%s\n", rx.query);
518       if (!db_sql_query(ua->db, rx.query, insert_bootstrap_handler, (void *)rx.bsr)) {
519          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(ua->db));
520       }
521       free_pool_memory(rx.query);
522       rx.query = NULL;
523    }
524 #endif
525
526    complete_bsr(ua, rx.bsr);
527    jcr->ExpectedFiles = write_bsr_file(ua, rx);
528    if (debug_level >= 10) {
529       Dmsg1(000,  "Found %d files to consolidate.\n", jcr->ExpectedFiles);
530    }
531    if (jcr->ExpectedFiles == 0) {
532       free_ua_context(ua);
533       free_bsr(rx.bsr);
534       return false;
535    }
536    free_ua_context(ua);
537    free_bsr(rx.bsr);
538    return true;
539 }