]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
Add replace=xxx on restore command line
[bacula/bacula] / bacula / src / dird / backup.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 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  *   Version $Id$
42  */
43
44 #include "bacula.h"
45 #include "dird.h"
46 #include "ua.h"
47
48 /* Commands sent to File daemon */
49 static char backupcmd[] = "backup\n";
50 static char storaddr[]  = "storage address=%s port=%d ssl=%d\n";
51
52 /* Responses received from File daemon */
53 static char OKbackup[]   = "2000 OK backup\n";
54 static char OKstore[]    = "2000 OK storage\n";
55 static char EndJob[]     = "2800 End Job TermCode=%d JobFiles=%u "
56                            "ReadBytes=%llu JobBytes=%llu Errors=%u "  
57                            "VSS=%d Encrypt=%d\n";
58 /* Pre 1.39.29 (04Dec06) EndJob */
59 static char OldEndJob[]  = "2800 End Job TermCode=%d JobFiles=%u "
60                            "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
61 /* 
62  * Called here before the job is run to do the job
63  *   specific setup.
64  */
65 bool do_backup_init(JCR *jcr)
66 {
67
68    if (jcr->getJobLevel() == L_VIRTUAL_FULL) {
69       return do_vbackup_init(jcr);
70    }
71    free_rstorage(jcr);                   /* we don't read so release */
72
73    if (!get_or_create_fileset_record(jcr)) {
74       return false;
75    }
76
77    /* 
78     * Get definitive Job level and since time
79     */
80    get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
81
82    apply_pool_overrides(jcr);
83
84    if (!allow_duplicate_job(jcr)) {
85       return false;
86    }
87
88    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
89    if (jcr->jr.PoolId == 0) {
90       return false;
91    }
92
93    /* If pool storage specified, use it instead of job storage */
94    copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
95
96    if (!jcr->wstorage) {
97       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
98       return false;
99    }
100
101    create_clones(jcr);                /* run any clone jobs */
102
103    return true;
104 }
105
106 /* Take all base jobs from job resource and find the
107  * last L_BASE jobid.
108  */
109 static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids)
110 {
111    JOB_DBR jr;
112    JOB *job;
113    JobId_t id;
114    char str_jobid[50];
115
116    if (!jcr->job->base) {
117       return false;             /* no base job, stop accurate */
118    }
119
120    memset(&jr, 0, sizeof(JOB_DBR));
121    jr.StartTime = jcr->jr.StartTime;
122
123    foreach_alist(job, jcr->job->base) {
124       bstrncpy(jr.Name, job->name(), sizeof(jr.Name));
125       db_get_base_jobid(jcr, jcr->db, &jr, &id);
126
127       if (id) {
128          if (jobids->count) {
129             pm_strcat(jobids->list, ",");
130          }
131          pm_strcat(jobids->list, edit_uint64(id, str_jobid));
132          jobids->count++;
133       }
134    }
135
136    return jobids->count > 0;
137 }
138
139 /*
140  * Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
141  *      row[0]=Path, row[1]=Filename, row[2]=FileIndex
142  *      row[3]=JobId row[4]=LStat row[5]=MarkId row[6]=MD5
143  */
144 static int accurate_list_handler(void *ctx, int num_fields, char **row)
145 {
146    JCR *jcr = (JCR *)ctx;
147
148    if (job_canceled(jcr)) {
149       return 1;
150    }
151    
152    if (row[2][0] == '0') {           /* discard when file_index == 0 */
153       return 0;
154    }
155
156    /* sending with checksum */
157    if (jcr->use_accurate_chksum 
158        && num_fields == 7 
159        && row[6][0] /* skip checksum = '0' */
160        && row[6][1])
161    { 
162       jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s", 
163                              row[0], row[1], 0, row[4], 0, row[6], 0, row[5]); 
164    } else {
165       jcr->file_bsock->fsend("%s%s%c%s%c%c%s", 
166                              row[0], row[1], 0, row[4], 0, 0, row[5]); 
167    }
168    return 0;
169 }
170
171 /* In this procedure, we check if the current fileset is using checksum
172  * FileSet-> Include-> Options-> Accurate/Verify/BaseJob=checksum
173  * This procedure uses jcr->HasBase, so it must be call after the initialization
174  */
175 static bool is_checksum_needed_by_fileset(JCR *jcr)
176 {
177    FILESET *f;
178    INCEXE *inc;
179    FOPTS *fopts;
180    bool in_block=false;
181    bool have_basejob_option=false;
182    if (!jcr->job || !jcr->job->fileset) {
183       return false;
184    }
185
186    f = jcr->job->fileset;
187    
188    for (int i=0; i < f->num_includes; i++) { /* Parse all Include {} */
189       inc = f->include_items[i];
190       
191       for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
192          fopts = inc->opts_list[j];
193          
194          for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
195             switch (*k) {
196             case 'V':           /* verify */
197                in_block = (jcr->getJobType() == JT_VERIFY); /* not used now */
198                break;
199             case 'J':           /* Basejob keyword */
200                have_basejob_option = in_block = jcr->HasBase;
201                break;
202             case 'C':           /* Accurate keyword */
203                in_block = (jcr->getJobLevel() != L_FULL);
204                break;
205             case ':':           /* End of keyword */
206                in_block = false;
207                break;
208             case '5':           /* MD5  */
209             case '1':           /* SHA1 */
210                if (in_block) {
211                   Dmsg0(50, "Checksum will be sent to FD\n");
212                   return true;
213                }
214                break;
215             default:
216                break;
217             }
218          }
219       }
220    }
221
222    /* By default for BaseJobs, we send the checksum */
223    if (!have_basejob_option && jcr->HasBase) {
224       return true;
225    }
226    
227    Dmsg0(50, "Checksum will be sent to FD\n");
228    return false;
229 }
230
231 /*
232  * Send current file list to FD
233  *    DIR -> FD : accurate files=xxxx
234  *    DIR -> FD : /path/to/file\0Lstat\0MD5\0Delta
235  *    DIR -> FD : /path/to/dir/\0Lstat\0MD5\0Delta
236  *    ...
237  *    DIR -> FD : EOD
238  */
239 bool send_accurate_current_files(JCR *jcr)
240 {
241    POOL_MEM buf;
242    bool ret=true;
243    db_list_ctx jobids;
244    db_list_ctx nb;
245
246    if (!jcr->accurate || job_canceled(jcr)) {
247       return true;
248    }
249    /* In base level, no previous job is used */
250    if (jcr->getJobLevel() == L_BASE) {
251       return true;
252    }
253    
254    if (jcr->getJobLevel() == L_FULL) {
255       /* On Full mode, if no previous base job, no accurate things */
256       if (!get_base_jobids(jcr, &jobids)) {
257          goto bail_out;
258       }
259       jcr->HasBase = true;
260       Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
261
262    } else {
263       /* For Incr/Diff level, we search for older jobs */
264       db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
265
266       /* We are in Incr/Diff, but no Full to build the accurate list... */
267       if (jobids.count == 0) {
268          ret=false;
269          Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
270          goto bail_out;
271       }
272    }
273
274    /* Don't send and store the checksum if fileset doesn't require it */
275    jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
276
277    if (jcr->JobId) {            /* display the message only for real jobs */
278       Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
279    }
280
281    /* to be able to allocate the right size for htable */
282    Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
283    db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
284    Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
285    jcr->file_bsock->fsend("accurate files=%s\n", nb.list); 
286
287    if (!db_open_batch_connexion(jcr, jcr->db)) {
288       Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
289       return false;
290    }
291    
292    if (jcr->HasBase) {
293       jcr->nb_base_files = str_to_int64(nb.list);
294       db_create_base_file_list(jcr, jcr->db, jobids.list);
295       db_get_base_file_list(jcr, jcr->db, jcr->use_accurate_chksum,
296                             accurate_list_handler, (void *)jcr);
297
298    } else {
299       db_get_file_list(jcr, jcr->db_batch,
300                        jobids.list, jcr->use_accurate_chksum, false /* no delta */,
301                        accurate_list_handler, (void *)jcr);
302    } 
303
304    /* TODO: close the batch connexion ? (can be used very soon) */
305
306    jcr->file_bsock->signal(BNET_EOD);
307
308 bail_out:
309    return ret;
310 }
311
312 /*
313  * Do a backup of the specified FileSet
314  *
315  *  Returns:  false on failure
316  *            true  on success
317  */
318 bool do_backup(JCR *jcr)
319 {
320    int stat;
321    int tls_need = BNET_TLS_NONE;
322    BSOCK   *fd;
323    STORE *store;
324    char ed1[100];
325
326    if (jcr->getJobLevel() == L_VIRTUAL_FULL) {
327       return do_vbackup(jcr);
328    }
329
330    /* Print Job Start message */
331    Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
332         edit_uint64(jcr->JobId, ed1), jcr->Job);
333
334    jcr->setJobStatus(JS_Running);
335    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
336    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
337       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
338       return false;
339    }
340
341    /*
342     * Open a message channel connection with the Storage
343     * daemon. This is to let him know that our client
344     * will be contacting him for a backup  session.
345     *
346     */
347    Dmsg0(110, "Open connection with storage daemon\n");
348    jcr->setJobStatus(JS_WaitSD);
349    /*
350     * Start conversation with Storage daemon
351     */
352    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
353       return false;
354    }
355    /*
356     * Now start a job with the Storage daemon
357     */
358    if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
359       return false;
360    }
361
362    /*
363     * Start the job prior to starting the message thread below
364     * to avoid two threads from using the BSOCK structure at
365     * the same time.
366     */
367    if (!bnet_fsend(jcr->store_bsock, "run")) {
368       return false;
369    }
370
371    /*
372     * Now start a Storage daemon message thread.  Note,
373     *   this thread is used to provide the catalog services
374     *   for the backup job, including inserting the attributes
375     *   into the catalog.  See catalog_update() in catreq.c
376     */
377    if (!start_storage_daemon_message_thread(jcr)) {
378       return false;
379    }
380    Dmsg0(150, "Storage daemon connection OK\n");
381
382    jcr->setJobStatus(JS_WaitFD);
383    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
384       goto bail_out;
385    }
386
387    jcr->setJobStatus(JS_Running);
388    fd = jcr->file_bsock;
389
390    if (!send_include_list(jcr)) {
391       goto bail_out;
392    }
393
394    if (!send_exclude_list(jcr)) {
395       goto bail_out;
396    }
397
398    if (!send_level_command(jcr)) {
399       goto bail_out;
400    }
401
402    /* TODO: See priority with bandwidth parameter */
403    if (jcr->job->max_bandwidth > 0) {
404       jcr->max_bandwidth = jcr->job->max_bandwidth;
405    } else if (jcr->client->max_bandwidth > 0) {
406       jcr->max_bandwidth = jcr->client->max_bandwidth;
407    }
408
409    if (jcr->max_bandwidth > 0) {
410       send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
411    }
412
413    /*
414     * send Storage daemon address to the File daemon
415     */
416    store = jcr->wstore;
417    if (store->SDDport == 0) {
418       store->SDDport = store->SDport;
419    }
420
421    /* TLS Requirement */
422    if (store->tls_enable) {
423       if (store->tls_require) {
424          tls_need = BNET_TLS_REQUIRED;
425       } else {
426          tls_need = BNET_TLS_OK;
427       }
428    }
429
430    fd->fsend(storaddr, store->address, store->SDDport, tls_need);
431    if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
432       goto bail_out;
433    }
434
435    if (!send_runscripts_commands(jcr)) {
436       goto bail_out;
437    }
438
439    /*    
440     * We re-update the job start record so that the start
441     *  time is set after the run before job.  This avoids 
442     *  that any files created by the run before job will
443     *  be saved twice.  They will be backed up in the current
444     *  job, but not in the next one unless they are changed.
445     *  Without this, they will be backed up in this job and
446     *  in the next job run because in that case, their date 
447     *   is after the start of this run.
448     */
449    jcr->start_time = time(NULL);
450    jcr->jr.StartTime = jcr->start_time;
451    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
452       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
453    }
454
455    /*
456     * If backup is in accurate mode, we send the list of
457     * all files to FD.
458     */
459    if (!send_accurate_current_files(jcr)) {
460       goto bail_out;
461    }
462
463    /* Send backup command */
464    fd->fsend(backupcmd);
465    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
466       goto bail_out;
467    }
468
469    /* Pickup Job termination data */
470    stat = wait_for_job_termination(jcr);
471    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
472
473    if (jcr->HasBase && 
474        !db_commit_base_file_attributes_record(jcr, jcr->db)) 
475    {
476          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
477    }
478
479    if (stat == JS_Terminated) {
480       backup_cleanup(jcr, stat);
481       return true;
482    }     
483    return false;
484
485 /* Come here only after starting SD thread */
486 bail_out:
487    jcr->setJobStatus(JS_ErrorTerminated);
488    Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
489    /* Cancel SD */
490    wait_for_job_termination(jcr, FDConnectTimeout);
491    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
492    return false;
493 }
494
495
496 /*
497  * Here we wait for the File daemon to signal termination,
498  *   then we wait for the Storage daemon.  When both
499  *   are done, we return the job status.
500  * Also used by restore.c
501  */
502 int wait_for_job_termination(JCR *jcr, int timeout)
503 {
504    int32_t n = 0;
505    BSOCK *fd = jcr->file_bsock;
506    bool fd_ok = false;
507    uint32_t JobFiles, JobErrors;
508    uint32_t JobWarnings = 0;
509    uint64_t ReadBytes = 0;
510    uint64_t JobBytes = 0;
511    int VSS = 0;
512    int Encrypt = 0;
513    btimer_t *tid=NULL;
514
515    jcr->setJobStatus(JS_Running);
516
517    if (fd) {
518       if (timeout) {
519          tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
520       }
521       /* Wait for Client to terminate */
522       while ((n = bget_dirmsg(fd)) >= 0) {
523          if (!fd_ok && 
524              (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
525                      &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
526               sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
527                      &ReadBytes, &JobBytes, &JobErrors) == 5)) {
528             fd_ok = true;
529             jcr->setJobStatus(jcr->FDJobStatus);
530             Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
531          } else {
532             Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
533                  fd->msg);
534          }
535          if (job_canceled(jcr)) {
536             break;
537          }
538       }
539       if (tid) {
540          stop_bsock_timer(tid);
541       }
542
543       if (is_bnet_error(fd)) {
544          Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
545               job_type_to_str(jcr->getJobType()), fd->bstrerror());
546       }
547       fd->signal(BNET_TERMINATE);   /* tell Client we are terminating */
548    }
549
550    /* Force cancel in SD if failing */
551    if (job_canceled(jcr) || !fd_ok) {
552       cancel_storage_daemon_job(jcr);
553    }
554
555    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
556    wait_for_storage_daemon_termination(jcr);
557
558    /* Return values from FD */
559    if (fd_ok) {
560       jcr->JobFiles = JobFiles;
561       jcr->JobErrors += JobErrors;       /* Keep total errors */
562       jcr->ReadBytes = ReadBytes;
563       jcr->JobBytes = JobBytes;
564       jcr->JobWarnings = JobWarnings;
565       jcr->VSS = VSS;
566       jcr->Encrypt = Encrypt;
567    } else {
568       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
569    }
570
571 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
572 //   jcr->JobStatus, jcr->SDJobStatus);
573
574    /* Return the first error status we find Dir, FD, or SD */
575    if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
576       jcr->FDJobStatus = JS_ErrorTerminated;
577    }
578    if (jcr->JobStatus != JS_Terminated) {
579       return jcr->JobStatus;
580    }
581    if (jcr->FDJobStatus != JS_Terminated) {
582       return jcr->FDJobStatus;
583    }
584    return jcr->SDJobStatus;
585 }
586
587 /*
588  * Release resources allocated during backup.
589  */
590 void backup_cleanup(JCR *jcr, int TermCode)
591 {
592    char sdt[50], edt[50], schedt[50];
593    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
594    char ec6[30], ec7[30], ec8[30], elapsed[50];
595    char term_code[100], fd_term_msg[100], sd_term_msg[100];
596    const char *term_msg;
597    int msg_type = M_INFO;
598    MEDIA_DBR mr;
599    CLIENT_DBR cr;
600    double kbps, compression;
601    utime_t RunTime;
602    POOL_MEM base_info;
603
604    if (jcr->getJobLevel() == L_VIRTUAL_FULL) {
605       vbackup_cleanup(jcr, TermCode);
606       return;
607    }
608
609    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
610    memset(&mr, 0, sizeof(mr));
611    memset(&cr, 0, sizeof(cr));
612
613 #ifdef xxxx
614    if (jcr->getJobStatus() == JS_Terminated && 
615         (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
616       TermCode = JS_Warnings;
617    }
618 #endif
619          
620    update_job_end(jcr, TermCode);
621
622    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
623       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
624          db_strerror(jcr->db));
625       jcr->setJobStatus(JS_ErrorTerminated);
626    }
627
628    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
629    if (!db_get_client_record(jcr, jcr->db, &cr)) {
630       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
631          db_strerror(jcr->db));
632    }
633
634    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
635    if (!db_get_media_record(jcr, jcr->db, &mr)) {
636       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
637          mr.VolumeName, db_strerror(jcr->db));
638       jcr->setJobStatus(JS_ErrorTerminated);
639    }
640
641    update_bootstrap_file(jcr);
642
643    switch (jcr->JobStatus) {
644       case JS_Terminated:
645          if (jcr->JobErrors || jcr->SDErrors) {
646             term_msg = _("Backup OK -- with warnings");
647          } else {
648             term_msg = _("Backup OK");
649          }
650          break;
651       case JS_Warnings:
652          term_msg = _("Backup OK -- with warnings");
653          break;
654       case JS_FatalError:
655       case JS_ErrorTerminated:
656          term_msg = _("*** Backup Error ***");
657          msg_type = M_ERROR;          /* Generate error message */
658          if (jcr->store_bsock) {
659             jcr->store_bsock->signal(BNET_TERMINATE);
660             if (jcr->SD_msg_chan) {
661                pthread_cancel(jcr->SD_msg_chan);
662             }
663          }
664          break;
665       case JS_Canceled:
666          term_msg = _("Backup Canceled");
667          if (jcr->store_bsock) {
668             jcr->store_bsock->signal(BNET_TERMINATE);
669             if (jcr->SD_msg_chan) {
670                pthread_cancel(jcr->SD_msg_chan);
671             }
672          }
673          break;
674       default:
675          term_msg = term_code;
676          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
677          break;
678    }
679    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
680    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
681    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
682    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
683    if (RunTime <= 0) {
684       kbps = 0;
685    } else {
686       kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
687    }
688    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
689       /*
690        * Note, if the job has erred, most likely it did not write any
691        *  tape, so suppress this "error" message since in that case
692        *  it is normal.  Or look at it the other way, only for a
693        *  normal exit should we complain about this error.
694        */
695       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
696          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
697       }
698       jcr->VolumeName[0] = 0;         /* none */
699    }
700
701    if (jcr->ReadBytes == 0) {
702       bstrncpy(compress, "None", sizeof(compress));
703    } else {
704       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
705       if (compression < 0.5) {
706          bstrncpy(compress, "None", sizeof(compress));
707       } else {
708          bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
709       }
710    }
711    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
712    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
713
714    if (jcr->HasBase) {
715       Mmsg(base_info, "  Base files/Used files:  %lld/%lld (%.2f%%)\n",
716            jcr->nb_base_files, 
717            jcr->nb_base_files_used, 
718            jcr->nb_base_files_used*100.0/jcr->nb_base_files);
719    }
720 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
721
722    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
723 "  Build OS:               %s %s %s\n"
724 "  JobId:                  %d\n"
725 "  Job:                    %s\n"
726 "  Backup Level:           %s%s\n"
727 "  Client:                 \"%s\" %s\n"
728 "  FileSet:                \"%s\" %s\n"
729 "  Pool:                   \"%s\" (From %s)\n"
730 "  Catalog:                \"%s\" (From %s)\n"
731 "  Storage:                \"%s\" (From %s)\n"
732 "  Scheduled time:         %s\n"
733 "  Start time:             %s\n"
734 "  End time:               %s\n"
735 "  Elapsed time:           %s\n"
736 "  Priority:               %d\n"
737 "  FD Files Written:       %s\n"
738 "  SD Files Written:       %s\n"
739 "  FD Bytes Written:       %s (%sB)\n"
740 "  SD Bytes Written:       %s (%sB)\n"
741 "  Rate:                   %.1f KB/s\n"
742 "  Software Compression:   %s\n"
743 "%s"                                         /* Basefile info */
744 "  VSS:                    %s\n"
745 "  Encryption:             %s\n"
746 "  Accurate:               %s\n"
747 "  Volume name(s):         %s\n"
748 "  Volume Session Id:      %d\n"
749 "  Volume Session Time:    %d\n"
750 "  Last Volume Bytes:      %s (%sB)\n"
751 "  Non-fatal FD errors:    %d\n"
752 "  SD Errors:              %d\n"
753 "  FD termination status:  %s\n"
754 "  SD termination status:  %s\n"
755 "  Termination:            %s\n\n"),
756         BACULA, my_name, VERSION, LSMDATE,
757         HOST_OS, DISTNAME, DISTVER,
758         jcr->jr.JobId,
759         jcr->jr.Job,
760         level_to_str(jcr->getJobLevel()), jcr->since,
761         jcr->client->name(), cr.Uname,
762         jcr->fileset->name(), jcr->FSCreateTime,
763         jcr->pool->name(), jcr->pool_source,
764         jcr->catalog->name(), jcr->catalog_source,
765         jcr->wstore->name(), jcr->wstore_source,
766         schedt,
767         sdt,
768         edt,
769         edit_utime(RunTime, elapsed, sizeof(elapsed)),
770         jcr->JobPriority,
771         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
772         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
773         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
774         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
775         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
776         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
777         kbps,
778         compress,
779         base_info.c_str(),
780         jcr->VSS?_("yes"):_("no"),
781         jcr->Encrypt?_("yes"):_("no"),
782         jcr->accurate?_("yes"):_("no"),
783         jcr->VolumeName,
784         jcr->VolSessionId,
785         jcr->VolSessionTime,
786         edit_uint64_with_commas(mr.VolBytes, ec7),
787         edit_uint64_with_suffix(mr.VolBytes, ec8),
788         jcr->JobErrors,
789         jcr->SDErrors,
790         fd_term_msg,
791         sd_term_msg,
792         term_msg);
793
794    Dmsg0(100, "Leave backup_cleanup()\n");
795 }
796
797 void update_bootstrap_file(JCR *jcr)
798 {
799    /* Now update the bootstrap file if any */
800    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
801        jcr->job->WriteBootstrap) {
802       FILE *fd;
803       BPIPE *bpipe = NULL;
804       int got_pipe = 0;
805       POOLMEM *fname = get_pool_memory(PM_FNAME);
806       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
807
808       VOL_PARAMS *VolParams = NULL;
809       int VolCount;
810       char edt[50], ed1[50], ed2[50];
811
812       if (*fname == '|') {
813          got_pipe = 1;
814          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
815          fd = bpipe ? bpipe->wfd : NULL;
816       } else {
817          /* ***FIXME*** handle BASE */
818          fd = fopen(fname, jcr->getJobLevel()==L_FULL?"w+b":"a+b");
819       }
820       if (fd) {
821          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
822                     &VolParams);
823          if (VolCount == 0) {
824             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
825                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
826              if (jcr->SDJobFiles != 0) {
827                 jcr->setJobStatus(JS_ErrorTerminated);
828              }
829
830          }
831          /* Start output with when and who wrote it */
832          bstrftimes(edt, sizeof(edt), time(NULL));
833          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
834                  level_to_str(jcr->getJobLevel()), jcr->since);
835          for (int i=0; i < VolCount; i++) {
836             /* Write the record */
837             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
838             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
839             if (VolParams[i].Slot > 0) {
840                fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
841             }
842             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
843             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
844             fprintf(fd, "VolAddr=%s-%s\n", 
845                     edit_uint64(VolParams[i].StartAddr, ed1),
846                     edit_uint64(VolParams[i].EndAddr, ed2));
847             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
848                          VolParams[i].LastIndex);
849          }
850          if (VolParams) {
851             free(VolParams);
852          }
853          if (got_pipe) {
854             close_bpipe(bpipe);
855          } else {
856             fclose(fd);
857          }
858       } else {
859          berrno be;
860          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
861               "%s: ERR=%s\n"), fname, be.bstrerror());
862          jcr->setJobStatus(JS_ErrorTerminated);
863       }
864       free_pool_memory(fname);
865    }
866 }