]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/vbackup.c
Fix #4996 about MaxRunTime canceling the job too early
[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    /* Declare the job started to start the MaxRunTime check */
236    jcr->setJobStarted();
237
238    /*
239     * Start the job prior to starting the message thread below
240     * to avoid two threads from using the BSOCK structure at
241     * the same time.
242     */
243    if (!sd->fsend("run")) {
244       return false;
245    }
246
247    /*
248     * Now start a Storage daemon message thread
249     */
250    if (!start_storage_daemon_message_thread(jcr)) {
251       return false;
252    }
253
254    jcr->setJobStatus(JS_Running);
255
256    /* Pickup Job termination data */
257    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
258    wait_for_storage_daemon_termination(jcr);
259    jcr->setJobStatus(jcr->SDJobStatus);
260    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
261    if (jcr->JobStatus != JS_Terminated) {
262       return false;
263    }
264
265    vbackup_cleanup(jcr, jcr->JobStatus);
266    return true;
267 }
268
269
270 /*
271  * Release resources allocated during backup.
272  */
273 void vbackup_cleanup(JCR *jcr, int TermCode)
274 {
275    char sdt[50], edt[50], schedt[50];
276    char ec1[30], ec3[30], ec4[30], compress[50];
277    char ec7[30], ec8[30], elapsed[50];
278    char term_code[100], sd_term_msg[100];
279    const char *term_msg;
280    int msg_type = M_INFO;
281    MEDIA_DBR mr;
282    CLIENT_DBR cr;
283    double kbps, compression;
284    utime_t RunTime;
285    POOL_MEM query(PM_MESSAGE);
286
287    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
288    memset(&cr, 0, sizeof(cr));
289
290    jcr->setJobLevel(L_FULL);         /* we want this to appear as a Full backup */
291    jcr->jr.JobLevel = L_FULL;         /* we want this to appear as a Full backup */
292    jcr->JobFiles = jcr->SDJobFiles;
293    jcr->JobBytes = jcr->SDJobBytes;
294    update_job_end(jcr, TermCode);
295
296    /* Update final items to set them to the previous job's values */
297    Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
298                "JobTDate=%s WHERE JobId=%s", 
299       jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
300       edit_uint64(jcr->previous_jr.JobTDate, ec1),
301       edit_uint64(jcr->JobId, ec3));
302    db_sql_query(jcr->db, query.c_str(), NULL, NULL);
303
304    /* Get the fully updated job record */
305    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
306       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
307          db_strerror(jcr->db));
308       jcr->setJobStatus(JS_ErrorTerminated);
309    }
310
311    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
312    if (!db_get_client_record(jcr, jcr->db, &cr)) {
313       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
314          db_strerror(jcr->db));
315    }
316
317    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
318    if (!db_get_media_record(jcr, jcr->db, &mr)) {
319       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
320          mr.VolumeName, db_strerror(jcr->db));
321       jcr->setJobStatus(JS_ErrorTerminated);
322    }
323
324    update_bootstrap_file(jcr);
325
326    switch (jcr->JobStatus) {
327       case JS_Terminated:
328          if (jcr->JobErrors || jcr->SDErrors) {
329             term_msg = _("Backup OK -- with warnings");
330          } else {
331             term_msg = _("Backup OK");
332          }
333          break;
334       case JS_FatalError:
335       case JS_ErrorTerminated:
336          term_msg = _("*** Backup Error ***");
337          msg_type = M_ERROR;          /* Generate error message */
338          if (jcr->store_bsock) {
339             jcr->store_bsock->signal(BNET_TERMINATE);
340             if (jcr->SD_msg_chan) {
341                pthread_cancel(jcr->SD_msg_chan);
342             }
343          }
344          break;
345       case JS_Canceled:
346          term_msg = _("Backup Canceled");
347          if (jcr->store_bsock) {
348             jcr->store_bsock->signal(BNET_TERMINATE);
349             if (jcr->SD_msg_chan) {
350                pthread_cancel(jcr->SD_msg_chan);
351             }
352          }
353          break;
354       default:
355          term_msg = term_code;
356          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
357          break;
358    }
359    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
360    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
361    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
362    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
363    if (RunTime <= 0) {
364       kbps = 0;
365    } else {
366       kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
367    }
368    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
369       /*
370        * Note, if the job has erred, most likely it did not write any
371        *  tape, so suppress this "error" message since in that case
372        *  it is normal.  Or look at it the other way, only for a
373        *  normal exit should we complain about this error.
374        */
375       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
376          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
377       }
378       jcr->VolumeName[0] = 0;         /* none */
379    }
380
381    if (jcr->ReadBytes == 0) {
382       bstrncpy(compress, "None", sizeof(compress));
383    } else {
384       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
385       if (compression < 0.5) {
386          bstrncpy(compress, "None", sizeof(compress));
387       } else {
388          bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
389       }
390    }
391    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
392
393    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
394 "  Build OS:               %s %s %s\n"
395 "  JobId:                  %d\n"
396 "  Job:                    %s\n"
397 "  Backup Level:           Virtual Full\n"
398 "  Client:                 \"%s\" %s\n"
399 "  FileSet:                \"%s\" %s\n"
400 "  Pool:                   \"%s\" (From %s)\n"
401 "  Catalog:                \"%s\" (From %s)\n"
402 "  Storage:                \"%s\" (From %s)\n"
403 "  Scheduled time:         %s\n"
404 "  Start time:             %s\n"
405 "  End time:               %s\n"
406 "  Elapsed time:           %s\n"
407 "  Priority:               %d\n"
408 "  SD Files Written:       %s\n"
409 "  SD Bytes Written:       %s (%sB)\n"
410 "  Rate:                   %.1f KB/s\n"
411 "  Volume name(s):         %s\n"
412 "  Volume Session Id:      %d\n"
413 "  Volume Session Time:    %d\n"
414 "  Last Volume Bytes:      %s (%sB)\n"
415 "  SD Errors:              %d\n"
416 "  SD termination status:  %s\n"
417 "  Termination:            %s\n\n"),
418         BACULA, my_name, VERSION, LSMDATE,
419         HOST_OS, DISTNAME, DISTVER,
420         jcr->jr.JobId,
421         jcr->jr.Job,
422         jcr->client->name(), cr.Uname,
423         jcr->fileset->name(), jcr->FSCreateTime,
424         jcr->pool->name(), jcr->pool_source,
425         jcr->catalog->name(), jcr->catalog_source,
426         jcr->wstore->name(), jcr->wstore_source,
427         schedt,
428         sdt,
429         edt,
430         edit_utime(RunTime, elapsed, sizeof(elapsed)),
431         jcr->JobPriority,
432         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
433         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
434         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
435         kbps,
436         jcr->VolumeName,
437         jcr->VolSessionId,
438         jcr->VolSessionTime,
439         edit_uint64_with_commas(mr.VolBytes, ec7),
440         edit_uint64_with_suffix(mr.VolBytes, ec8),
441         jcr->SDErrors,
442         sd_term_msg,
443         term_msg);
444
445    Dmsg0(100, "Leave vbackup_cleanup()\n");
446 }
447
448 /*
449  * This callback routine is responsible for inserting the
450  *  items it gets into the bootstrap structure. For each JobId selected
451  *  this routine is called once for each file. We do not allow
452  *  duplicate filenames, but instead keep the info from the most
453  *  recent file entered (i.e. the JobIds are assumed to be sorted)
454  *
455  *   See uar_sel_files in sql_cmds.c for query that calls us.
456  *      row[0]=Path, row[1]=Filename, row[2]=FileIndex
457  *      row[3]=JobId row[4]=LStat
458  */
459 int insert_bootstrap_handler(void *ctx, int num_fields, char **row)
460 {
461    JobId_t JobId;
462    int FileIndex;
463    RBSR *bsr = (RBSR *)ctx;
464
465    JobId = str_to_int64(row[3]);
466    FileIndex = str_to_int64(row[2]);
467    add_findex(bsr, JobId, FileIndex);
468    return 0;
469 }
470
471
472 static bool create_bootstrap_file(JCR *jcr, char *jobids)
473 {
474    RESTORE_CTX rx;
475    UAContext *ua;
476
477    memset(&rx, 0, sizeof(rx));
478    rx.bsr = new_bsr();
479    ua = new_ua_context(jcr);
480    rx.JobIds = jobids;
481
482 #define new_get_file_list
483 #ifdef new_get_file_list
484    if (!db_open_batch_connexion(jcr, jcr->db)) {
485       Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
486       return false;
487    }
488
489    if (!db_get_file_list(jcr, jcr->db_batch, jobids, false /* don't use md5 */,
490                          true /* use delta */,
491                          insert_bootstrap_handler, (void *)rx.bsr))
492    {
493       Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db_batch));
494    }
495 #else
496    char *p;
497    JobId_t JobId, last_JobId = 0;
498    rx.query = get_pool_memory(PM_MESSAGE);
499    for (p=rx.JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
500       char ed1[50];
501
502       if (JobId == last_JobId) {
503          continue;                    /* eliminate duplicate JobIds */
504       }
505       last_JobId = JobId;
506       /*
507        * Find files for this JobId and insert them in the tree
508        */
509       Mmsg(rx.query, uar_sel_files, edit_int64(JobId, ed1));
510       Dmsg1(100, "uar_sel_files=%s\n", rx.query);
511       if (!db_sql_query(ua->db, rx.query, insert_bootstrap_handler, (void *)rx.bsr)) {
512          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(ua->db));
513       }
514       free_pool_memory(rx.query);
515       rx.query = NULL;
516    }
517 #endif
518
519    complete_bsr(ua, rx.bsr);
520    jcr->ExpectedFiles = write_bsr_file(ua, rx);
521    if (debug_level >= 10) {
522       Dmsg1(000,  "Found %d files to consolidate.\n", jcr->ExpectedFiles);
523    }
524    if (jcr->ExpectedFiles == 0) {
525       free_ua_context(ua);
526       free_bsr(rx.bsr);
527       return false;
528    }
529    free_ua_context(ua);
530    free_bsr(rx.bsr);
531    return true;
532 }