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