]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
Remove ifdeffing that turned off JS_Warning status -- must have been missed test...
[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    if (jcr->getJobStatus() == JS_Terminated && 
611         (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
612       TermCode = JS_Warnings;
613    }
614          
615    update_job_end(jcr, TermCode);
616
617    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
618       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
619          db_strerror(jcr->db));
620       jcr->setJobStatus(JS_ErrorTerminated);
621    }
622
623    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
624    if (!db_get_client_record(jcr, jcr->db, &cr)) {
625       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
626          db_strerror(jcr->db));
627    }
628
629    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
630    if (!db_get_media_record(jcr, jcr->db, &mr)) {
631       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
632          mr.VolumeName, db_strerror(jcr->db));
633       jcr->setJobStatus(JS_ErrorTerminated);
634    }
635
636    update_bootstrap_file(jcr);
637
638    switch (jcr->JobStatus) {
639       case JS_Terminated:
640          if (jcr->JobErrors || jcr->SDErrors) {
641             term_msg = _("Backup OK -- with warnings");
642          } else {
643             term_msg = _("Backup OK");
644          }
645          break;
646       case JS_Incomplete:
647          term_msg = _("Backup failed -- incomplete");
648          break;
649       case JS_Warnings:
650          term_msg = _("Backup OK -- with warnings");
651          break;
652       case JS_FatalError:
653       case JS_ErrorTerminated:
654          term_msg = _("*** Backup Error ***");
655          msg_type = M_ERROR;          /* Generate error message */
656          if (jcr->store_bsock) {
657             jcr->store_bsock->signal(BNET_TERMINATE);
658             if (jcr->SD_msg_chan) {
659                pthread_cancel(jcr->SD_msg_chan);
660             }
661          }
662          break;
663       case JS_Canceled:
664          term_msg = _("Backup Canceled");
665          if (jcr->store_bsock) {
666             jcr->store_bsock->signal(BNET_TERMINATE);
667             if (jcr->SD_msg_chan) {
668                pthread_cancel(jcr->SD_msg_chan);
669             }
670          }
671          break;
672       default:
673          term_msg = term_code;
674          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
675          break;
676    }
677    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
678    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
679    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
680    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
681    if (RunTime <= 0) {
682       kbps = 0;
683    } else {
684       kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
685    }
686    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
687       /*
688        * Note, if the job has erred, most likely it did not write any
689        *  tape, so suppress this "error" message since in that case
690        *  it is normal.  Or look at it the other way, only for a
691        *  normal exit should we complain about this error.
692        */
693       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
694          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
695       }
696       jcr->VolumeName[0] = 0;         /* none */
697    }
698
699    if (jcr->ReadBytes == 0) {
700       bstrncpy(compress, "None", sizeof(compress));
701    } else {
702       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
703       if (compression < 0.5) {
704          bstrncpy(compress, "None", sizeof(compress));
705       } else {
706          bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
707       }
708    }
709    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
710    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
711
712    if (jcr->HasBase) {
713       Mmsg(base_info, "  Base files/Used files:  %lld/%lld (%.2f%%)\n",
714            jcr->nb_base_files, 
715            jcr->nb_base_files_used, 
716            jcr->nb_base_files_used*100.0/jcr->nb_base_files);
717    }
718 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
719
720    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
721 "  Build OS:               %s %s %s\n"
722 "  JobId:                  %d\n"
723 "  Job:                    %s\n"
724 "  Backup Level:           %s%s\n"
725 "  Client:                 \"%s\" %s\n"
726 "  FileSet:                \"%s\" %s\n"
727 "  Pool:                   \"%s\" (From %s)\n"
728 "  Catalog:                \"%s\" (From %s)\n"
729 "  Storage:                \"%s\" (From %s)\n"
730 "  Scheduled time:         %s\n"
731 "  Start time:             %s\n"
732 "  End time:               %s\n"
733 "  Elapsed time:           %s\n"
734 "  Priority:               %d\n"
735 "  FD Files Written:       %s\n"
736 "  SD Files Written:       %s\n"
737 "  FD Bytes Written:       %s (%sB)\n"
738 "  SD Bytes Written:       %s (%sB)\n"
739 "  Rate:                   %.1f KB/s\n"
740 "  Software Compression:   %s\n"
741 "%s"                                         /* Basefile info */
742 "  VSS:                    %s\n"
743 "  Encryption:             %s\n"
744 "  Accurate:               %s\n"
745 "  Volume name(s):         %s\n"
746 "  Volume Session Id:      %d\n"
747 "  Volume Session Time:    %d\n"
748 "  Last Volume Bytes:      %s (%sB)\n"
749 "  Non-fatal FD errors:    %d\n"
750 "  SD Errors:              %d\n"
751 "  FD termination status:  %s\n"
752 "  SD termination status:  %s\n"
753 "  Termination:            %s\n\n"),
754         BACULA, my_name, VERSION, LSMDATE,
755         HOST_OS, DISTNAME, DISTVER,
756         jcr->jr.JobId,
757         jcr->jr.Job,
758         level_to_str(jcr->getJobLevel()), jcr->since,
759         jcr->client->name(), cr.Uname,
760         jcr->fileset->name(), jcr->FSCreateTime,
761         jcr->pool->name(), jcr->pool_source,
762         jcr->catalog->name(), jcr->catalog_source,
763         jcr->wstore->name(), jcr->wstore_source,
764         schedt,
765         sdt,
766         edt,
767         edit_utime(RunTime, elapsed, sizeof(elapsed)),
768         jcr->JobPriority,
769         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
770         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
771         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
772         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
773         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
774         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
775         kbps,
776         compress,
777         base_info.c_str(),
778         jcr->VSS?_("yes"):_("no"),
779         jcr->Encrypt?_("yes"):_("no"),
780         jcr->accurate?_("yes"):_("no"),
781         jcr->VolumeName,
782         jcr->VolSessionId,
783         jcr->VolSessionTime,
784         edit_uint64_with_commas(mr.VolBytes, ec7),
785         edit_uint64_with_suffix(mr.VolBytes, ec8),
786         jcr->JobErrors,
787         jcr->SDErrors,
788         fd_term_msg,
789         sd_term_msg,
790         term_msg);
791
792    Dmsg0(100, "Leave backup_cleanup()\n");
793 }
794
795 void update_bootstrap_file(JCR *jcr)
796 {
797    /* Now update the bootstrap file if any */
798    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
799        jcr->job->WriteBootstrap) {
800       FILE *fd;
801       BPIPE *bpipe = NULL;
802       int got_pipe = 0;
803       POOLMEM *fname = get_pool_memory(PM_FNAME);
804       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
805
806       VOL_PARAMS *VolParams = NULL;
807       int VolCount;
808       char edt[50], ed1[50], ed2[50];
809
810       if (*fname == '|') {
811          got_pipe = 1;
812          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
813          fd = bpipe ? bpipe->wfd : NULL;
814       } else {
815          /* ***FIXME*** handle BASE */
816          fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
817       }
818       if (fd) {
819          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
820                     &VolParams);
821          if (VolCount == 0) {
822             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
823                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
824              if (jcr->SDJobFiles != 0) {
825                 jcr->setJobStatus(JS_ErrorTerminated);
826              }
827
828          }
829          /* Start output with when and who wrote it */
830          bstrftimes(edt, sizeof(edt), time(NULL));
831          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
832                  level_to_str(jcr->getJobLevel()), jcr->since);
833          for (int i=0; i < VolCount; i++) {
834             /* Write the record */
835             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
836             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
837             if (VolParams[i].Slot > 0) {
838                fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
839             }
840             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
841             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
842             fprintf(fd, "VolAddr=%s-%s\n", 
843                     edit_uint64(VolParams[i].StartAddr, ed1),
844                     edit_uint64(VolParams[i].EndAddr, ed2));
845             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
846                          VolParams[i].LastIndex);
847          }
848          if (VolParams) {
849             free(VolParams);
850          }
851          if (got_pipe) {
852             close_bpipe(bpipe);
853          } else {
854             fclose(fd);
855          }
856       } else {
857          berrno be;
858          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
859               "%s: ERR=%s\n"), fname, be.bstrerror());
860          jcr->setJobStatus(JS_ErrorTerminated);
861       }
862       free_pool_memory(fname);
863    }
864 }