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