]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
Disable usage of JS_Warning waiting for a more complete implementation
[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]=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    if (!send_runscripts_commands(jcr)) {
422       goto bail_out;
423    }
424
425    /*    
426     * We re-update the job start record so that the start
427     *  time is set after the run before job.  This avoids 
428     *  that any files created by the run before job will
429     *  be saved twice.  They will be backed up in the current
430     *  job, but not in the next one unless they are changed.
431     *  Without this, they will be backed up in this job and
432     *  in the next job run because in that case, their date 
433     *   is after the start of this run.
434     */
435    jcr->start_time = time(NULL);
436    jcr->jr.StartTime = jcr->start_time;
437    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
438       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
439    }
440
441    /*
442     * If backup is in accurate mode, we send the list of
443     * all files to FD.
444     */
445    if (!send_accurate_current_files(jcr)) {
446       goto bail_out;     /* error */
447    }
448
449    /* Send backup command */
450    fd->fsend(backupcmd, jcr->JobFiles);
451    Dmsg1(100, ">filed: %s", fd->msg);
452    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
453       goto bail_out;
454    }
455
456    /* Pickup Job termination data */
457    stat = wait_for_job_termination(jcr);
458    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
459
460    if (jcr->HasBase && !db_commit_base_file_attributes_record(jcr, jcr->db))  {
461       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
462    }
463
464    if (stat == JS_Terminated) {
465       backup_cleanup(jcr, stat);
466       return true;
467    }     
468    return false;
469
470 /* Come here only after starting SD thread */
471 bail_out:
472    jcr->setJobStatus(JS_ErrorTerminated);
473    Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
474    /* Cancel SD */
475    wait_for_job_termination(jcr, FDConnectTimeout);
476    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
477    return false;
478 }
479
480
481 /*
482  * Here we wait for the File daemon to signal termination,
483  *   then we wait for the Storage daemon.  When both
484  *   are done, we return the job status.
485  * Also used by restore.c
486  */
487 int wait_for_job_termination(JCR *jcr, int timeout)
488 {
489    int32_t n = 0;
490    BSOCK *fd = jcr->file_bsock;
491    bool fd_ok = false;
492    uint32_t JobFiles, JobErrors;
493    uint32_t JobWarnings = 0;
494    uint64_t ReadBytes = 0;
495    uint64_t JobBytes = 0;
496    int VSS = 0;
497    int Encrypt = 0;
498    btimer_t *tid=NULL;
499
500    jcr->setJobStatus(JS_Running);
501
502    if (fd) {
503       if (timeout) {
504          tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
505       }
506       /* Wait for Client to terminate */
507       while ((n = bget_dirmsg(fd)) >= 0) {
508          if (!fd_ok && 
509              (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
510                      &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
511               sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
512                      &ReadBytes, &JobBytes, &JobErrors) == 5)) {
513             fd_ok = true;
514             jcr->setJobStatus(jcr->FDJobStatus);
515             Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
516          } else {
517             Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
518                  fd->msg);
519          }
520          if (job_canceled(jcr)) {
521             break;
522          }
523       }
524       if (tid) {
525          stop_bsock_timer(tid);
526       }
527
528       if (is_bnet_error(fd)) {
529          int i = 0;
530          Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
531               job_type_to_str(jcr->getJobType()), fd->bstrerror());
532          while (i++ < 10 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
533             bmicrosleep(3, 0);
534          }
535             
536       }
537       fd->signal(BNET_TERMINATE);   /* tell Client we are terminating */
538    }
539
540    /*
541     * Force cancel in SD if failing, but not for Incomplete jobs
542     *  so that we let the SD despool.
543     */
544    Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
545         jcr->JobStatus, jcr->SDJobStatus);
546    if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
547       Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
548            jcr->JobStatus, jcr->SDJobStatus);
549       cancel_storage_daemon_job(jcr);
550    }
551
552    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
553    wait_for_storage_daemon_termination(jcr);
554
555    /* Return values from FD */
556    if (fd_ok) {
557       jcr->JobFiles = JobFiles;
558       jcr->JobErrors += JobErrors;       /* Keep total errors */
559       jcr->ReadBytes = ReadBytes;
560       jcr->JobBytes = JobBytes;
561       jcr->JobWarnings = JobWarnings;
562       jcr->VSS = VSS;
563       jcr->Encrypt = Encrypt;
564    } else {
565       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
566    }
567
568 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
569 //   jcr->JobStatus, jcr->SDJobStatus);
570
571    /* Return the first error status we find Dir, FD, or SD */
572    if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
573       jcr->FDJobStatus = JS_ErrorTerminated;
574    }
575    if (jcr->JobStatus != JS_Terminated) {
576       return jcr->JobStatus;
577    }
578    if (jcr->FDJobStatus != JS_Terminated) {
579       return jcr->FDJobStatus;
580    }
581    return jcr->SDJobStatus;
582 }
583
584 /*
585  * Release resources allocated during backup.
586  */
587 void backup_cleanup(JCR *jcr, int TermCode)
588 {
589    char sdt[50], edt[50], schedt[50];
590    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
591    char ec6[30], ec7[30], ec8[30], elapsed[50];
592    char term_code[100], fd_term_msg[100], sd_term_msg[100];
593    const char *term_msg;
594    int msg_type = M_INFO;
595    MEDIA_DBR mr;
596    CLIENT_DBR cr;
597    double kbps, compression;
598    utime_t RunTime;
599    POOL_MEM base_info;
600
601    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
602       vbackup_cleanup(jcr, TermCode);
603       return;
604    }
605
606    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
607    memset(&mr, 0, sizeof(mr));
608    memset(&cr, 0, sizeof(cr));
609
610 #ifdef xxxx
611    /* The current implementation of the JS_Warning status is not
612     * completed. SQL part looks to be ok, but the code is using
613     * JS_Terminated almost everywhere instead of (JS_Terminated || JS_Warning)
614     * as we do with is_canceled()
615     */
616    if (jcr->getJobStatus() == JS_Terminated && 
617         (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
618       TermCode = JS_Warnings;
619    }
620 #endif
621          
622    update_job_end(jcr, TermCode);
623
624    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
625       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
626          db_strerror(jcr->db));
627       jcr->setJobStatus(JS_ErrorTerminated);
628    }
629
630    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
631    if (!db_get_client_record(jcr, jcr->db, &cr)) {
632       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
633          db_strerror(jcr->db));
634    }
635
636    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
637    if (!db_get_media_record(jcr, jcr->db, &mr)) {
638       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
639          mr.VolumeName, db_strerror(jcr->db));
640       jcr->setJobStatus(JS_ErrorTerminated);
641    }
642
643    update_bootstrap_file(jcr);
644
645    switch (jcr->JobStatus) {
646       case JS_Terminated:
647          if (jcr->JobErrors || jcr->SDErrors) {
648             term_msg = _("Backup OK -- with warnings");
649          } else {
650             term_msg = _("Backup OK");
651          }
652          break;
653       case JS_Incomplete:
654          term_msg = _("Backup failed -- incomplete");
655          break;
656       case JS_Warnings:
657          term_msg = _("Backup OK -- with warnings");
658          break;
659       case JS_FatalError:
660       case JS_ErrorTerminated:
661          term_msg = _("*** Backup Error ***");
662          msg_type = M_ERROR;          /* Generate error message */
663          if (jcr->store_bsock) {
664             jcr->store_bsock->signal(BNET_TERMINATE);
665             if (jcr->SD_msg_chan) {
666                pthread_cancel(jcr->SD_msg_chan);
667             }
668          }
669          break;
670       case JS_Canceled:
671          term_msg = _("Backup Canceled");
672          if (jcr->store_bsock) {
673             jcr->store_bsock->signal(BNET_TERMINATE);
674             if (jcr->SD_msg_chan) {
675                pthread_cancel(jcr->SD_msg_chan);
676             }
677          }
678          break;
679       default:
680          term_msg = term_code;
681          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
682          break;
683    }
684    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
685    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
686    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
687    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
688    if (RunTime <= 0) {
689       kbps = 0;
690    } else {
691       kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
692    }
693    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
694       /*
695        * Note, if the job has erred, most likely it did not write any
696        *  tape, so suppress this "error" message since in that case
697        *  it is normal.  Or look at it the other way, only for a
698        *  normal exit should we complain about this error.
699        */
700       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
701          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
702       }
703       jcr->VolumeName[0] = 0;         /* none */
704    }
705
706    if (jcr->ReadBytes == 0) {
707       bstrncpy(compress, "None", sizeof(compress));
708    } else {
709       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
710       if (compression < 0.5) {
711          bstrncpy(compress, "None", sizeof(compress));
712       } else {
713          bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
714       }
715    }
716    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
717    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
718
719    if (jcr->HasBase) {
720       Mmsg(base_info, "  Base files/Used files:  %lld/%lld (%.2f%%)\n",
721            jcr->nb_base_files, 
722            jcr->nb_base_files_used, 
723            jcr->nb_base_files_used*100.0/jcr->nb_base_files);
724    }
725 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
726
727    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
728 "  Build OS:               %s %s %s\n"
729 "  JobId:                  %d\n"
730 "  Job:                    %s\n"
731 "  Backup Level:           %s%s\n"
732 "  Client:                 \"%s\" %s\n"
733 "  FileSet:                \"%s\" %s\n"
734 "  Pool:                   \"%s\" (From %s)\n"
735 "  Catalog:                \"%s\" (From %s)\n"
736 "  Storage:                \"%s\" (From %s)\n"
737 "  Scheduled time:         %s\n"
738 "  Start time:             %s\n"
739 "  End time:               %s\n"
740 "  Elapsed time:           %s\n"
741 "  Priority:               %d\n"
742 "  FD Files Written:       %s\n"
743 "  SD Files Written:       %s\n"
744 "  FD Bytes Written:       %s (%sB)\n"
745 "  SD Bytes Written:       %s (%sB)\n"
746 "  Rate:                   %.1f KB/s\n"
747 "  Software Compression:   %s\n"
748 "%s"                                         /* Basefile info */
749 "  VSS:                    %s\n"
750 "  Encryption:             %s\n"
751 "  Accurate:               %s\n"
752 "  Volume name(s):         %s\n"
753 "  Volume Session Id:      %d\n"
754 "  Volume Session Time:    %d\n"
755 "  Last Volume Bytes:      %s (%sB)\n"
756 "  Non-fatal FD errors:    %d\n"
757 "  SD Errors:              %d\n"
758 "  FD termination status:  %s\n"
759 "  SD termination status:  %s\n"
760 "  Termination:            %s\n\n"),
761         BACULA, my_name, VERSION, LSMDATE,
762         HOST_OS, DISTNAME, DISTVER,
763         jcr->jr.JobId,
764         jcr->jr.Job,
765         level_to_str(jcr->getJobLevel()), jcr->since,
766         jcr->client->name(), cr.Uname,
767         jcr->fileset->name(), jcr->FSCreateTime,
768         jcr->pool->name(), jcr->pool_source,
769         jcr->catalog->name(), jcr->catalog_source,
770         jcr->wstore->name(), jcr->wstore_source,
771         schedt,
772         sdt,
773         edt,
774         edit_utime(RunTime, elapsed, sizeof(elapsed)),
775         jcr->JobPriority,
776         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
777         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
778         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
779         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
780         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
781         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
782         kbps,
783         compress,
784         base_info.c_str(),
785         jcr->VSS?_("yes"):_("no"),
786         jcr->Encrypt?_("yes"):_("no"),
787         jcr->accurate?_("yes"):_("no"),
788         jcr->VolumeName,
789         jcr->VolSessionId,
790         jcr->VolSessionTime,
791         edit_uint64_with_commas(mr.VolBytes, ec7),
792         edit_uint64_with_suffix(mr.VolBytes, ec8),
793         jcr->JobErrors,
794         jcr->SDErrors,
795         fd_term_msg,
796         sd_term_msg,
797         term_msg);
798
799    Dmsg0(100, "Leave backup_cleanup()\n");
800 }
801
802 void update_bootstrap_file(JCR *jcr)
803 {
804    /* Now update the bootstrap file if any */
805    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
806        jcr->job->WriteBootstrap) {
807       FILE *fd;
808       BPIPE *bpipe = NULL;
809       int got_pipe = 0;
810       POOLMEM *fname = get_pool_memory(PM_FNAME);
811       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
812
813       VOL_PARAMS *VolParams = NULL;
814       int VolCount;
815       char edt[50], ed1[50], ed2[50];
816
817       if (*fname == '|') {
818          got_pipe = 1;
819          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
820          fd = bpipe ? bpipe->wfd : NULL;
821       } else {
822          /* ***FIXME*** handle BASE */
823          fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
824       }
825       if (fd) {
826          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
827                     &VolParams);
828          if (VolCount == 0) {
829             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
830                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
831              if (jcr->SDJobFiles != 0) {
832                 jcr->setJobStatus(JS_ErrorTerminated);
833              }
834
835          }
836          /* Start output with when and who wrote it */
837          bstrftimes(edt, sizeof(edt), time(NULL));
838          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
839                  level_to_str(jcr->getJobLevel()), jcr->since);
840          for (int i=0; i < VolCount; i++) {
841             /* Write the record */
842             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
843             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
844             if (VolParams[i].Slot > 0) {
845                fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
846             }
847             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
848             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
849             fprintf(fd, "VolAddr=%s-%s\n", 
850                     edit_uint64(VolParams[i].StartAddr, ed1),
851                     edit_uint64(VolParams[i].EndAddr, ed2));
852             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
853                          VolParams[i].LastIndex);
854          }
855          if (VolParams) {
856             free(VolParams);
857          }
858          if (got_pipe) {
859             close_bpipe(bpipe);
860          } else {
861             fclose(fd);
862          }
863       } else {
864          berrno be;
865          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
866               "%s: ERR=%s\n"), fname, be.bstrerror());
867          jcr->setJobStatus(JS_ErrorTerminated);
868       }
869       free_pool_memory(fname);
870    }
871 }