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