]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
Fix #4996 about MaxRunTime canceling the job too early
[bacula/bacula] / bacula / src / dird / backup.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- 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]=DeltaSeq 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
244    /* In base level, no previous job is used and no restart incomplete jobs */
245    if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
246       return true;
247    }
248    if (!jcr->accurate) {
249       return true;
250    }
251
252    if (jcr->is_JobLevel(L_FULL)) {
253       /* On Full mode, if no previous base job, no accurate things */
254       if (get_base_jobids(jcr, &jobids)) {
255          jcr->HasBase = true;
256          Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
257       } else {
258          return true;
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          Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
267          return false;  /* fail */
268       }
269    }
270
271    /* Don't send and store the checksum if fileset doesn't require it */
272    jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
273
274    if (jcr->JobId) {            /* display the message only for real jobs */
275       Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
276    }
277
278    /* to be able to allocate the right size for htable */
279    Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
280    db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
281    Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
282    jcr->file_bsock->fsend("accurate files=%s\n", nb.list); 
283
284    if (!db_open_batch_connexion(jcr, jcr->db)) {
285       Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
286       return false;  /* Fail */
287    }
288    
289    if (jcr->HasBase) {
290       jcr->nb_base_files = str_to_int64(nb.list);
291       db_create_base_file_list(jcr, jcr->db, jobids.list);
292       db_get_base_file_list(jcr, jcr->db, jcr->use_accurate_chksum,
293                             accurate_list_handler, (void *)jcr);
294
295    } else {
296       db_get_file_list(jcr, jcr->db_batch,
297                        jobids.list, jcr->use_accurate_chksum, false /* no delta */,
298                        accurate_list_handler, (void *)jcr);
299    } 
300
301    /* TODO: close the batch connection ? (can be used very soon) */
302
303    jcr->file_bsock->signal(BNET_EOD);
304    return true;
305 }
306
307 /*
308  * Do a backup of the specified FileSet
309  *
310  *  Returns:  false on failure
311  *            true  on success
312  */
313 bool do_backup(JCR *jcr)
314 {
315    int stat;
316    int tls_need = BNET_TLS_NONE;
317    BSOCK   *fd;
318    STORE *store;
319    char ed1[100];
320    db_int64_ctx job;
321    POOL_MEM buf;
322
323    if (jcr->is_JobLevel(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 (!jcr->store_bsock->fsend("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_level_command(jcr)) {
388       goto bail_out;
389    }
390
391    if (!send_include_list(jcr)) {
392       goto bail_out;
393    }
394
395    if (!send_exclude_list(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    /* Declare the job started to start the MaxRunTime check */
422    jcr->setJobStarted();
423
424    /* Send and run the RunBefore */
425    if (!send_runscripts_commands(jcr)) {
426       goto bail_out;
427    }
428
429    /*    
430     * We re-update the job start record so that the start
431     *  time is set after the run before job.  This avoids 
432     *  that any files created by the run before job will
433     *  be saved twice.  They will be backed up in the current
434     *  job, but not in the next one unless they are changed.
435     *  Without this, they will be backed up in this job and
436     *  in the next job run because in that case, their date 
437     *   is after the start of this run.
438     */
439    jcr->start_time = time(NULL);
440    jcr->jr.StartTime = jcr->start_time;
441    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
442       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
443    }
444
445    /*
446     * If backup is in accurate mode, we send the list of
447     * all files to FD.
448     */
449    if (!send_accurate_current_files(jcr)) {
450       goto bail_out;     /* error */
451    }
452
453    /* Send backup command */
454    fd->fsend(backupcmd, jcr->JobFiles);
455    Dmsg1(100, ">filed: %s", fd->msg);
456    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
457       goto bail_out;
458    }
459
460    /* Pickup Job termination data */
461    stat = wait_for_job_termination(jcr);
462    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
463
464    if (jcr->HasBase && !db_commit_base_file_attributes_record(jcr, jcr->db))  {
465       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
466    }
467
468    if (stat == JS_Terminated) {
469       backup_cleanup(jcr, stat);
470       return true;
471    }     
472    return false;
473
474 /* Come here only after starting SD thread */
475 bail_out:
476    jcr->setJobStatus(JS_ErrorTerminated);
477    Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
478    /* Cancel SD */
479    wait_for_job_termination(jcr, FDConnectTimeout);
480    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
481    return false;
482 }
483
484
485 /*
486  * Here we wait for the File daemon to signal termination,
487  *   then we wait for the Storage daemon.  When both
488  *   are done, we return the job status.
489  * Also used by restore.c
490  */
491 int wait_for_job_termination(JCR *jcr, int timeout)
492 {
493    int32_t n = 0;
494    BSOCK *fd = jcr->file_bsock;
495    bool fd_ok = false;
496    uint32_t JobFiles, JobErrors;
497    uint32_t JobWarnings = 0;
498    uint64_t ReadBytes = 0;
499    uint64_t JobBytes = 0;
500    int VSS = 0;
501    int Encrypt = 0;
502    btimer_t *tid=NULL;
503
504    jcr->setJobStatus(JS_Running);
505
506    if (fd) {
507       if (timeout) {
508          tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
509       }
510       /* Wait for Client to terminate */
511       while ((n = bget_dirmsg(fd)) >= 0) {
512          if (!fd_ok && 
513              (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
514                      &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
515               sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
516                      &ReadBytes, &JobBytes, &JobErrors) == 5)) {
517             fd_ok = true;
518             jcr->setJobStatus(jcr->FDJobStatus);
519             Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
520          } else {
521             Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
522                  fd->msg);
523          }
524          if (job_canceled(jcr)) {
525             break;
526          }
527       }
528       if (tid) {
529          stop_bsock_timer(tid);
530       }
531
532       if (is_bnet_error(fd)) {
533          int i = 0;
534          Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
535               job_type_to_str(jcr->getJobType()), fd->bstrerror());
536          while (i++ < 10 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
537             bmicrosleep(3, 0);
538          }
539             
540       }
541       fd->signal(BNET_TERMINATE);   /* tell Client we are terminating */
542    }
543
544    /*
545     * Force cancel in SD if failing, but not for Incomplete jobs
546     *  so that we let the SD despool.
547     */
548    Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
549         jcr->JobStatus, jcr->SDJobStatus);
550    if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
551       Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
552            jcr->JobStatus, jcr->SDJobStatus);
553       cancel_storage_daemon_job(jcr);
554    }
555
556    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
557    wait_for_storage_daemon_termination(jcr);
558
559    /* Return values from FD */
560    if (fd_ok) {
561       jcr->JobFiles = JobFiles;
562       jcr->JobErrors += JobErrors;       /* Keep total errors */
563       jcr->ReadBytes = ReadBytes;
564       jcr->JobBytes = JobBytes;
565       jcr->JobWarnings = JobWarnings;
566       jcr->VSS = VSS;
567       jcr->Encrypt = Encrypt;
568    } else {
569       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
570    }
571
572 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
573 //   jcr->JobStatus, jcr->SDJobStatus);
574
575    /* Return the first error status we find Dir, FD, or SD */
576    if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
577       jcr->FDJobStatus = JS_ErrorTerminated;
578    }
579    if (jcr->JobStatus != JS_Terminated) {
580       return jcr->JobStatus;
581    }
582    if (jcr->FDJobStatus != JS_Terminated) {
583       return jcr->FDJobStatus;
584    }
585    return jcr->SDJobStatus;
586 }
587
588 /*
589  * Release resources allocated during backup.
590  */
591 void backup_cleanup(JCR *jcr, int TermCode)
592 {
593    char sdt[50], edt[50], schedt[50];
594    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
595    char ec6[30], ec7[30], ec8[30], elapsed[50];
596    char term_code[100], fd_term_msg[100], sd_term_msg[100];
597    const char *term_msg;
598    int msg_type = M_INFO;
599    MEDIA_DBR mr;
600    CLIENT_DBR cr;
601    double kbps, compression;
602    utime_t RunTime;
603    POOL_MEM base_info;
604
605    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
606       vbackup_cleanup(jcr, TermCode);
607       return;
608    }
609
610    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
611    memset(&cr, 0, sizeof(cr));
612
613 #ifdef xxxx
614    /* The current implementation of the JS_Warning status is not
615     * completed. SQL part looks to be ok, but the code is using
616     * JS_Terminated almost everywhere instead of (JS_Terminated || JS_Warning)
617     * as we do with is_canceled()
618     */
619    if (jcr->getJobStatus() == JS_Terminated && 
620         (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
621       TermCode = JS_Warnings;
622    }
623 #endif
624          
625    update_job_end(jcr, TermCode);
626
627    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
628       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
629          db_strerror(jcr->db));
630       jcr->setJobStatus(JS_ErrorTerminated);
631    }
632
633    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
634    if (!db_get_client_record(jcr, jcr->db, &cr)) {
635       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
636          db_strerror(jcr->db));
637    }
638
639    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
640    if (!db_get_media_record(jcr, jcr->db, &mr)) {
641       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
642          mr.VolumeName, db_strerror(jcr->db));
643       jcr->setJobStatus(JS_ErrorTerminated);
644    }
645
646    update_bootstrap_file(jcr);
647
648    switch (jcr->JobStatus) {
649       case JS_Terminated:
650          if (jcr->JobErrors || jcr->SDErrors) {
651             term_msg = _("Backup OK -- with warnings");
652          } else {
653             term_msg = _("Backup OK");
654          }
655          break;
656       case JS_Incomplete:
657          term_msg = _("Backup failed -- incomplete");
658          break;
659       case JS_Warnings:
660          term_msg = _("Backup OK -- with warnings");
661          break;
662       case JS_FatalError:
663       case JS_ErrorTerminated:
664          term_msg = _("*** Backup Error ***");
665          msg_type = M_ERROR;          /* Generate error message */
666          if (jcr->store_bsock) {
667             jcr->store_bsock->signal(BNET_TERMINATE);
668             if (jcr->SD_msg_chan) {
669                pthread_cancel(jcr->SD_msg_chan);
670             }
671          }
672          break;
673       case JS_Canceled:
674          term_msg = _("Backup Canceled");
675          if (jcr->store_bsock) {
676             jcr->store_bsock->signal(BNET_TERMINATE);
677             if (jcr->SD_msg_chan) {
678                pthread_cancel(jcr->SD_msg_chan);
679             }
680          }
681          break;
682       default:
683          term_msg = term_code;
684          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
685          break;
686    }
687    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
688    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
689    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
690    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
691    if (RunTime <= 0) {
692       kbps = 0;
693    } else {
694       kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
695    }
696    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
697       /*
698        * Note, if the job has erred, most likely it did not write any
699        *  tape, so suppress this "error" message since in that case
700        *  it is normal.  Or look at it the other way, only for a
701        *  normal exit should we complain about this error.
702        */
703       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
704          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
705       }
706       jcr->VolumeName[0] = 0;         /* none */
707    }
708
709    if (jcr->ReadBytes == 0) {
710       bstrncpy(compress, "None", sizeof(compress));
711    } else {
712       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
713       if (compression < 0.5) {
714          bstrncpy(compress, "None", sizeof(compress));
715       } else {
716          bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
717       }
718    }
719    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
720    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
721
722    if (jcr->HasBase) {
723       Mmsg(base_info, "  Base files/Used files:  %lld/%lld (%.2f%%)\n",
724            jcr->nb_base_files, 
725            jcr->nb_base_files_used, 
726            jcr->nb_base_files_used*100.0/jcr->nb_base_files);
727    }
728 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
729
730    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
731 "  Build OS:               %s %s %s\n"
732 "  JobId:                  %d\n"
733 "  Job:                    %s\n"
734 "  Backup Level:           %s%s\n"
735 "  Client:                 \"%s\" %s\n"
736 "  FileSet:                \"%s\" %s\n"
737 "  Pool:                   \"%s\" (From %s)\n"
738 "  Catalog:                \"%s\" (From %s)\n"
739 "  Storage:                \"%s\" (From %s)\n"
740 "  Scheduled time:         %s\n"
741 "  Start time:             %s\n"
742 "  End time:               %s\n"
743 "  Elapsed time:           %s\n"
744 "  Priority:               %d\n"
745 "  FD Files Written:       %s\n"
746 "  SD Files Written:       %s\n"
747 "  FD Bytes Written:       %s (%sB)\n"
748 "  SD Bytes Written:       %s (%sB)\n"
749 "  Rate:                   %.1f KB/s\n"
750 "  Software Compression:   %s\n"
751 "%s"                                         /* Basefile info */
752 "  VSS:                    %s\n"
753 "  Encryption:             %s\n"
754 "  Accurate:               %s\n"
755 "  Volume name(s):         %s\n"
756 "  Volume Session Id:      %d\n"
757 "  Volume Session Time:    %d\n"
758 "  Last Volume Bytes:      %s (%sB)\n"
759 "  Non-fatal FD errors:    %d\n"
760 "  SD Errors:              %d\n"
761 "  FD termination status:  %s\n"
762 "  SD termination status:  %s\n"
763 "  Termination:            %s\n\n"),
764         BACULA, my_name, VERSION, LSMDATE,
765         HOST_OS, DISTNAME, DISTVER,
766         jcr->jr.JobId,
767         jcr->jr.Job,
768         level_to_str(jcr->getJobLevel()), jcr->since,
769         jcr->client->name(), cr.Uname,
770         jcr->fileset->name(), jcr->FSCreateTime,
771         jcr->pool->name(), jcr->pool_source,
772         jcr->catalog->name(), jcr->catalog_source,
773         jcr->wstore->name(), jcr->wstore_source,
774         schedt,
775         sdt,
776         edt,
777         edit_utime(RunTime, elapsed, sizeof(elapsed)),
778         jcr->JobPriority,
779         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
780         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
781         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
782         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
783         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
784         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
785         kbps,
786         compress,
787         base_info.c_str(),
788         jcr->VSS?_("yes"):_("no"),
789         jcr->Encrypt?_("yes"):_("no"),
790         jcr->accurate?_("yes"):_("no"),
791         jcr->VolumeName,
792         jcr->VolSessionId,
793         jcr->VolSessionTime,
794         edit_uint64_with_commas(mr.VolBytes, ec7),
795         edit_uint64_with_suffix(mr.VolBytes, ec8),
796         jcr->JobErrors,
797         jcr->SDErrors,
798         fd_term_msg,
799         sd_term_msg,
800         term_msg);
801
802    Dmsg0(100, "Leave backup_cleanup()\n");
803 }
804
805 void update_bootstrap_file(JCR *jcr)
806 {
807    /* Now update the bootstrap file if any */
808    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
809        jcr->job->WriteBootstrap) {
810       FILE *fd;
811       BPIPE *bpipe = NULL;
812       int got_pipe = 0;
813       POOLMEM *fname = get_pool_memory(PM_FNAME);
814       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
815
816       VOL_PARAMS *VolParams = NULL;
817       int VolCount;
818       char edt[50], ed1[50], ed2[50];
819
820       if (*fname == '|') {
821          got_pipe = 1;
822          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
823          fd = bpipe ? bpipe->wfd : NULL;
824       } else {
825          /* ***FIXME*** handle BASE */
826          fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
827       }
828       if (fd) {
829          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
830                     &VolParams);
831          if (VolCount == 0) {
832             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
833                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
834              if (jcr->SDJobFiles != 0) {
835                 jcr->setJobStatus(JS_ErrorTerminated);
836              }
837
838          }
839          /* Start output with when and who wrote it */
840          bstrftimes(edt, sizeof(edt), time(NULL));
841          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
842                  level_to_str(jcr->getJobLevel()), jcr->since);
843          for (int i=0; i < VolCount; i++) {
844             /* Write the record */
845             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
846             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
847             if (VolParams[i].Slot > 0) {
848                fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
849             }
850             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
851             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
852             fprintf(fd, "VolAddr=%s-%s\n", 
853                     edit_uint64(VolParams[i].StartAddr, ed1),
854                     edit_uint64(VolParams[i].EndAddr, ed2));
855             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
856                          VolParams[i].LastIndex);
857          }
858          if (VolParams) {
859             free(VolParams);
860          }
861          if (got_pipe) {
862             close_bpipe(bpipe);
863          } else {
864             fclose(fd);
865          }
866       } else {
867          berrno be;
868          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
869               "%s: ERR=%s\n"), fname, be.bstrerror());
870          jcr->setJobStatus(JS_ErrorTerminated);
871       }
872       free_pool_memory(fname);
873    }
874 }