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