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