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