]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
Rework Incomplete job intereaction with Base and Accurate
[bacula/bacula] / bacula / src / dird / backup.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2011 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 -- backup.c -- responsible for doing backup jobs
31  *
32  *     Kern Sibbald, March MM
33  *
34  *  Basic tasks done here:
35  *     Open DB and create records for this job.
36  *     Open Message Channel with Storage daemon to tell him a job will be starting.
37  *     Open connection with File daemon and pass him commands
38  *       to do the backup.
39  *     When the File daemon finishes the job, update the DB.
40  *
41  */
42
43 #include "bacula.h"
44 #include "dird.h"
45 #include "ua.h"
46
47 /* Commands sent to File daemon */
48 static char backupcmd[] = "backup FileIndex=%ld\n";
49 static char storaddr[]  = "storage address=%s port=%d ssl=%d\n";
50
51 /* Responses received from File daemon */
52 static char OKbackup[]   = "2000 OK backup\n";
53 static char OKstore[]    = "2000 OK storage\n";
54 static char EndJob[]     = "2800 End Job TermCode=%d JobFiles=%u "
55                            "ReadBytes=%llu JobBytes=%llu Errors=%u "  
56                            "VSS=%d Encrypt=%d\n";
57 /* Pre 1.39.29 (04Dec06) EndJob */
58 static char OldEndJob[]  = "2800 End Job TermCode=%d JobFiles=%u "
59                            "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
60 /* 
61  * Called here before the job is run to do the job
62  *   specific setup.
63  */
64 bool do_backup_init(JCR *jcr)
65 {
66
67    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
68       return do_vbackup_init(jcr);
69    }
70    free_rstorage(jcr);                   /* we don't read so release */
71
72    if (!get_or_create_fileset_record(jcr)) {
73       return false;
74    }
75
76    /* 
77     * Get definitive Job level and since time
78     */
79    get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
80
81    apply_pool_overrides(jcr);
82
83    if (!allow_duplicate_job(jcr)) {
84       return false;
85    }
86
87    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
88    if (jcr->jr.PoolId == 0) {
89       return false;
90    }
91
92    /* If pool storage specified, use it instead of job storage */
93    copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
94
95    if (!jcr->wstorage) {
96       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
97       return false;
98    }
99
100    create_clones(jcr);                /* run any clone jobs */
101
102    return true;
103 }
104
105 /* Take all base jobs from job resource and find the
106  * last L_BASE jobid.
107  */
108 static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids)
109 {
110    JOB_DBR jr;
111    JOB *job;
112    JobId_t id;
113    char str_jobid[50];
114
115    if (!jcr->job->base) {
116       return false;             /* no base job, stop accurate */
117    }
118
119    memset(&jr, 0, sizeof(JOB_DBR));
120    jr.StartTime = jcr->jr.StartTime;
121
122    foreach_alist(job, jcr->job->base) {
123       bstrncpy(jr.Name, job->name(), sizeof(jr.Name));
124       db_get_base_jobid(jcr, jcr->db, &jr, &id);
125
126       if (id) {
127          if (jobids->count) {
128             pm_strcat(jobids->list, ",");
129          }
130          pm_strcat(jobids->list, edit_uint64(id, str_jobid));
131          jobids->count++;
132       }
133    }
134
135    return jobids->count > 0;
136 }
137
138 /*
139  * Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
140  *      row[0]=Path, row[1]=Filename, row[2]=FileIndex
141  *      row[3]=JobId row[4]=LStat row[5]=MarkId row[6]=MD5
142  */
143 static int accurate_list_handler(void *ctx, int num_fields, char **row)
144 {
145    JCR *jcr = (JCR *)ctx;
146
147    if (job_canceled(jcr)) {
148       return 1;
149    }
150    
151    if (row[2][0] == '0') {           /* discard when file_index == 0 */
152       return 0;
153    }
154
155    /* sending with checksum */
156    if (jcr->use_accurate_chksum 
157        && num_fields == 7 
158        && row[6][0] /* skip checksum = '0' */
159        && row[6][1])
160    { 
161       jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s", 
162                              row[0], row[1], 0, row[4], 0, row[6], 0, row[5]); 
163    } else {
164       jcr->file_bsock->fsend("%s%s%c%s%c%c%s", 
165                              row[0], row[1], 0, row[4], 0, 0, row[5]); 
166    }
167    return 0;
168 }
169
170 /* In this procedure, we check if the current fileset is using checksum
171  * FileSet-> Include-> Options-> Accurate/Verify/BaseJob=checksum
172  * This procedure uses jcr->HasBase, so it must be call after the initialization
173  */
174 static bool is_checksum_needed_by_fileset(JCR *jcr)
175 {
176    FILESET *f;
177    INCEXE *inc;
178    FOPTS *fopts;
179    bool in_block=false;
180    bool have_basejob_option=false;
181    if (!jcr->job || !jcr->job->fileset) {
182       return false;
183    }
184
185    f = jcr->job->fileset;
186    
187    for (int i=0; i < f->num_includes; i++) { /* Parse all Include {} */
188       inc = f->include_items[i];
189       
190       for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
191          fopts = inc->opts_list[j];
192          
193          for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
194             switch (*k) {
195             case 'V':           /* verify */
196                in_block = (jcr->getJobType() == JT_VERIFY); /* not used now */
197                break;
198             case 'J':           /* Basejob keyword */
199                have_basejob_option = in_block = jcr->HasBase;
200                break;
201             case 'C':           /* Accurate keyword */
202                in_block = !jcr->is_JobLevel(L_FULL);
203                break;
204             case ':':           /* End of keyword */
205                in_block = false;
206                break;
207             case '5':           /* MD5  */
208             case '1':           /* SHA1 */
209                if (in_block) {
210                   Dmsg0(50, "Checksum will be sent to FD\n");
211                   return true;
212                }
213                break;
214             default:
215                break;
216             }
217          }
218       }
219    }
220
221    /* By default for BaseJobs, we send the checksum */
222    if (!have_basejob_option && jcr->HasBase) {
223       return true;
224    }
225    
226    Dmsg0(50, "Checksum will be sent to FD\n");
227    return false;
228 }
229
230 /*
231  * Send current file list to FD
232  *    DIR -> FD : accurate files=xxxx
233  *    DIR -> FD : /path/to/file\0Lstat\0MD5\0Delta
234  *    DIR -> FD : /path/to/dir/\0Lstat\0MD5\0Delta
235  *    ...
236  *    DIR -> FD : EOD
237  */
238 bool send_accurate_current_files(JCR *jcr)
239 {
240    POOL_MEM buf;
241    db_list_ctx jobids;
242    db_list_ctx nb;
243    char ed1[50];
244
245    /* In base level, no previous job is used and no restart incomplete jobs */
246    if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
247       return true;
248    }
249    if (!jcr->accurate && !jcr->incomplete) {
250       return true;
251    }
252
253    /* For incomplete Jobs, we add our own id */
254    if (jcr->incomplete) {
255       edit_int64(jcr->JobId, ed1);   
256       jobids.add(ed1);
257    }
258    
259    if (jcr->is_JobLevel(L_FULL)) {
260       /* On Full mode, if no previous base job, no accurate things */
261       if (!get_base_jobids(jcr, &jobids) && !jcr->incomplete) {
262          return true;
263       }
264       jcr->HasBase = true;
265       Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
266
267    } else {
268       /* For Incr/Diff level, we search for older jobs */
269       db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
270
271       /* We are in Incr/Diff, but no Full to build the accurate list... */
272       if (jobids.count == 0) {
273          Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
274          return false;  /* fail */
275       }
276    }
277
278    /* Don't send and store the checksum if fileset doesn't require it */
279    jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
280
281    if (jcr->JobId) {            /* display the message only for real jobs */
282       Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
283    }
284
285    /* to be able to allocate the right size for htable */
286    Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
287    db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
288    Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
289    jcr->file_bsock->fsend("accurate files=%s\n", nb.list); 
290
291    if (!db_open_batch_connexion(jcr, jcr->db)) {
292       Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
293       return false;  /* Fail */
294    }
295    
296    if (jcr->HasBase) {
297       jcr->nb_base_files = str_to_int64(nb.list);
298       db_create_base_file_list(jcr, jcr->db, jobids.list);
299       db_get_base_file_list(jcr, jcr->db, jcr->use_accurate_chksum,
300                             accurate_list_handler, (void *)jcr);
301
302    } else {
303       db_get_file_list(jcr, jcr->db_batch,
304                        jobids.list, jcr->use_accurate_chksum, false /* no delta */,
305                        accurate_list_handler, (void *)jcr);
306    } 
307
308    /* TODO: close the batch connection ? (can be used very soon) */
309
310    jcr->file_bsock->signal(BNET_EOD);
311    return true;
312 }
313
314 /*
315  * Do a backup of the specified FileSet
316  *
317  *  Returns:  false on failure
318  *            true  on success
319  */
320 bool do_backup(JCR *jcr)
321 {
322    int stat;
323    int tls_need = BNET_TLS_NONE;
324    BSOCK   *fd;
325    STORE *store;
326    char ed1[100];
327    db_int64_ctx job;
328    POOL_MEM buf;
329
330    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
331       return do_vbackup(jcr);
332    }
333
334    /* Print Job Start message */
335    Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
336         edit_uint64(jcr->JobId, ed1), jcr->Job);
337
338    jcr->setJobStatus(JS_Running);
339    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
340    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
341       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
342       return false;
343    }
344
345    /* For incomplete Jobs, we add our own id */
346    if (jcr->incomplete) {
347       edit_int64(jcr->JobId, ed1);   
348       Mmsg(buf, "SELECT max(FileIndex) FROM File WHERE JobId=%s", ed1);
349       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
350          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
351          return false;
352       }
353       jcr->JobFiles = job.value;
354       Dmsg1(100, "==== FI=%ld\n", jcr->JobFiles);
355       Mmsg(buf, "SELECT VolSessionId FROM Job WHERE JobId=%s", ed1);
356       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
357          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
358          return false;
359       }
360       jcr->VolSessionId = job.value;
361       Mmsg(buf, "SELECT VolSessionTime FROM Job WHERE JobId=%s", ed1);
362       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
363          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
364          return false;
365       }
366       jcr->VolSessionTime = job.value;
367       Dmsg4(100, "JobId=%s JobFiles=%ld VolSessionId=%ld VolSessionTime=%ld\n", ed1, 
368             jcr->JobFiles, jcr->VolSessionId, jcr->VolSessionTime);
369    }
370
371    /*
372     * Open a message channel connection with the Storage
373     * daemon. This is to let him know that our client
374     * will be contacting him for a backup  session.
375     *
376     */
377    Dmsg0(110, "Open connection with storage daemon\n");
378    jcr->setJobStatus(JS_WaitSD);
379    /*
380     * Start conversation with Storage daemon
381     */
382    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
383       return false;
384    }
385    /*
386     * Now start a job with the Storage daemon
387     */
388    if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
389       return false;
390    }
391
392    /*
393     * Start the job prior to starting the message thread below
394     * to avoid two threads from using the BSOCK structure at
395     * the same time.
396     */
397    if (!jcr->store_bsock->fsend("run")) {
398       return false;
399    }
400
401    /*
402     * Now start a Storage daemon message thread.  Note,
403     *   this thread is used to provide the catalog services
404     *   for the backup job, including inserting the attributes
405     *   into the catalog.  See catalog_update() in catreq.c
406     */
407    if (!start_storage_daemon_message_thread(jcr)) {
408       return false;
409    }
410    Dmsg0(150, "Storage daemon connection OK\n");
411
412    jcr->setJobStatus(JS_WaitFD);
413    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
414       goto bail_out;
415    }
416
417    jcr->setJobStatus(JS_Running);
418    fd = jcr->file_bsock;
419
420    if (!send_include_list(jcr)) {
421       goto bail_out;
422    }
423
424    if (!send_exclude_list(jcr)) {
425       goto bail_out;
426    }
427
428    if (!send_level_command(jcr)) {
429       goto bail_out;
430    }
431
432    /*
433     * send Storage daemon address to the File daemon
434     */
435    store = jcr->wstore;
436    if (store->SDDport == 0) {
437       store->SDDport = store->SDport;
438    }
439
440    /* TLS Requirement */
441    if (store->tls_enable) {
442       if (store->tls_require) {
443          tls_need = BNET_TLS_REQUIRED;
444       } else {
445          tls_need = BNET_TLS_OK;
446       }
447    }
448
449    fd->fsend(storaddr, store->address, store->SDDport, tls_need);
450    if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
451       goto bail_out;
452    }
453
454    if (!send_runscripts_commands(jcr)) {
455       goto bail_out;
456    }
457
458    /*    
459     * We re-update the job start record so that the start
460     *  time is set after the run before job.  This avoids 
461     *  that any files created by the run before job will
462     *  be saved twice.  They will be backed up in the current
463     *  job, but not in the next one unless they are changed.
464     *  Without this, they will be backed up in this job and
465     *  in the next job run because in that case, their date 
466     *   is after the start of this run.
467     */
468    jcr->start_time = time(NULL);
469    jcr->jr.StartTime = jcr->start_time;
470    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
471       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
472    }
473
474    /*
475     * If backup is in accurate mode, we send the list of
476     * all files to FD.
477     */
478    if (!send_accurate_current_files(jcr)) {
479       goto bail_out;     /* error */
480    }
481
482    /* Send backup command */
483    fd->fsend(backupcmd, jcr->JobFiles);
484    Dmsg1(100, ">filed: %s", fd->msg);
485    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
486       goto bail_out;
487    }
488
489    /* Pickup Job termination data */
490    stat = wait_for_job_termination(jcr);
491    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
492
493    if (jcr->HasBase && !db_commit_base_file_attributes_record(jcr, jcr->db))  {
494       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
495    }
496
497    if (stat == JS_Terminated) {
498       backup_cleanup(jcr, stat);
499       return true;
500    }     
501    return false;
502
503 /* Come here only after starting SD thread */
504 bail_out:
505    jcr->setJobStatus(JS_ErrorTerminated);
506    Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
507    /* Cancel SD */
508    wait_for_job_termination(jcr, FDConnectTimeout);
509    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
510    return false;
511 }
512
513
514 /*
515  * Here we wait for the File daemon to signal termination,
516  *   then we wait for the Storage daemon.  When both
517  *   are done, we return the job status.
518  * Also used by restore.c
519  */
520 int wait_for_job_termination(JCR *jcr, int timeout)
521 {
522    int32_t n = 0;
523    BSOCK *fd = jcr->file_bsock;
524    bool fd_ok = false;
525    uint32_t JobFiles, JobErrors;
526    uint32_t JobWarnings = 0;
527    uint64_t ReadBytes = 0;
528    uint64_t JobBytes = 0;
529    int VSS = 0;
530    int Encrypt = 0;
531    btimer_t *tid=NULL;
532
533    jcr->setJobStatus(JS_Running);
534
535    if (fd) {
536       if (timeout) {
537          tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
538       }
539       /* Wait for Client to terminate */
540       while ((n = bget_dirmsg(fd)) >= 0) {
541          if (!fd_ok && 
542              (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
543                      &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
544               sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
545                      &ReadBytes, &JobBytes, &JobErrors) == 5)) {
546             fd_ok = true;
547             jcr->setJobStatus(jcr->FDJobStatus);
548             Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
549          } else {
550             Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
551                  fd->msg);
552          }
553          if (job_canceled(jcr)) {
554             break;
555          }
556       }
557       if (tid) {
558          stop_bsock_timer(tid);
559       }
560
561       if (is_bnet_error(fd)) {
562          int i = 0;
563          Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
564               job_type_to_str(jcr->getJobType()), fd->bstrerror());
565          while (i++ < 10 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
566             bmicrosleep(3, 0);
567          }
568             
569       }
570       fd->signal(BNET_TERMINATE);   /* tell Client we are terminating */
571    }
572
573    /*
574     * Force cancel in SD if failing, but not for Incomplete jobs
575     *  so that we let the SD despool.
576     */
577    Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
578         jcr->JobStatus, jcr->SDJobStatus);
579    if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
580       Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
581            jcr->JobStatus, jcr->SDJobStatus);
582       cancel_storage_daemon_job(jcr);
583    }
584
585    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
586    wait_for_storage_daemon_termination(jcr);
587
588    /* Return values from FD */
589    if (fd_ok) {
590       jcr->JobFiles = JobFiles;
591       jcr->JobErrors += JobErrors;       /* Keep total errors */
592       jcr->ReadBytes = ReadBytes;
593       jcr->JobBytes = JobBytes;
594       jcr->JobWarnings = JobWarnings;
595       jcr->VSS = VSS;
596       jcr->Encrypt = Encrypt;
597    } else {
598       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
599    }
600
601 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
602 //   jcr->JobStatus, jcr->SDJobStatus);
603
604    /* Return the first error status we find Dir, FD, or SD */
605    if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
606       jcr->FDJobStatus = JS_ErrorTerminated;
607    }
608    if (jcr->JobStatus != JS_Terminated) {
609       return jcr->JobStatus;
610    }
611    if (jcr->FDJobStatus != JS_Terminated) {
612       return jcr->FDJobStatus;
613    }
614    return jcr->SDJobStatus;
615 }
616
617 /*
618  * Release resources allocated during backup.
619  */
620 void backup_cleanup(JCR *jcr, int TermCode)
621 {
622    char sdt[50], edt[50], schedt[50];
623    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
624    char ec6[30], ec7[30], ec8[30], elapsed[50];
625    char term_code[100], fd_term_msg[100], sd_term_msg[100];
626    const char *term_msg;
627    int msg_type = M_INFO;
628    MEDIA_DBR mr;
629    CLIENT_DBR cr;
630    double kbps, compression;
631    utime_t RunTime;
632    POOL_MEM base_info;
633
634    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
635       vbackup_cleanup(jcr, TermCode);
636       return;
637    }
638
639    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
640    memset(&mr, 0, sizeof(mr));
641    memset(&cr, 0, sizeof(cr));
642
643 #ifdef xxxx
644    if (jcr->getJobStatus() == JS_Terminated && 
645         (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
646       TermCode = JS_Warnings;
647    }
648 #endif
649          
650    update_job_end(jcr, TermCode);
651
652    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
653       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
654          db_strerror(jcr->db));
655       jcr->setJobStatus(JS_ErrorTerminated);
656    }
657
658    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
659    if (!db_get_client_record(jcr, jcr->db, &cr)) {
660       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
661          db_strerror(jcr->db));
662    }
663
664    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
665    if (!db_get_media_record(jcr, jcr->db, &mr)) {
666       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
667          mr.VolumeName, db_strerror(jcr->db));
668       jcr->setJobStatus(JS_ErrorTerminated);
669    }
670
671    update_bootstrap_file(jcr);
672
673    switch (jcr->JobStatus) {
674       case JS_Terminated:
675          if (jcr->JobErrors || jcr->SDErrors) {
676             term_msg = _("Backup OK -- with warnings");
677          } else {
678             term_msg = _("Backup OK");
679          }
680          break;
681       case JS_Incomplete:
682          term_msg = _("Backup failed -- incomplete");
683          break;
684       case JS_Warnings:
685          term_msg = _("Backup OK -- with warnings");
686          break;
687       case JS_FatalError:
688       case JS_ErrorTerminated:
689          term_msg = _("*** Backup Error ***");
690          msg_type = M_ERROR;          /* Generate error message */
691          if (jcr->store_bsock) {
692             jcr->store_bsock->signal(BNET_TERMINATE);
693             if (jcr->SD_msg_chan) {
694                pthread_cancel(jcr->SD_msg_chan);
695             }
696          }
697          break;
698       case JS_Canceled:
699          term_msg = _("Backup Canceled");
700          if (jcr->store_bsock) {
701             jcr->store_bsock->signal(BNET_TERMINATE);
702             if (jcr->SD_msg_chan) {
703                pthread_cancel(jcr->SD_msg_chan);
704             }
705          }
706          break;
707       default:
708          term_msg = term_code;
709          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
710          break;
711    }
712    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
713    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
714    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
715    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
716    if (RunTime <= 0) {
717       kbps = 0;
718    } else {
719       kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
720    }
721    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
722       /*
723        * Note, if the job has erred, most likely it did not write any
724        *  tape, so suppress this "error" message since in that case
725        *  it is normal.  Or look at it the other way, only for a
726        *  normal exit should we complain about this error.
727        */
728       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
729          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
730       }
731       jcr->VolumeName[0] = 0;         /* none */
732    }
733
734    if (jcr->ReadBytes == 0) {
735       bstrncpy(compress, "None", sizeof(compress));
736    } else {
737       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
738       if (compression < 0.5) {
739          bstrncpy(compress, "None", sizeof(compress));
740       } else {
741          bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
742       }
743    }
744    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
745    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
746
747    if (jcr->HasBase) {
748       Mmsg(base_info, "  Base files/Used files:  %lld/%lld (%.2f%%)\n",
749            jcr->nb_base_files, 
750            jcr->nb_base_files_used, 
751            jcr->nb_base_files_used*100.0/jcr->nb_base_files);
752    }
753 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
754
755    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
756 "  Build OS:               %s %s %s\n"
757 "  JobId:                  %d\n"
758 "  Job:                    %s\n"
759 "  Backup Level:           %s%s\n"
760 "  Client:                 \"%s\" %s\n"
761 "  FileSet:                \"%s\" %s\n"
762 "  Pool:                   \"%s\" (From %s)\n"
763 "  Catalog:                \"%s\" (From %s)\n"
764 "  Storage:                \"%s\" (From %s)\n"
765 "  Scheduled time:         %s\n"
766 "  Start time:             %s\n"
767 "  End time:               %s\n"
768 "  Elapsed time:           %s\n"
769 "  Priority:               %d\n"
770 "  FD Files Written:       %s\n"
771 "  SD Files Written:       %s\n"
772 "  FD Bytes Written:       %s (%sB)\n"
773 "  SD Bytes Written:       %s (%sB)\n"
774 "  Rate:                   %.1f KB/s\n"
775 "  Software Compression:   %s\n"
776 "%s"                                         /* Basefile info */
777 "  VSS:                    %s\n"
778 "  Encryption:             %s\n"
779 "  Accurate:               %s\n"
780 "  Volume name(s):         %s\n"
781 "  Volume Session Id:      %d\n"
782 "  Volume Session Time:    %d\n"
783 "  Last Volume Bytes:      %s (%sB)\n"
784 "  Non-fatal FD errors:    %d\n"
785 "  SD Errors:              %d\n"
786 "  FD termination status:  %s\n"
787 "  SD termination status:  %s\n"
788 "  Termination:            %s\n\n"),
789         BACULA, my_name, VERSION, LSMDATE,
790         HOST_OS, DISTNAME, DISTVER,
791         jcr->jr.JobId,
792         jcr->jr.Job,
793         level_to_str(jcr->getJobLevel()), jcr->since,
794         jcr->client->name(), cr.Uname,
795         jcr->fileset->name(), jcr->FSCreateTime,
796         jcr->pool->name(), jcr->pool_source,
797         jcr->catalog->name(), jcr->catalog_source,
798         jcr->wstore->name(), jcr->wstore_source,
799         schedt,
800         sdt,
801         edt,
802         edit_utime(RunTime, elapsed, sizeof(elapsed)),
803         jcr->JobPriority,
804         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
805         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
806         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
807         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
808         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
809         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
810         kbps,
811         compress,
812         base_info.c_str(),
813         jcr->VSS?_("yes"):_("no"),
814         jcr->Encrypt?_("yes"):_("no"),
815         jcr->accurate?_("yes"):_("no"),
816         jcr->VolumeName,
817         jcr->VolSessionId,
818         jcr->VolSessionTime,
819         edit_uint64_with_commas(mr.VolBytes, ec7),
820         edit_uint64_with_suffix(mr.VolBytes, ec8),
821         jcr->JobErrors,
822         jcr->SDErrors,
823         fd_term_msg,
824         sd_term_msg,
825         term_msg);
826
827    Dmsg0(100, "Leave backup_cleanup()\n");
828 }
829
830 void update_bootstrap_file(JCR *jcr)
831 {
832    /* Now update the bootstrap file if any */
833    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
834        jcr->job->WriteBootstrap) {
835       FILE *fd;
836       BPIPE *bpipe = NULL;
837       int got_pipe = 0;
838       POOLMEM *fname = get_pool_memory(PM_FNAME);
839       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
840
841       VOL_PARAMS *VolParams = NULL;
842       int VolCount;
843       char edt[50], ed1[50], ed2[50];
844
845       if (*fname == '|') {
846          got_pipe = 1;
847          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
848          fd = bpipe ? bpipe->wfd : NULL;
849       } else {
850          /* ***FIXME*** handle BASE */
851          fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
852       }
853       if (fd) {
854          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
855                     &VolParams);
856          if (VolCount == 0) {
857             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
858                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
859              if (jcr->SDJobFiles != 0) {
860                 jcr->setJobStatus(JS_ErrorTerminated);
861              }
862
863          }
864          /* Start output with when and who wrote it */
865          bstrftimes(edt, sizeof(edt), time(NULL));
866          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
867                  level_to_str(jcr->getJobLevel()), jcr->since);
868          for (int i=0; i < VolCount; i++) {
869             /* Write the record */
870             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
871             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
872             if (VolParams[i].Slot > 0) {
873                fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
874             }
875             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
876             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
877             fprintf(fd, "VolAddr=%s-%s\n", 
878                     edit_uint64(VolParams[i].StartAddr, ed1),
879                     edit_uint64(VolParams[i].EndAddr, ed2));
880             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
881                          VolParams[i].LastIndex);
882          }
883          if (VolParams) {
884             free(VolParams);
885          }
886          if (got_pipe) {
887             close_bpipe(bpipe);
888          } else {
889             fclose(fd);
890          }
891       } else {
892          berrno be;
893          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
894               "%s: ERR=%s\n"), fname, be.bstrerror());
895          jcr->setJobStatus(JS_ErrorTerminated);
896       }
897       free_pool_memory(fname);
898    }
899 }