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