1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *   Bacula Director -- backup.c -- responsible for doing backup jobs
22  *
23  *     Kern Sibbald, March MM
24  *
25  *  Basic tasks done here:
26  *     Open DB and create records for this job.
27  *     Open Message Channel with Storage daemon to tell him a job will be starting.
28  *     Open connection with File daemon and pass him commands
29  *       to do the backup.
30  *     When the File daemon finishes the job, update the DB.
31  *
32  */
33 
34 #include "bacula.h"
35 #include "dird.h"
36 #include "ua.h"
37 
38 
39 /* Commands sent to File daemon */
40 static char backupcmd[] = "backup FileIndex=%ld\n";
41 static char storaddr[]  = "storage address=%s port=%d ssl=%d\n";
42 
43 /* Responses received from File daemon */
44 static char OKbackup[]   = "2000 OK backup\n";
45 static char OKstore[]    = "2000 OK storage\n";
46 /* After 17 Aug 2013 */
47 static char newEndJob[]  = "2800 End Job TermCode=%d JobFiles=%u "
48                            "ReadBytes=%llu JobBytes=%llu Errors=%u "
49                            "VSS=%d Encrypt=%d "
50                            "CommBytes=%lld CompressCommBytes=%lld\n";
51 /* Pre 17 Aug 2013 */
52 static char EndJob[]     = "2800 End Job TermCode=%d JobFiles=%u "
53                            "ReadBytes=%llu JobBytes=%llu Errors=%u "
54                            "VSS=%d Encrypt=%d\n";
55 /* Pre 1.39.29 (04Dec06) EndJob */
56 static char OldEndJob[]  = "2800 End Job TermCode=%d JobFiles=%u "
57                            "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
58 
59 /* Commands sent to Storage daemon */
60 static char clientaddr[] = "client address=%s port=%d ssl=%d\n";
61 
62 /* Commands received from Storage daemon */
63 static char OKclient[]   = "3000 OK client command\n";
64 
65 /*
66  * Called here before the job is run to do the job
67  *   specific setup.
68  */
do_backup_init(JCR * jcr)69 bool do_backup_init(JCR *jcr)
70 {
71    /* Make local copy */
72    jcr->RescheduleIncompleteJobs = jcr->job->RescheduleIncompleteJobs;
73 
74    if (!get_or_create_fileset_record(jcr)) {
75       Dmsg1(100, "JobId=%d no FileSet\n", (int)jcr->JobId);
76       return false;
77    }
78 
79    /*
80     * Get definitive Job level and since time
81     * unless it's a virtual full.  In that case
82     * it is not needed.
83     */
84    if (!jcr->is_JobLevel(L_VIRTUAL_FULL)) {
85       get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
86    }
87 
88    apply_pool_overrides(jcr);
89 
90    if (!allow_duplicate_job(jcr)) {
91       return false;
92    }
93 
94    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
95    if (jcr->jr.PoolId == 0) {
96       Dmsg1(100, "JobId=%d no PoolId\n", (int)jcr->JobId);
97       Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
98       return false;
99    }
100 
101    /*
102     * If we are a virtual full job or got upgraded to one
103     * then we divert at this point and call the virtual full
104     * backup init method
105     */
106    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
107      return do_vbackup_init(jcr);
108    }
109 
110    free_rstorage(jcr);                   /* we don't read so release */
111 
112    /* If pool storage specified, use it instead of job storage */
113    copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
114 
115    if (!jcr->wstorage) {
116       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
117       return false;
118    }
119 
120    create_clones(jcr);                /* run any clone jobs */
121 
122    return true;
123 }
124 
125 /* Take all base jobs from job resource and find the
126  * last L_BASE jobid.
127  */
get_base_jobids(JCR * jcr,db_list_ctx * jobids)128 static bool get_base_jobids(JCR *jcr, db_list_ctx *jobids)
129 {
130    JOB_DBR jr;
131    JOB *job;
132    JobId_t id;
133    char str_jobid[50];
134 
135    if (!jcr->job->base) {
136       return false;             /* no base job, stop accurate */
137    }
138 
139    memset(&jr, 0, sizeof(JOB_DBR));
140    jr.StartTime = jcr->jr.StartTime;
141 
142    foreach_alist(job, jcr->job->base) {
143       bstrncpy(jr.Name, job->name(), sizeof(jr.Name));
144       db_get_base_jobid(jcr, jcr->db, &jr, &id);
145 
146       if (id) {
147          if (jobids->count) {
148             pm_strcat(jobids->list, ",");
149          }
150          pm_strcat(jobids->list, edit_uint64(id, str_jobid));
151          jobids->count++;
152       }
153    }
154 
155    return jobids->count > 0;
156 }
157 
158 /*
159  * Foreach files in currrent list, send "/path/fname\0LStat\0MD5\0Delta" to FD
160  *      row[0]=Path, row[1]=Filename, row[2]=FileIndex
161  *      row[3]=JobId row[4]=LStat row[5]=DeltaSeq row[6]=MD5
162  */
accurate_list_handler(void * ctx,int num_fields,char ** row)163 static int accurate_list_handler(void *ctx, int num_fields, char **row)
164 {
165    JCR *jcr = (JCR *)ctx;
166 
167    if (job_canceled(jcr)) {
168       return 1;
169    }
170 
171    if (row[2][0] == '0') {           /* discard when file_index == 0 */
172       return 0;
173    }
174 
175    /* sending with checksum */
176    if (jcr->use_accurate_chksum
177        && num_fields == 7
178        && row[6][0] /* skip checksum = '0' */
179        && row[6][1])
180    {
181       jcr->file_bsock->fsend("%s%s%c%s%c%s%c%s",
182                              row[0], row[1], 0, row[4], 0, row[6], 0, row[5]);
183    } else {
184       jcr->file_bsock->fsend("%s%s%c%s%c%c%s",
185                              row[0], row[1], 0, row[4], 0, 0, row[5]);
186    }
187    return 0;
188 }
189 
190 /* In this procedure, we check if the current fileset is using checksum
191  * FileSet-> Include-> Options-> Accurate/Verify/BaseJob=checksum
192  * This procedure uses jcr->HasBase, so it must be call after the initialization
193  */
is_checksum_needed_by_fileset(JCR * jcr)194 static bool is_checksum_needed_by_fileset(JCR *jcr)
195 {
196    FILESET *f;
197    INCEXE *inc;
198    FOPTS *fopts;
199    bool in_block=false;
200    bool have_basejob_option=false;
201    if (!jcr->job || !jcr->job->fileset) {
202       return false;
203    }
204 
205    f = jcr->job->fileset;
206 
207    for (int i=0; i < f->num_includes; i++) { /* Parse all Include {} */
208       inc = f->include_items[i];
209 
210       for (int j=0; j < inc->num_opts; j++) { /* Parse all Options {} */
211          fopts = inc->opts_list[j];
212 
213          for (char *k=fopts->opts; *k ; k++) { /* Try to find one request */
214             switch (*k) {
215             case 'V':           /* verify */
216                in_block = (jcr->getJobType() == JT_VERIFY); /* not used now */
217                break;
218             case 'J':           /* Basejob keyword */
219                have_basejob_option = in_block = jcr->HasBase;
220                break;
221             case 'C':           /* Accurate keyword */
222                in_block = !jcr->is_JobLevel(L_FULL);
223                break;
224             case ':':           /* End of keyword */
225                in_block = false;
226                break;
227             case '5':           /* MD5  */
228             case '1':           /* SHA1 */
229                if (in_block) {
230                   Dmsg0(50, "Checksum will be sent to FD\n");
231                   return true;
232                }
233                break;
234             default:
235                break;
236             }
237          }
238       }
239    }
240 
241    /* By default for BaseJobs, we send the checksum */
242    if (!have_basejob_option && jcr->HasBase) {
243       return true;
244    }
245 
246    Dmsg0(50, "Checksum will be sent to FD\n");
247    return false;
248 }
249 
250 /*
251  * Send current file list to FD
252  *    DIR -> FD : accurate files=xxxx
253  *    DIR -> FD : /path/to/file\0Lstat\0MD5\0Delta
254  *    DIR -> FD : /path/to/dir/\0Lstat\0MD5\0Delta
255  *    ...
256  *    DIR -> FD : EOD
257  */
send_accurate_current_files(JCR * jcr)258 bool send_accurate_current_files(JCR *jcr)
259 {
260    POOL_MEM buf;
261    db_list_ctx jobids;
262    db_list_ctx nb;
263    char ed1[50];
264 
265    /* In base level, no previous job is used and no restart incomplete jobs */
266    if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
267       return true;
268    }
269    if (!jcr->accurate && !jcr->rerunning) {
270       return true;
271    }
272 
273    if (jcr->is_JobLevel(L_FULL)) {
274       /* On Full mode, if no previous base job, no accurate things */
275       if (get_base_jobids(jcr, &jobids)) {
276          jcr->HasBase = true;
277          Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
278       } else if (!jcr->rerunning) {
279          return true;
280       }
281 
282    } else if (jcr->is_JobLevel(L_VERIFY_DATA)) {
283       char ed1[50];
284       jobids.add(edit_uint64(jcr->previous_jr.JobId, ed1));
285 
286    } else {
287       /* For Incr/Diff level, we search for older jobs */
288       db_get_accurate_jobids(jcr, jcr->db, &jcr->jr, &jobids);
289 
290       /* We are in Incr/Diff, but no Full to build the accurate list... */
291       if (jobids.count == 0) {
292          Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
293          return false;  /* fail */
294       }
295    }
296 
297    /* For incomplete Jobs, we add our own id */
298    if (jcr->rerunning) {
299       edit_int64(jcr->JobId, ed1);
300       jobids.add(ed1);
301    }
302 
303    /* Don't send and store the checksum if fileset doesn't require it */
304    jcr->use_accurate_chksum = is_checksum_needed_by_fileset(jcr);
305 
306    if (jcr->JobId) {            /* display the message only for real jobs */
307       Jmsg(jcr, M_INFO, 0, _("Sending Accurate information to the FD.\n"));
308    }
309 
310    /* to be able to allocate the right size for htable */
311    Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
312    db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
313    Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
314    jcr->file_bsock->fsend("accurate files=%s\n", nb.list);
315 
316    if (!db_open_batch_connexion(jcr, jcr->db)) {
317       Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
318       return false;  /* Fail */
319    }
320 
321    if (jcr->HasBase) {
322       jcr->nb_base_files = str_to_int64(nb.list);
323       if (!db_create_base_file_list(jcr, jcr->db, jobids.list)) {
324          Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
325          return false;
326       }
327       if (!db_get_base_file_list(jcr, jcr->db, jcr->use_accurate_chksum,
328                             accurate_list_handler, (void *)jcr)) {
329          Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
330          return false;
331       }
332 
333    } else {
334       int opts = jcr->use_accurate_chksum ? DBL_USE_MD5 : DBL_NONE;
335       if (!db_get_file_list(jcr, jcr->db_batch,
336                        jobids.list, opts,
337                        accurate_list_handler, (void *)jcr)) {
338          Jmsg1(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db_batch));
339          return false;
340       }
341    }
342 
343    /* TODO: close the batch connection ? (can be used very soon) */
344    jcr->file_bsock->signal(BNET_EOD);
345    return true;
346 }
347 
send_store_addr_to_fd(JCR * jcr,STORE * store,char * store_address,uint32_t store_port)348 bool send_store_addr_to_fd(JCR *jcr, STORE *store,
349                            char *store_address, uint32_t store_port)
350 {
351    int tls_need = BNET_TLS_NONE;
352 
353    /* TLS Requirement */
354    if (store->tls_enable) {
355       if (store->tls_require) {
356          tls_need = BNET_TLS_REQUIRED;
357       } else {
358          tls_need = BNET_TLS_OK;
359       }
360    }
361 
362    /*
363     * Send Storage address to the FD
364     */
365    jcr->file_bsock->fsend(storaddr, store_address, store_port, tls_need);
366    if (!response(jcr, jcr->file_bsock, OKstore, "Storage", DISPLAY_ERROR)) {
367       return false;
368    }
369    return true;
370 }
371 
send_client_addr_to_sd(JCR * jcr)372 bool send_client_addr_to_sd(JCR *jcr)
373 {
374    int tls_need = BNET_TLS_NONE;
375    BSOCK *sd = jcr->store_bsock;
376    POOL_MEM buf;
377 
378    /* TLS Requirement for the client */
379    if (jcr->client->tls_enable) {
380       if (jcr->client->tls_require) {
381          tls_need = BNET_TLS_REQUIRED;
382       } else {
383          tls_need = BNET_TLS_OK;
384       }
385    }
386    /* ATTN tls_need is not used on the other side !!!!!!!!! */
387    /*
388     * Send Client address to the SD
389     */
390    sd->fsend(clientaddr, get_client_address(jcr, jcr->client, buf.addr()), jcr->client->FDport, tls_need);
391    if (!response(jcr, sd, OKclient, "Client", DISPLAY_ERROR)) {
392       return false;
393    }
394    return true;
395 }
396 
397 /*
398  * Allow to specify the address used by the Client to
399  * connect to the storage daemon in the Client resource
400  * or in the Storage resource.
401  */
get_storage_address(CLIENT * client,STORE * store)402 char *get_storage_address(CLIENT *client, STORE *store)
403 {
404    char *store_address;
405 
406    if (client && client->fd_storage_address) {
407       Dmsg0(10, "Using Client resource FD Storage Address to contact the Storage\n");
408       store_address = client->fd_storage_address;
409 
410    } else if (store->fd_storage_address) {
411       Dmsg0(10, "Using Storage resource FD Storage Address to contact the Storage\n");
412       store_address = store->fd_storage_address;
413 
414    } else {
415       Dmsg0(10, "Using default Storage address\n");
416       store_address = store->address;
417    }
418    return store_address;
419 }
420 
run_storage_and_start_message_thread(JCR * jcr,BSOCK * sd)421 bool run_storage_and_start_message_thread(JCR *jcr, BSOCK *sd)
422 {
423    /*
424     * Start the job prior to starting the message thread below
425     * to avoid two threads from using the BSOCK structure at
426     * the same time.
427     */
428    if (!sd->fsend("run")) {
429       return false;
430    }
431 
432    /*
433     * Now start a Storage daemon message thread.  Note,
434     *   this thread is used to provide the catalog services
435     *   for the backup job, including inserting the attributes
436     *   into the catalog.  See catalog_update() in catreq.c
437     */
438    if (!start_storage_daemon_message_thread(jcr)) {
439       return false;
440    }
441    Dmsg0(150, "Storage daemon connection OK\n");
442    return true;
443 }
444 
445 /*
446  * Do a backup of the specified FileSet
447  *
448  *  Returns:  false on failure
449  *            true  on success
450  */
do_backup(JCR * jcr)451 bool do_backup(JCR *jcr)
452 {
453    int stat;
454    BSOCK   *fd, *sd;
455    STORE *store;
456    char *store_address;
457    uint32_t store_port;
458    char ed1[100];
459    db_int64_ctx job, first, last;
460    int64_t val=0;
461    POOL_MEM buf;
462 
463    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
464       return do_vbackup(jcr);
465    }
466 
467    /* Print Job Start message */
468    if (jcr->rerunning) {
469       Jmsg(jcr, M_INFO, 0, _("Restart Incomplete Backup JobId %s, Job=%s\n"),
470            edit_uint64(jcr->JobId, ed1), jcr->Job);
471    } else {
472       Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
473            edit_uint64(jcr->JobId, ed1), jcr->Job);
474    }
475 
476    jcr->setJobStatus(JS_Running);
477    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
478    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
479       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
480       return false;
481    }
482 
483    /* For incomplete Jobs, we add our own id */
484    if (jcr->rerunning) {
485       edit_int64(jcr->JobId, ed1);
486       Mmsg(buf, "SELECT max(FileIndex) FROM File WHERE JobId=%s", ed1);
487       if (db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
488          Jmsg(jcr, M_INFO, 0, _("Found %ld files from prior incomplete Job.\n"),
489             (int32_t)job.value);
490       } else {
491          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
492          return false;
493       }
494       Mmsg(buf, "SELECT max(LastIndex) FROM JobMedia WHERE JobId=%s", ed1);
495       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &last)) {
496          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
497          return false;
498       }
499       Mmsg(buf, "SELECT max(FirstIndex) FROM JobMedia WHERE JobId=%s", ed1);
500       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &first)) {
501          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
502          return false;
503       }
504       /* We skip the last FileIndex (MAX) and the one after (MAX+1), can be
505        * already referenced in JobMedia or a Volume
506        */
507       val = MAX(job.value, MAX(first.value, last.value));
508       jcr->JobFiles = val + 2;
509       Dmsg1(100, "==== FI=%ld\n", jcr->JobFiles);
510       Mmsg(buf, "SELECT VolSessionId FROM Job WHERE JobId=%s", ed1);
511       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
512          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
513          return false;
514       }
515       jcr->VolSessionId = job.value;
516       Mmsg(buf, "SELECT VolSessionTime FROM Job WHERE JobId=%s", ed1);
517       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
518          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
519          return false;
520       }
521       jcr->VolSessionTime = job.value;
522       Dmsg4(100, "JobId=%s JobFiles=%ld VolSessionId=%ld VolSessionTime=%ld\n", ed1,
523             jcr->JobFiles, jcr->VolSessionId, jcr->VolSessionTime);
524    }
525 
526    /*
527     * Open a message channel connection with the Storage
528     * daemon. This is to let him know that our client
529     * will be contacting him for a backup  session.
530     *
531     */
532    Dmsg0(110, "Open connection with storage daemon\n");
533    jcr->setJobStatus(JS_WaitSD);
534    /*
535     * Start conversation with Storage daemon
536     */
537    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
538       return false;
539    }
540    /*
541     * Now start a job with the Storage daemon
542     */
543    if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
544       return false;
545    }
546    sd = jcr->store_bsock;
547    if (jcr->client) {
548       jcr->sd_calls_client = jcr->client->sd_calls_client;
549    }
550    /*
551     * Note startup sequence of SD/FD is different depending on
552     *  whether the SD listens (normal) or the SD calls the FD.
553     */
554    if (!jcr->sd_calls_client) {
555       if (!run_storage_and_start_message_thread(jcr, sd)) {
556          goto bail_out;
557       }
558    }
559    jcr->setJobStatus(JS_WaitFD);
560    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
561       goto bail_out;
562    }
563 
564    jcr->setJobStatus(JS_Running);
565    fd = jcr->file_bsock;
566 
567    if (!send_level_command(jcr)) {
568       goto bail_out;
569    }
570 
571    if (!send_include_list(jcr)) {
572       goto bail_out;
573    }
574 
575    if (!send_exclude_list(jcr)) {
576       goto bail_out;
577    }
578 
579    /* TODO: See priority with bandwidth parameter */
580    if (jcr->job->max_bandwidth > 0) {
581       jcr->max_bandwidth = jcr->job->max_bandwidth;
582    } else if (jcr->client->max_bandwidth > 0) {
583       jcr->max_bandwidth = jcr->client->max_bandwidth;
584    }
585 
586    if (jcr->max_bandwidth > 0) {
587       send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
588    }
589 
590    send_snapshot_retention(jcr, jcr->snapshot_retention);
591 
592    store = jcr->wstore;
593 
594    if (jcr->sd_calls_client) {
595       if (jcr->FDVersion < 10) {
596          Jmsg(jcr, M_FATAL, 0, _("The File daemon does not support SDCallsClient.\n"));
597          goto bail_out;
598       }
599       if (!send_client_addr_to_sd(jcr)) {
600          goto bail_out;
601       }
602 
603       if (!run_storage_and_start_message_thread(jcr, sd)) {
604          goto bail_out;
605       }
606 
607       store_address = jcr->wstore->address;  /* dummy */
608       store_port = 0;           /* flag that SD calls FD */
609    } else {
610       /*
611        * send Storage daemon address to the File daemon
612        */
613       if (store->SDDport == 0) {
614          store->SDDport = store->SDport;
615       }
616 
617       store_address = get_storage_address(jcr->client, store);
618       store_port = store->SDDport;
619    }
620 
621    if (!send_store_addr_to_fd(jcr, store, store_address, store_port)) {
622       goto bail_out;
623    }
624 
625    /* Declare the job started to start the MaxRunTime check */
626    jcr->setJobStarted();
627 
628    /* Send and run the RunBefore */
629    if (!send_runscripts_commands(jcr)) {
630       goto bail_out;
631    }
632 
633    /*
634     * We re-update the job start record so that the start
635     *  time is set after the run before job.  This avoids
636     *  that any files created by the run before job will
637     *  be saved twice.  They will be backed up in the current
638     *  job, but not in the next one unless they are changed.
639     *  Without this, they will be backed up in this job and
640     *  in the next job run because in that case, their date
641     *   is after the start of this run.
642     */
643    jcr->start_time = time(NULL);
644    jcr->jr.StartTime = jcr->start_time;
645    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
646       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
647    }
648 
649    /*
650     * If backup is in accurate mode, we send the list of
651     * all files to FD.
652     */
653    if (!send_accurate_current_files(jcr)) {
654       goto bail_out;     /* error */
655    }
656 
657    /* Send backup command */
658    fd->fsend(backupcmd, jcr->JobFiles);
659    Dmsg1(100, ">filed: %s", fd->msg);
660    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
661       goto bail_out;
662    }
663 
664    /* Pickup Job termination data */
665    stat = wait_for_job_termination(jcr);
666 
667    flush_file_records(jcr);     /* cached attribute + batch insert */
668 
669    if (jcr->HasBase) {
670       db_commit_base_file_attributes_record(jcr, jcr->db);
671       /* Any error already printed */
672    }
673 
674    if (!jcr->is_canceled() && stat == JS_Terminated) {
675       backup_cleanup(jcr, stat);
676       return true;
677    }
678    return false;
679 
680 /* Come here only after starting SD thread */
681 bail_out:
682    jcr->setJobStatus(JS_ErrorTerminated);
683    Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
684    /* Cancel SD */
685    wait_for_job_termination(jcr, FDConnectTimeout);
686    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
687    return false;
688 }
689 
690 
691 /*
692  * Here we wait for the File daemon to signal termination,
693  *   then we wait for the Storage daemon.  When both
694  *   are done, we return the job status.
695  * Also used by restore.c
696  */
wait_for_job_termination(JCR * jcr,int timeout)697 int wait_for_job_termination(JCR *jcr, int timeout)
698 {
699    int32_t n = 0;
700    BSOCK *fd = jcr->file_bsock;
701    bool fd_ok = false;
702    uint32_t JobFiles, JobErrors;
703    uint32_t JobWarnings = 0;
704    uint64_t ReadBytes = 0;
705    uint64_t JobBytes = 0;
706    uint64_t CommBytes = 0;
707    uint64_t CommCompressedBytes = 0;
708    int VSS = 0;                 /* or Snapshot on Unix */
709    int Encrypt = 0;
710    btimer_t *tid=NULL;
711 
712    if (fd) {
713       if (timeout) {
714          tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
715       }
716       /* Wait for Client to terminate */
717       while ((n = bget_dirmsg(fd)) >= 0) {
718          if (!fd_ok &&
719              (sscanf(fd->msg, newEndJob, &jcr->FDJobStatus, &JobFiles,
720                      &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt,
721                      &CommBytes, &CommCompressedBytes) == 9 ||
722               sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
723                      &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
724               sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
725                      &ReadBytes, &JobBytes, &JobErrors) == 5)) {
726             fd_ok = true;
727             jcr->setJobStatus(jcr->FDJobStatus);
728             Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
729          } else {
730             Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
731                  fd->msg);
732          }
733          if (job_canceled(jcr)) {
734             break;
735          }
736       }
737       if (tid) {
738          stop_bsock_timer(tid);
739       }
740 
741       if (fd->is_error() && jcr->getJobStatus() != JS_Canceled) {
742          int i = 0;
743          Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
744               job_type_to_str(jcr->getJobType()), fd->bstrerror());
745          while (i++ < 20 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
746             bmicrosleep(3, 0);
747          }
748       }
749       fd->signal(BNET_TERMINATE);   /* tell Client we are terminating */
750    }
751 
752    /*
753     * Force cancel in SD if failing, but not for Incomplete jobs
754     *  so that we let the SD despool.
755     */
756    Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
757         jcr->JobStatus, jcr->SDJobStatus);
758    if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
759       Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
760            jcr->JobStatus, jcr->SDJobStatus);
761       cancel_storage_daemon_job(jcr);
762    }
763 
764    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
765    wait_for_storage_daemon_termination(jcr);
766 
767    /* Return values from FD */
768    if (fd_ok) {
769       jcr->JobFiles = JobFiles;
770       jcr->JobErrors += JobErrors;       /* Keep total errors */
771       jcr->ReadBytes = ReadBytes;
772       jcr->JobBytes = JobBytes;
773       jcr->JobWarnings = JobWarnings;
774       jcr->CommBytes = CommBytes;
775       jcr->CommCompressedBytes = CommCompressedBytes;
776       jcr->Snapshot = VSS;
777       jcr->Encrypt = Encrypt;
778    } else if (jcr->getJobStatus() != JS_Canceled) {
779       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
780    }
781 
782    /* Return the first error status we find Dir, FD, or SD */
783    if (!fd_ok || fd->is_error()) { /* if fd not set, that use !fd_ok */
784       if (jcr->getJobStatus() == JS_Canceled) {
785          jcr->FDJobStatus = JS_Canceled;
786       } else {
787          jcr->FDJobStatus = JS_ErrorTerminated;
788       }
789    }
790    if (jcr->JobStatus != JS_Terminated) {
791       return jcr->JobStatus;
792    }
793    if (jcr->FDJobStatus != JS_Terminated) {
794       return jcr->FDJobStatus;
795    }
796    return jcr->SDJobStatus;
797 }
798 
799 /* When the job is incomplete, we need to make sure the catalog
800  * is consistent. The JobMedia table should reference Files that
801  * are not in the file table for example.
802  */
incomplete_cleanup(JCR * jcr)803 void incomplete_cleanup(JCR *jcr)
804 {
805    POOL_MEM buf;
806    char ed1[50], *jmid;
807    bool ok=true;
808    db_int64_ctx job;
809    alist ids(owned_by_alist, 10);
810    alist *pids = &ids;
811 
812    if (!jcr->is_incomplete()) {
813       return;
814    }
815 
816    edit_int64(jcr->JobId, ed1);
817    /* Get the last valid FileIndex */
818    Mmsg(buf, "SELECT max(FileIndex) FROM File WHERE JobId=%s", ed1);
819    if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
820       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
821       return;
822    }
823 
824    Mmsg(buf, "SELECT JobMediaId FROM JobMedia "
825         "WHERE JobId=%s "
826         "AND (FirstIndex > %lld OR LastIndex > %lld)",
827         ed1, job.value, job.value);
828    if (!db_sql_query(jcr->db, buf.c_str(), db_string_list_handler, &pids)) {
829       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
830       return;
831    }
832    /* Nothing to fix */
833    if (pids->size() == 0) {
834       return;
835    }
836 
837    db_lock(jcr->db);
838    db_start_transaction(jcr, jcr->db);
839 
840    /* Foreach id */
841    foreach_alist(jmid, pids) {
842       JOBMEDIA_DBR jmr;
843       memset(&jmr, 0, sizeof(jmr));
844 
845       jmr.JobMediaId = str_to_int64(jmid);
846       if (!db_get_jobmedia_record(jcr, jcr->db, &jmr)) {
847          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
848          ok=false;
849          goto bail_out;
850       }
851       if (jmr.FirstIndex > job.value) { /* JobMedia for files not in the catalog */
852          if (jmr.LastIndex > job.value) {
853             Dmsg3(50, "Drop JobMediaId=%d FirstIndex=%lld LastIndex=%lld\n",
854                   jmr.JobMediaId, jmr.FirstIndex, jmr.LastIndex);
855 
856          } else {
857             Dmsg3(50, "Incorrect JobMediaId=%d FirstIndex=%lld LastIndex=%lld\n",
858                   jmr.JobMediaId, jmr.FirstIndex, jmr.LastIndex);
859          }
860          Mmsg(buf, "DELETE  FROM JobMedia WHERE JobMediaId=%s", jmid);
861          if (!db_sql_query(jcr->db, buf.c_str(), NULL, NULL)) {
862             ok=false;
863             goto bail_out;
864          }
865       } else if (jmr.LastIndex > job.value) { /* The last index is not in the catalog */
866          Dmsg4(50, "Fix JobMediaId=%d LastIndex=%lld FirstIndex=%lld LastIndex=%lld\n",
867                jmr.JobMediaId, job.value, jmr.FirstIndex, jmr.LastIndex);
868          Mmsg(buf, "UPDATE JobMedia SET LastIndex=%lld WHERE JobMediaId=%s", job.value, jmid);
869          if (!db_sql_query(jcr->db, buf.c_str(), NULL, NULL)) {
870             ok=false;
871             goto bail_out;
872          }
873       } else {
874          Dmsg1(50, "?? JobMedia %d\n", jmr.JobMediaId);
875       }
876    }
877 bail_out:
878    if (!ok) {
879       db_sql_query(jcr->db, "ROLLBACK", NULL, NULL);
880       Jmsg(jcr, M_FATAL, 0, _("Unable to cleanup JobMedia records\n"));
881    }
882    db_end_transaction(jcr, jcr->db);
883    db_unlock(jcr->db);
884 }
885 
886 /*
887  * Release resources allocated during backup.
888  */
backup_cleanup(JCR * jcr,int TermCode)889 void backup_cleanup(JCR *jcr, int TermCode)
890 {
891    char sdt[50], edt[50], schedt[50], edl[50];
892    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30];
893    char ec6[30], ec7[30], ec8[30], ec9[30], ec10[30], elapsed[50];
894    char data_compress[200], comm_compress[200];
895    char fd_term_msg[100], sd_term_msg[100];
896    POOL_MEM term_msg;
897    int msg_type = M_INFO;
898    MEDIA_DBR mr;
899    CLIENT_DBR cr;
900    double kbps, compression, ratio;
901    utime_t RunTime;
902    POOL_MEM base_info;
903    POOL_MEM vol_info;
904 
905    remove_dummy_jobmedia_records(jcr);
906 
907    /* cleanup the job media table after an incomplete job, should not be needed */
908    incomplete_cleanup(jcr);
909 
910    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
911       vbackup_cleanup(jcr, TermCode);
912       return;
913    }
914 
915    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
916    memset(&cr, 0, sizeof(cr));
917 
918 #ifdef xxxx
919    /* The current implementation of the JS_Warning status is not
920     * completed. SQL part looks to be ok, but the code is using
921     * JS_Terminated almost everywhere instead of (JS_Terminated || JS_Warning)
922     * as we do with is_canceled()
923     */
924    if (jcr->getJobStatus() == JS_Terminated &&
925         (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
926       TermCode = JS_Warnings;
927    }
928 #endif
929 
930    update_job_end(jcr, TermCode);
931 
932    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
933       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
934          db_strerror(jcr->db));
935       jcr->setJobStatus(JS_ErrorTerminated);
936    }
937 
938    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
939    if (!db_get_client_record(jcr, jcr->db, &cr)) {
940       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
941          db_strerror(jcr->db));
942    }
943 
944    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
945    if (!db_get_media_record(jcr, jcr->db, &mr)) {
946       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
947          mr.VolumeName, db_strerror(jcr->db));
948       jcr->setJobStatus(JS_ErrorTerminated);
949    }
950 
951    update_bootstrap_file(jcr);
952 
953    if (jcr->is_incomplete() && !jcr->job->allow_incomplete_jobs) {
954       jcr->forceJobStatus(JS_FatalError);
955    }
956 
957    switch (jcr->JobStatus) {
958       case JS_Terminated:
959          if (jcr->JobErrors || jcr->SDErrors) {
960             Mmsg(term_msg, _("Backup OK -- %s"), jcr->StatusErrMsg[0] ? jcr->StatusErrMsg : _("with warnings"));
961 
962          } else {
963             Mmsg(term_msg, _("Backup OK"));
964          }
965          break;
966       case JS_Incomplete:
967          Mmsg(term_msg, _("Backup failed -- Incomplete"));
968          break;
969       case JS_Warnings:
970          Mmsg(term_msg, _("Backup OK -- %s"), jcr->StatusErrMsg[0] ? jcr->StatusErrMsg : _("with warnings"));
971          break;
972       case JS_FatalError:
973       case JS_ErrorTerminated:
974          Mmsg(term_msg, _("*** Backup Error ***"));
975          msg_type = M_ERROR;          /* Generate error message */
976          terminate_sd_msg_chan_thread(jcr);
977          break;
978       case JS_Canceled:
979          Mmsg(term_msg, _("Backup Canceled"));
980          terminate_sd_msg_chan_thread(jcr);
981          break;
982       default:
983          Mmsg(term_msg, _("Inappropriate term code: %c\n"), jcr->JobStatus);
984          break;
985    }
986    bstrftimes_na(schedt, sizeof(schedt), jcr->jr.SchedTime);
987    bstrftimes_na(sdt, sizeof(sdt), jcr->jr.StartTime);
988    bstrftimes_na(edt, sizeof(edt), jcr->jr.EndTime);
989    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
990    if (jcr->jr.StartTime == 0 || RunTime <= 0) {
991       RunTime = 1;
992    }
993    kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
994    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
995       /*
996        * Note, if the job has erred, most likely it did not write any
997        *  tape, so suppress this "error" message since in that case
998        *  it is normal.  Or look at it the other way, only for a
999        *  normal exit should we complain about this error.
1000        */
1001       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
1002          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
1003       }
1004       jcr->VolumeName[0] = 0;         /* none */
1005    }
1006 
1007    if (jcr->ReadBytes == 0) {
1008       bstrncpy(data_compress, "None", sizeof(data_compress));
1009    } else {
1010       compression = (double)100 - 100.0 * ((double)jcr->SDJobBytes / (double)jcr->ReadBytes);
1011       if (compression < 0.5) {
1012          bstrncpy(data_compress, "None", sizeof(data_compress));
1013       } else {
1014          if (jcr->SDJobBytes > 0) {
1015             ratio = (double)jcr->ReadBytes / (double)jcr->SDJobBytes;
1016          } else {
1017             ratio = 1.0;
1018          }
1019          bsnprintf(data_compress, sizeof(data_compress), "%.1f%% %.1f:1",
1020             compression, ratio);
1021       }
1022    }
1023    if (jcr->CommBytes == 0 || jcr->CommCompressedBytes == 0) {
1024       bstrncpy(comm_compress, "None", sizeof(comm_compress));
1025    } else {
1026       compression = (double)100 - 100.0 * ((double)jcr->CommCompressedBytes / (double)jcr->CommBytes);
1027       if (compression < 0.5) {
1028          bstrncpy(comm_compress, "None", sizeof(comm_compress));
1029       } else {
1030          ratio = (double)jcr->CommBytes / (double)jcr->CommCompressedBytes;
1031          bsnprintf(comm_compress, sizeof(comm_compress), "%.1f%% %.1f:1",
1032             compression, ratio);
1033       }
1034       Dmsg2(200, "=== CommCompressed=%lld CommBytes=%lld\n",
1035          jcr->CommCompressedBytes, jcr->CommBytes);
1036    }
1037    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
1038    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1039 
1040    if (jcr->HasBase) {
1041       Mmsg(base_info, _("  Base files/Used files:  %lld/%lld (%.2f%%)\n"),
1042            jcr->nb_base_files,
1043            jcr->nb_base_files_used,
1044            jcr->nb_base_files_used*100.0/jcr->nb_base_files);
1045    }
1046    /* Edit string for last volume size */
1047    if (mr.VolABytes != 0) {
1048       Mmsg(vol_info, _("meta: %s (%sB) aligned: %s (%sB)"),
1049         edit_uint64_with_commas(mr.VolBytes, ec7),
1050         edit_uint64_with_suffix(mr.VolBytes, ec8),
1051         edit_uint64_with_commas(mr.VolABytes, ec9),
1052         edit_uint64_with_suffix(mr.VolABytes, ec10));
1053    } else {
1054      Mmsg(vol_info, _("%s (%sB)"),
1055         edit_uint64_with_commas(mr.VolBytes, ec7),
1056         edit_uint64_with_suffix(mr.VolBytes, ec8));
1057    }
1058 
1059 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
1060 
1061    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
1062 "  Build OS:               %s %s %s\n"
1063 "  JobId:                  %d\n"
1064 "  Job:                    %s\n"
1065 "  Backup Level:           %s%s\n"
1066 "  Client:                 \"%s\" %s\n"
1067 "  FileSet:                \"%s\" %s\n"
1068 "  Pool:                   \"%s\" (From %s)\n"
1069 "  Catalog:                \"%s\" (From %s)\n"
1070 "  Storage:                \"%s\" (From %s)\n"
1071 "  Scheduled time:         %s\n"
1072 "  Start time:             %s\n"
1073 "  End time:               %s\n"
1074 "  Elapsed time:           %s\n"
1075 "  Priority:               %d\n"
1076 "  FD Files Written:       %s\n"
1077 "  SD Files Written:       %s\n"
1078 "  FD Bytes Written:       %s (%sB)\n"
1079 "  SD Bytes Written:       %s (%sB)\n"
1080 "  Rate:                   %.1f KB/s\n"
1081 "  Software Compression:   %s\n"
1082 "  Comm Line Compression:  %s\n"
1083 "%s"                                         /* Basefile info */
1084 "  Snapshot/VSS:           %s\n"
1085 "  Encryption:             %s\n"
1086 "  Accurate:               %s\n"
1087 "  Volume name(s):         %s\n"
1088 "  Volume Session Id:      %d\n"
1089 "  Volume Session Time:    %d\n"
1090 "  Last Volume Bytes:      %s\n"
1091 "  Non-fatal FD errors:    %d\n"
1092 "  SD Errors:              %d\n"
1093 "  FD termination status:  %s\n"
1094 "  SD termination status:  %s\n"
1095 "  Termination:            %s\n\n"),
1096         BACULA, my_name, VERSION, LSMDATE,
1097         HOST_OS, DISTNAME, DISTVER,
1098         jcr->jr.JobId,
1099         jcr->jr.Job,
1100         level_to_str(edl, sizeof(edl), jcr->getJobLevel()), jcr->since,
1101         jcr->client->name(), cr.Uname,
1102         jcr->fileset->name(), jcr->FSCreateTime,
1103         jcr->pool->name(), jcr->pool_source,
1104         jcr->catalog->name(), jcr->catalog_source,
1105         jcr->wstore->name(), jcr->wstore_source,
1106         schedt,
1107         sdt,
1108         edt,
1109         edit_utime(RunTime, elapsed, sizeof(elapsed)),
1110         jcr->JobPriority,
1111         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
1112         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
1113         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
1114         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
1115         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
1116         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
1117         kbps,
1118         data_compress,
1119         comm_compress,
1120         base_info.c_str(),
1121         jcr->Snapshot?_("yes"):_("no"),
1122         jcr->Encrypt?_("yes"):_("no"),
1123         jcr->accurate?_("yes"):_("no"),
1124         jcr->VolumeName,
1125         jcr->VolSessionId,
1126         jcr->VolSessionTime,
1127         vol_info.c_str(),
1128         jcr->JobErrors,
1129         jcr->SDErrors,
1130         fd_term_msg,
1131         sd_term_msg,
1132         term_msg.c_str());
1133 
1134    Dmsg0(100, "Leave backup_cleanup()\n");
1135 }
1136 
update_bootstrap_file(JCR * jcr)1137 void update_bootstrap_file(JCR *jcr)
1138 {
1139    /* Now update the bootstrap file if any */
1140    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
1141        jcr->job->WriteBootstrap) {
1142       FILE *fd;
1143       BPIPE *bpipe = NULL;
1144       int got_pipe = 0;
1145       char edl[50];
1146       POOLMEM *fname = get_pool_memory(PM_FNAME);
1147       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "",
1148                              job_code_callback_director);
1149 
1150       VOL_PARAMS *VolParams = NULL;
1151       int VolCount;
1152       char edt[50], ed1[50], ed2[50];
1153 
1154       if (*fname == '|') {
1155          got_pipe = 1;
1156          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
1157          fd = bpipe ? bpipe->wfd : NULL;
1158       } else {
1159          /* ***FIXME*** handle BASE */
1160          fd = bfopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
1161       }
1162       if (fd) {
1163          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
1164                     &VolParams);
1165          if (VolCount == 0) {
1166             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
1167                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
1168              if (jcr->SDJobFiles != 0) {
1169                 jcr->setJobStatus(JS_ErrorTerminated);
1170              }
1171 
1172          }
1173          /* Start output with when and who wrote it */
1174          bstrftimes(edt, sizeof(edt), time(NULL));
1175          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
1176                  level_to_str(edl, sizeof(edl), jcr->getJobLevel()),
1177                  jcr->since);
1178          for (int i=0; i < VolCount; i++) {
1179             /* Write the record */
1180             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
1181             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
1182             if (VolParams[i].Slot > 0) {
1183                fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
1184             }
1185             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
1186             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
1187             fprintf(fd, "VolAddr=%s-%s\n",
1188                     edit_uint64(VolParams[i].StartAddr, ed1),
1189                     edit_uint64(VolParams[i].EndAddr, ed2));
1190             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
1191                          VolParams[i].LastIndex);
1192          }
1193          if (VolParams) {
1194             free(VolParams);
1195          }
1196          if (got_pipe) {
1197             close_bpipe(bpipe);
1198          } else {
1199             fclose(fd);
1200          }
1201       } else {
1202          berrno be;
1203          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
1204               "%s: ERR=%s\n"), fname, be.bstrerror());
1205          jcr->setJobStatus(JS_ErrorTerminated);
1206       }
1207       free_pool_memory(fname);
1208    }
1209 }
1210