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