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