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