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