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