]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
Tweak restart base
[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->incomplete) {
250       return true;
251    }
252
253    /* For incomplete Jobs, we add our own id */
254    if (jcr->incomplete) {
255       edit_int64(jcr->JobId, ed1);   
256       jobids.add(ed1);
257    }
258    
259    if (jcr->is_JobLevel(L_FULL)) {
260       /* On Full mode, if no previous base job, no accurate things */
261       if (!get_base_jobids(jcr, &jobids) && !jcr->incomplete) {
262          return true;
263       }
264       jcr->HasBase = true;
265       Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
266
267    } else {
268       /* For Incr/Diff level, we search for older jobs */
269       db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
270
271       /* We are in Incr/Diff, but no Full to build the accurate list... */
272       if (jobids.count == 0) {
273          Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
274          return false;  /* fail */
275       }
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->incomplete) {
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->incomplete) {
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_FATAL, 0, "%s", db_strerror(jcr->db));
356          return false;
357       }
358       jcr->JobFiles = job.value;
359       Dmsg1(100, "==== FI=%ld\n", jcr->JobFiles);
360       Mmsg(buf, "SELECT VolSessionId FROM Job WHERE JobId=%s", ed1);
361       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
362          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
363          return false;
364       }
365       jcr->VolSessionId = job.value;
366       Mmsg(buf, "SELECT VolSessionTime FROM Job WHERE JobId=%s", ed1);
367       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
368          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
369          return false;
370       }
371       jcr->VolSessionTime = job.value;
372       Dmsg4(100, "JobId=%s JobFiles=%ld VolSessionId=%ld VolSessionTime=%ld\n", ed1, 
373             jcr->JobFiles, jcr->VolSessionId, jcr->VolSessionTime);
374    }
375
376    /*
377     * Open a message channel connection with the Storage
378     * daemon. This is to let him know that our client
379     * will be contacting him for a backup  session.
380     *
381     */
382    Dmsg0(110, "Open connection with storage daemon\n");
383    jcr->setJobStatus(JS_WaitSD);
384    /*
385     * Start conversation with Storage daemon
386     */
387    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
388       return false;
389    }
390    /*
391     * Now start a job with the Storage daemon
392     */
393    if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
394       return false;
395    }
396
397    /*
398     * Start the job prior to starting the message thread below
399     * to avoid two threads from using the BSOCK structure at
400     * the same time.
401     */
402    if (!jcr->store_bsock->fsend("run")) {
403       return false;
404    }
405
406    /*
407     * Now start a Storage daemon message thread.  Note,
408     *   this thread is used to provide the catalog services
409     *   for the backup job, including inserting the attributes
410     *   into the catalog.  See catalog_update() in catreq.c
411     */
412    if (!start_storage_daemon_message_thread(jcr)) {
413       return false;
414    }
415    Dmsg0(150, "Storage daemon connection OK\n");
416
417    jcr->setJobStatus(JS_WaitFD);
418    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
419       goto bail_out;
420    }
421
422    jcr->setJobStatus(JS_Running);
423    fd = jcr->file_bsock;
424
425    if (!send_include_list(jcr)) {
426       goto bail_out;
427    }
428
429    if (!send_exclude_list(jcr)) {
430       goto bail_out;
431    }
432
433    if (!send_level_command(jcr)) {
434       goto bail_out;
435    }
436
437    /* TODO: See priority with bandwidth parameter */
438    if (jcr->job->max_bandwidth > 0) {
439       jcr->max_bandwidth = jcr->job->max_bandwidth;
440    } else if (jcr->client->max_bandwidth > 0) {
441       jcr->max_bandwidth = jcr->client->max_bandwidth;
442    }
443
444    if (jcr->max_bandwidth > 0) {
445       send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
446    }
447
448    /*
449     * send Storage daemon address to the File daemon
450     */
451    store = jcr->wstore;
452    if (store->SDDport == 0) {
453       store->SDDport = store->SDport;
454    }
455
456    /* TLS Requirement */
457    if (store->tls_enable) {
458       if (store->tls_require) {
459          tls_need = BNET_TLS_REQUIRED;
460       } else {
461          tls_need = BNET_TLS_OK;
462       }
463    }
464
465    fd->fsend(storaddr, store->address, store->SDDport, tls_need);
466    if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
467       goto bail_out;
468    }
469
470    if (!send_runscripts_commands(jcr)) {
471       goto bail_out;
472    }
473
474    /*    
475     * We re-update the job start record so that the start
476     *  time is set after the run before job.  This avoids 
477     *  that any files created by the run before job will
478     *  be saved twice.  They will be backed up in the current
479     *  job, but not in the next one unless they are changed.
480     *  Without this, they will be backed up in this job and
481     *  in the next job run because in that case, their date 
482     *   is after the start of this run.
483     */
484    jcr->start_time = time(NULL);
485    jcr->jr.StartTime = jcr->start_time;
486    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
487       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
488    }
489
490    /*
491     * If backup is in accurate mode, we send the list of
492     * all files to FD.
493     */
494    if (!send_accurate_current_files(jcr)) {
495       goto bail_out;     /* error */
496    }
497
498    /* Send backup command */
499    fd->fsend(backupcmd, jcr->JobFiles);
500    Dmsg1(100, ">filed: %s", fd->msg);
501    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
502       goto bail_out;
503    }
504
505    /* Pickup Job termination data */
506    stat = wait_for_job_termination(jcr);
507    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
508
509    if (jcr->HasBase && !db_commit_base_file_attributes_record(jcr, jcr->db))  {
510       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
511    }
512
513    if (stat == JS_Terminated) {
514       backup_cleanup(jcr, stat);
515       return true;
516    }     
517    return false;
518
519 /* Come here only after starting SD thread */
520 bail_out:
521    jcr->setJobStatus(JS_ErrorTerminated);
522    Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
523    /* Cancel SD */
524    wait_for_job_termination(jcr, FDConnectTimeout);
525    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
526    return false;
527 }
528
529
530 /*
531  * Here we wait for the File daemon to signal termination,
532  *   then we wait for the Storage daemon.  When both
533  *   are done, we return the job status.
534  * Also used by restore.c
535  */
536 int wait_for_job_termination(JCR *jcr, int timeout)
537 {
538    int32_t n = 0;
539    BSOCK *fd = jcr->file_bsock;
540    bool fd_ok = false;
541    uint32_t JobFiles, JobErrors;
542    uint32_t JobWarnings = 0;
543    uint64_t ReadBytes = 0;
544    uint64_t JobBytes = 0;
545    int VSS = 0;
546    int Encrypt = 0;
547    btimer_t *tid=NULL;
548
549    jcr->setJobStatus(JS_Running);
550
551    if (fd) {
552       if (timeout) {
553          tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
554       }
555       /* Wait for Client to terminate */
556       while ((n = bget_dirmsg(fd)) >= 0) {
557          if (!fd_ok && 
558              (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
559                      &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
560               sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
561                      &ReadBytes, &JobBytes, &JobErrors) == 5)) {
562             fd_ok = true;
563             jcr->setJobStatus(jcr->FDJobStatus);
564             Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
565          } else {
566             Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
567                  fd->msg);
568          }
569          if (job_canceled(jcr)) {
570             break;
571          }
572       }
573       if (tid) {
574          stop_bsock_timer(tid);
575       }
576
577       if (is_bnet_error(fd)) {
578          int i = 0;
579          Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
580               job_type_to_str(jcr->getJobType()), fd->bstrerror());
581          while (i++ < 10 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
582             bmicrosleep(3, 0);
583          }
584             
585       }
586       fd->signal(BNET_TERMINATE);   /* tell Client we are terminating */
587    }
588
589    /*
590     * Force cancel in SD if failing, but not for Incomplete jobs
591     *  so that we let the SD despool.
592     */
593    Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
594         jcr->JobStatus, jcr->SDJobStatus);
595    if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
596       Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
597            jcr->JobStatus, jcr->SDJobStatus);
598       cancel_storage_daemon_job(jcr);
599    }
600
601    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
602    wait_for_storage_daemon_termination(jcr);
603
604    /* Return values from FD */
605    if (fd_ok) {
606       jcr->JobFiles = JobFiles;
607       jcr->JobErrors += JobErrors;       /* Keep total errors */
608       jcr->ReadBytes = ReadBytes;
609       jcr->JobBytes = JobBytes;
610       jcr->JobWarnings = JobWarnings;
611       jcr->VSS = VSS;
612       jcr->Encrypt = Encrypt;
613    } else {
614       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
615    }
616
617 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
618 //   jcr->JobStatus, jcr->SDJobStatus);
619
620    /* Return the first error status we find Dir, FD, or SD */
621    if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
622       jcr->FDJobStatus = JS_ErrorTerminated;
623    }
624    if (jcr->JobStatus != JS_Terminated) {
625       return jcr->JobStatus;
626    }
627    if (jcr->FDJobStatus != JS_Terminated) {
628       return jcr->FDJobStatus;
629    }
630    return jcr->SDJobStatus;
631 }
632
633 /*
634  * Release resources allocated during backup.
635  */
636 void backup_cleanup(JCR *jcr, int TermCode)
637 {
638    char sdt[50], edt[50], schedt[50];
639    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
640    char ec6[30], ec7[30], ec8[30], elapsed[50];
641    char term_code[100], fd_term_msg[100], sd_term_msg[100];
642    const char *term_msg;
643    int msg_type = M_INFO;
644    MEDIA_DBR mr;
645    CLIENT_DBR cr;
646    double kbps, compression;
647    utime_t RunTime;
648    POOL_MEM base_info;
649
650    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
651       vbackup_cleanup(jcr, TermCode);
652       return;
653    }
654
655    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
656    memset(&mr, 0, sizeof(mr));
657    memset(&cr, 0, sizeof(cr));
658
659 #ifdef xxxx
660    if (jcr->getJobStatus() == JS_Terminated && 
661         (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
662       TermCode = JS_Warnings;
663    }
664 #endif
665          
666    update_job_end(jcr, TermCode);
667
668    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
669       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
670          db_strerror(jcr->db));
671       jcr->setJobStatus(JS_ErrorTerminated);
672    }
673
674    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
675    if (!db_get_client_record(jcr, jcr->db, &cr)) {
676       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
677          db_strerror(jcr->db));
678    }
679
680    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
681    if (!db_get_media_record(jcr, jcr->db, &mr)) {
682       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
683          mr.VolumeName, db_strerror(jcr->db));
684       jcr->setJobStatus(JS_ErrorTerminated);
685    }
686
687    update_bootstrap_file(jcr);
688
689    switch (jcr->JobStatus) {
690       case JS_Terminated:
691          if (jcr->JobErrors || jcr->SDErrors) {
692             term_msg = _("Backup OK -- with warnings");
693          } else {
694             term_msg = _("Backup OK");
695          }
696          break;
697       case JS_Incomplete:
698          term_msg = _("Backup failed -- incomplete");
699          break;
700       case JS_Warnings:
701          term_msg = _("Backup OK -- with warnings");
702          break;
703       case JS_FatalError:
704       case JS_ErrorTerminated:
705          term_msg = _("*** Backup Error ***");
706          msg_type = M_ERROR;          /* Generate error message */
707          if (jcr->store_bsock) {
708             jcr->store_bsock->signal(BNET_TERMINATE);
709             if (jcr->SD_msg_chan) {
710                pthread_cancel(jcr->SD_msg_chan);
711             }
712          }
713          break;
714       case JS_Canceled:
715          term_msg = _("Backup Canceled");
716          if (jcr->store_bsock) {
717             jcr->store_bsock->signal(BNET_TERMINATE);
718             if (jcr->SD_msg_chan) {
719                pthread_cancel(jcr->SD_msg_chan);
720             }
721          }
722          break;
723       default:
724          term_msg = term_code;
725          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
726          break;
727    }
728    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
729    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
730    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
731    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
732    if (RunTime <= 0) {
733       kbps = 0;
734    } else {
735       kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
736    }
737    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
738       /*
739        * Note, if the job has erred, most likely it did not write any
740        *  tape, so suppress this "error" message since in that case
741        *  it is normal.  Or look at it the other way, only for a
742        *  normal exit should we complain about this error.
743        */
744       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
745          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
746       }
747       jcr->VolumeName[0] = 0;         /* none */
748    }
749
750    if (jcr->ReadBytes == 0) {
751       bstrncpy(compress, "None", sizeof(compress));
752    } else {
753       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
754       if (compression < 0.5) {
755          bstrncpy(compress, "None", sizeof(compress));
756       } else {
757          bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
758       }
759    }
760    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
761    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
762
763    if (jcr->HasBase) {
764       Mmsg(base_info, "  Base files/Used files:  %lld/%lld (%.2f%%)\n",
765            jcr->nb_base_files, 
766            jcr->nb_base_files_used, 
767            jcr->nb_base_files_used*100.0/jcr->nb_base_files);
768    }
769 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
770
771    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
772 "  Build OS:               %s %s %s\n"
773 "  JobId:                  %d\n"
774 "  Job:                    %s\n"
775 "  Backup Level:           %s%s\n"
776 "  Client:                 \"%s\" %s\n"
777 "  FileSet:                \"%s\" %s\n"
778 "  Pool:                   \"%s\" (From %s)\n"
779 "  Catalog:                \"%s\" (From %s)\n"
780 "  Storage:                \"%s\" (From %s)\n"
781 "  Scheduled time:         %s\n"
782 "  Start time:             %s\n"
783 "  End time:               %s\n"
784 "  Elapsed time:           %s\n"
785 "  Priority:               %d\n"
786 "  FD Files Written:       %s\n"
787 "  SD Files Written:       %s\n"
788 "  FD Bytes Written:       %s (%sB)\n"
789 "  SD Bytes Written:       %s (%sB)\n"
790 "  Rate:                   %.1f KB/s\n"
791 "  Software Compression:   %s\n"
792 "%s"                                         /* Basefile info */
793 "  VSS:                    %s\n"
794 "  Encryption:             %s\n"
795 "  Accurate:               %s\n"
796 "  Volume name(s):         %s\n"
797 "  Volume Session Id:      %d\n"
798 "  Volume Session Time:    %d\n"
799 "  Last Volume Bytes:      %s (%sB)\n"
800 "  Non-fatal FD errors:    %d\n"
801 "  SD Errors:              %d\n"
802 "  FD termination status:  %s\n"
803 "  SD termination status:  %s\n"
804 "  Termination:            %s\n\n"),
805         BACULA, my_name, VERSION, LSMDATE,
806         HOST_OS, DISTNAME, DISTVER,
807         jcr->jr.JobId,
808         jcr->jr.Job,
809         level_to_str(jcr->getJobLevel()), jcr->since,
810         jcr->client->name(), cr.Uname,
811         jcr->fileset->name(), jcr->FSCreateTime,
812         jcr->pool->name(), jcr->pool_source,
813         jcr->catalog->name(), jcr->catalog_source,
814         jcr->wstore->name(), jcr->wstore_source,
815         schedt,
816         sdt,
817         edt,
818         edit_utime(RunTime, elapsed, sizeof(elapsed)),
819         jcr->JobPriority,
820         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
821         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
822         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
823         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
824         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
825         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
826         kbps,
827         compress,
828         base_info.c_str(),
829         jcr->VSS?_("yes"):_("no"),
830         jcr->Encrypt?_("yes"):_("no"),
831         jcr->accurate?_("yes"):_("no"),
832         jcr->VolumeName,
833         jcr->VolSessionId,
834         jcr->VolSessionTime,
835         edit_uint64_with_commas(mr.VolBytes, ec7),
836         edit_uint64_with_suffix(mr.VolBytes, ec8),
837         jcr->JobErrors,
838         jcr->SDErrors,
839         fd_term_msg,
840         sd_term_msg,
841         term_msg);
842
843    Dmsg0(100, "Leave backup_cleanup()\n");
844 }
845
846 void update_bootstrap_file(JCR *jcr)
847 {
848    /* Now update the bootstrap file if any */
849    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
850        jcr->job->WriteBootstrap) {
851       FILE *fd;
852       BPIPE *bpipe = NULL;
853       int got_pipe = 0;
854       POOLMEM *fname = get_pool_memory(PM_FNAME);
855       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
856
857       VOL_PARAMS *VolParams = NULL;
858       int VolCount;
859       char edt[50], ed1[50], ed2[50];
860
861       if (*fname == '|') {
862          got_pipe = 1;
863          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
864          fd = bpipe ? bpipe->wfd : NULL;
865       } else {
866          /* ***FIXME*** handle BASE */
867          fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
868       }
869       if (fd) {
870          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
871                     &VolParams);
872          if (VolCount == 0) {
873             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
874                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
875              if (jcr->SDJobFiles != 0) {
876                 jcr->setJobStatus(JS_ErrorTerminated);
877              }
878
879          }
880          /* Start output with when and who wrote it */
881          bstrftimes(edt, sizeof(edt), time(NULL));
882          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
883                  level_to_str(jcr->getJobLevel()), jcr->since);
884          for (int i=0; i < VolCount; i++) {
885             /* Write the record */
886             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
887             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
888             if (VolParams[i].Slot > 0) {
889                fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
890             }
891             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
892             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
893             fprintf(fd, "VolAddr=%s-%s\n", 
894                     edit_uint64(VolParams[i].StartAddr, ed1),
895                     edit_uint64(VolParams[i].EndAddr, ed2));
896             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
897                          VolParams[i].LastIndex);
898          }
899          if (VolParams) {
900             free(VolParams);
901          }
902          if (got_pipe) {
903             close_bpipe(bpipe);
904          } else {
905             fclose(fd);
906          }
907       } else {
908          berrno be;
909          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
910               "%s: ERR=%s\n"), fname, be.bstrerror());
911          jcr->setJobStatus(JS_ErrorTerminated);
912       }
913       free_pool_memory(fname);
914    }
915 }