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