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