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