]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
Fix couple of g++ warnings
[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]=DeltaSeq 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    /* In base level, no previous job is used and no restart incomplete jobs */
246    if (jcr->is_canceled() || jcr->is_JobLevel(L_BASE)) {
247       return true;
248    }
249    if (!jcr->accurate && !jcr->rerunning) {
250       return true;
251    }
252
253    if (jcr->is_JobLevel(L_FULL)) {
254       /* On Full mode, if no previous base job, no accurate things */
255       if (get_base_jobids(jcr, &jobids)) {
256          jcr->HasBase = true;
257          Jmsg(jcr, M_INFO, 0, _("Using BaseJobId(s): %s\n"), jobids.list);
258       } else if (!jcr->rerunning) {
259          return true;
260       }
261    } else {
262       /* For Incr/Diff level, we search for older jobs */
263       db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
264
265       /* We are in Incr/Diff, but no Full to build the accurate list... */
266       if (jobids.count == 0) {
267          Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
268          return false;  /* fail */
269       }
270    }
271
272    /* For incomplete Jobs, we add our own id */
273    if (jcr->rerunning) {
274       edit_int64(jcr->JobId, ed1);   
275       jobids.add(ed1);
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    if (jcr->rerunning) {
336       Jmsg(jcr, M_INFO, 0, _("Restart Incomplete Backup JobId %s, Job=%s\n"),
337            edit_uint64(jcr->JobId, ed1), jcr->Job);
338    } else {
339       Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
340            edit_uint64(jcr->JobId, ed1), jcr->Job);
341    }
342
343    jcr->setJobStatus(JS_Running);
344    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
345    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
346       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
347       return false;
348    }
349
350    /* For incomplete Jobs, we add our own id */
351    if (jcr->rerunning) {
352       edit_int64(jcr->JobId, ed1);   
353       Mmsg(buf, "SELECT max(FileIndex) FROM File WHERE JobId=%s", ed1);
354       if (db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
355          Jmsg(jcr, M_INFO, 0, _("Found %ld files from prior incomplete Job.\n"),
356             (int32_t)job.value);
357       } else {
358          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
359          return false;
360       }
361       jcr->JobFiles = job.value;
362       Dmsg1(100, "==== FI=%ld\n", jcr->JobFiles);
363       Mmsg(buf, "SELECT VolSessionId FROM Job WHERE JobId=%s", ed1);
364       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
365          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
366          return false;
367       }
368       jcr->VolSessionId = job.value;
369       Mmsg(buf, "SELECT VolSessionTime FROM Job WHERE JobId=%s", ed1);
370       if (!db_sql_query(jcr->db, buf.c_str(), db_int64_handler, &job)) {
371          Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
372          return false;
373       }
374       jcr->VolSessionTime = job.value;
375       Dmsg4(100, "JobId=%s JobFiles=%ld VolSessionId=%ld VolSessionTime=%ld\n", ed1, 
376             jcr->JobFiles, jcr->VolSessionId, jcr->VolSessionTime);
377    }
378
379    /*
380     * Open a message channel connection with the Storage
381     * daemon. This is to let him know that our client
382     * will be contacting him for a backup  session.
383     *
384     */
385    Dmsg0(110, "Open connection with storage daemon\n");
386    jcr->setJobStatus(JS_WaitSD);
387    /*
388     * Start conversation with Storage daemon
389     */
390    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
391       return false;
392    }
393    /*
394     * Now start a job with the Storage daemon
395     */
396    if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
397       return false;
398    }
399
400    /*
401     * Start the job prior to starting the message thread below
402     * to avoid two threads from using the BSOCK structure at
403     * the same time.
404     */
405    if (!jcr->store_bsock->fsend("run")) {
406       return false;
407    }
408
409    /*
410     * Now start a Storage daemon message thread.  Note,
411     *   this thread is used to provide the catalog services
412     *   for the backup job, including inserting the attributes
413     *   into the catalog.  See catalog_update() in catreq.c
414     */
415    if (!start_storage_daemon_message_thread(jcr)) {
416       return false;
417    }
418    Dmsg0(150, "Storage daemon connection OK\n");
419
420    jcr->setJobStatus(JS_WaitFD);
421    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
422       goto bail_out;
423    }
424
425    jcr->setJobStatus(JS_Running);
426    fd = jcr->file_bsock;
427
428    if (!send_include_list(jcr)) {
429       goto bail_out;
430    }
431
432    if (!send_exclude_list(jcr)) {
433       goto bail_out;
434    }
435
436    if (!send_level_command(jcr)) {
437       goto bail_out;
438    }
439
440    /* TODO: See priority with bandwidth parameter */
441    if (jcr->job->max_bandwidth > 0) {
442       jcr->max_bandwidth = jcr->job->max_bandwidth;
443    } else if (jcr->client->max_bandwidth > 0) {
444       jcr->max_bandwidth = jcr->client->max_bandwidth;
445    }
446
447    if (jcr->max_bandwidth > 0) {
448       send_bwlimit(jcr, jcr->Job); /* Old clients don't have this command */
449    }
450
451    /*
452     * send Storage daemon address to the File daemon
453     */
454    store = jcr->wstore;
455    if (store->SDDport == 0) {
456       store->SDDport = store->SDport;
457    }
458
459    /* TLS Requirement */
460    if (store->tls_enable) {
461       if (store->tls_require) {
462          tls_need = BNET_TLS_REQUIRED;
463       } else {
464          tls_need = BNET_TLS_OK;
465       }
466    }
467
468    fd->fsend(storaddr, store->address, store->SDDport, tls_need);
469    if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
470       goto bail_out;
471    }
472
473    if (!send_runscripts_commands(jcr)) {
474       goto bail_out;
475    }
476
477    /*    
478     * We re-update the job start record so that the start
479     *  time is set after the run before job.  This avoids 
480     *  that any files created by the run before job will
481     *  be saved twice.  They will be backed up in the current
482     *  job, but not in the next one unless they are changed.
483     *  Without this, they will be backed up in this job and
484     *  in the next job run because in that case, their date 
485     *   is after the start of this run.
486     */
487    jcr->start_time = time(NULL);
488    jcr->jr.StartTime = jcr->start_time;
489    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
490       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
491    }
492
493    /*
494     * If backup is in accurate mode, we send the list of
495     * all files to FD.
496     */
497    if (!send_accurate_current_files(jcr)) {
498       goto bail_out;     /* error */
499    }
500
501    /* Send backup command */
502    fd->fsend(backupcmd, jcr->JobFiles);
503    Dmsg1(100, ">filed: %s", fd->msg);
504    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
505       goto bail_out;
506    }
507
508    /* Pickup Job termination data */
509    stat = wait_for_job_termination(jcr);
510    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
511
512    if (jcr->HasBase && !db_commit_base_file_attributes_record(jcr, jcr->db))  {
513       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
514    }
515
516    if (stat == JS_Terminated) {
517       backup_cleanup(jcr, stat);
518       return true;
519    }     
520    return false;
521
522 /* Come here only after starting SD thread */
523 bail_out:
524    jcr->setJobStatus(JS_ErrorTerminated);
525    Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
526    /* Cancel SD */
527    wait_for_job_termination(jcr, FDConnectTimeout);
528    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
529    return false;
530 }
531
532
533 /*
534  * Here we wait for the File daemon to signal termination,
535  *   then we wait for the Storage daemon.  When both
536  *   are done, we return the job status.
537  * Also used by restore.c
538  */
539 int wait_for_job_termination(JCR *jcr, int timeout)
540 {
541    int32_t n = 0;
542    BSOCK *fd = jcr->file_bsock;
543    bool fd_ok = false;
544    uint32_t JobFiles, JobErrors;
545    uint32_t JobWarnings = 0;
546    uint64_t ReadBytes = 0;
547    uint64_t JobBytes = 0;
548    int VSS = 0;
549    int Encrypt = 0;
550    btimer_t *tid=NULL;
551
552    jcr->setJobStatus(JS_Running);
553
554    if (fd) {
555       if (timeout) {
556          tid = start_bsock_timer(fd, timeout); /* TODO: New timeout directive??? */
557       }
558       /* Wait for Client to terminate */
559       while ((n = bget_dirmsg(fd)) >= 0) {
560          if (!fd_ok && 
561              (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
562                      &ReadBytes, &JobBytes, &JobErrors, &VSS, &Encrypt) == 7 ||
563               sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
564                      &ReadBytes, &JobBytes, &JobErrors) == 5)) {
565             fd_ok = true;
566             jcr->setJobStatus(jcr->FDJobStatus);
567             Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
568          } else {
569             Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
570                  fd->msg);
571          }
572          if (job_canceled(jcr)) {
573             break;
574          }
575       }
576       if (tid) {
577          stop_bsock_timer(tid);
578       }
579
580       if (is_bnet_error(fd)) {
581          int i = 0;
582          Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
583               job_type_to_str(jcr->getJobType()), fd->bstrerror());
584          while (i++ < 10 && jcr->job->RescheduleIncompleteJobs && jcr->is_canceled()) {
585             bmicrosleep(3, 0);
586          }
587             
588       }
589       fd->signal(BNET_TERMINATE);   /* tell Client we are terminating */
590    }
591
592    /*
593     * Force cancel in SD if failing, but not for Incomplete jobs
594     *  so that we let the SD despool.
595     */
596    Dmsg5(100, "cancel=%d fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", jcr->is_canceled(), fd_ok, jcr->FDJobStatus,
597         jcr->JobStatus, jcr->SDJobStatus);
598    if (jcr->is_canceled() || (!jcr->job->RescheduleIncompleteJobs && !fd_ok)) {
599       Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
600            jcr->JobStatus, jcr->SDJobStatus);
601       cancel_storage_daemon_job(jcr);
602    }
603
604    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
605    wait_for_storage_daemon_termination(jcr);
606
607    /* Return values from FD */
608    if (fd_ok) {
609       jcr->JobFiles = JobFiles;
610       jcr->JobErrors += JobErrors;       /* Keep total errors */
611       jcr->ReadBytes = ReadBytes;
612       jcr->JobBytes = JobBytes;
613       jcr->JobWarnings = JobWarnings;
614       jcr->VSS = VSS;
615       jcr->Encrypt = Encrypt;
616    } else {
617       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
618    }
619
620 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
621 //   jcr->JobStatus, jcr->SDJobStatus);
622
623    /* Return the first error status we find Dir, FD, or SD */
624    if (!fd_ok || is_bnet_error(fd)) { /* if fd not set, that use !fd_ok */
625       jcr->FDJobStatus = JS_ErrorTerminated;
626    }
627    if (jcr->JobStatus != JS_Terminated) {
628       return jcr->JobStatus;
629    }
630    if (jcr->FDJobStatus != JS_Terminated) {
631       return jcr->FDJobStatus;
632    }
633    return jcr->SDJobStatus;
634 }
635
636 /*
637  * Release resources allocated during backup.
638  */
639 void backup_cleanup(JCR *jcr, int TermCode)
640 {
641    char sdt[50], edt[50], schedt[50];
642    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
643    char ec6[30], ec7[30], ec8[30], elapsed[50];
644    char term_code[100], fd_term_msg[100], sd_term_msg[100];
645    const char *term_msg;
646    int msg_type = M_INFO;
647    MEDIA_DBR mr;
648    CLIENT_DBR cr;
649    double kbps, compression;
650    utime_t RunTime;
651    POOL_MEM base_info;
652
653    if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
654       vbackup_cleanup(jcr, TermCode);
655       return;
656    }
657
658    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
659    memset(&mr, 0, sizeof(mr));
660    memset(&cr, 0, sizeof(cr));
661
662 #ifdef xxxx
663    if (jcr->getJobStatus() == JS_Terminated && 
664         (jcr->JobErrors || jcr->SDErrors || jcr->JobWarnings)) {
665       TermCode = JS_Warnings;
666    }
667 #endif
668          
669    update_job_end(jcr, TermCode);
670
671    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
672       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
673          db_strerror(jcr->db));
674       jcr->setJobStatus(JS_ErrorTerminated);
675    }
676
677    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
678    if (!db_get_client_record(jcr, jcr->db, &cr)) {
679       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
680          db_strerror(jcr->db));
681    }
682
683    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
684    if (!db_get_media_record(jcr, jcr->db, &mr)) {
685       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
686          mr.VolumeName, db_strerror(jcr->db));
687       jcr->setJobStatus(JS_ErrorTerminated);
688    }
689
690    update_bootstrap_file(jcr);
691
692    switch (jcr->JobStatus) {
693       case JS_Terminated:
694          if (jcr->JobErrors || jcr->SDErrors) {
695             term_msg = _("Backup OK -- with warnings");
696          } else {
697             term_msg = _("Backup OK");
698          }
699          break;
700       case JS_Incomplete:
701          term_msg = _("Backup failed -- incomplete");
702          break;
703       case JS_Warnings:
704          term_msg = _("Backup OK -- with warnings");
705          break;
706       case JS_FatalError:
707       case JS_ErrorTerminated:
708          term_msg = _("*** Backup Error ***");
709          msg_type = M_ERROR;          /* Generate error message */
710          if (jcr->store_bsock) {
711             jcr->store_bsock->signal(BNET_TERMINATE);
712             if (jcr->SD_msg_chan) {
713                pthread_cancel(jcr->SD_msg_chan);
714             }
715          }
716          break;
717       case JS_Canceled:
718          term_msg = _("Backup Canceled");
719          if (jcr->store_bsock) {
720             jcr->store_bsock->signal(BNET_TERMINATE);
721             if (jcr->SD_msg_chan) {
722                pthread_cancel(jcr->SD_msg_chan);
723             }
724          }
725          break;
726       default:
727          term_msg = term_code;
728          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
729          break;
730    }
731    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
732    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
733    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
734    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
735    if (RunTime <= 0) {
736       kbps = 0;
737    } else {
738       kbps = ((double)jcr->jr.JobBytes) / (1000.0 * (double)RunTime);
739    }
740    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
741       /*
742        * Note, if the job has erred, most likely it did not write any
743        *  tape, so suppress this "error" message since in that case
744        *  it is normal.  Or look at it the other way, only for a
745        *  normal exit should we complain about this error.
746        */
747       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
748          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
749       }
750       jcr->VolumeName[0] = 0;         /* none */
751    }
752
753    if (jcr->ReadBytes == 0) {
754       bstrncpy(compress, "None", sizeof(compress));
755    } else {
756       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
757       if (compression < 0.5) {
758          bstrncpy(compress, "None", sizeof(compress));
759       } else {
760          bsnprintf(compress, sizeof(compress), "%.1f %%", compression);
761       }
762    }
763    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
764    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
765
766    if (jcr->HasBase) {
767       Mmsg(base_info, "  Base files/Used files:  %lld/%lld (%.2f%%)\n",
768            jcr->nb_base_files, 
769            jcr->nb_base_files_used, 
770            jcr->nb_base_files_used*100.0/jcr->nb_base_files);
771    }
772 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
773
774    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
775 "  Build OS:               %s %s %s\n"
776 "  JobId:                  %d\n"
777 "  Job:                    %s\n"
778 "  Backup Level:           %s%s\n"
779 "  Client:                 \"%s\" %s\n"
780 "  FileSet:                \"%s\" %s\n"
781 "  Pool:                   \"%s\" (From %s)\n"
782 "  Catalog:                \"%s\" (From %s)\n"
783 "  Storage:                \"%s\" (From %s)\n"
784 "  Scheduled time:         %s\n"
785 "  Start time:             %s\n"
786 "  End time:               %s\n"
787 "  Elapsed time:           %s\n"
788 "  Priority:               %d\n"
789 "  FD Files Written:       %s\n"
790 "  SD Files Written:       %s\n"
791 "  FD Bytes Written:       %s (%sB)\n"
792 "  SD Bytes Written:       %s (%sB)\n"
793 "  Rate:                   %.1f KB/s\n"
794 "  Software Compression:   %s\n"
795 "%s"                                         /* Basefile info */
796 "  VSS:                    %s\n"
797 "  Encryption:             %s\n"
798 "  Accurate:               %s\n"
799 "  Volume name(s):         %s\n"
800 "  Volume Session Id:      %d\n"
801 "  Volume Session Time:    %d\n"
802 "  Last Volume Bytes:      %s (%sB)\n"
803 "  Non-fatal FD errors:    %d\n"
804 "  SD Errors:              %d\n"
805 "  FD termination status:  %s\n"
806 "  SD termination status:  %s\n"
807 "  Termination:            %s\n\n"),
808         BACULA, my_name, VERSION, LSMDATE,
809         HOST_OS, DISTNAME, DISTVER,
810         jcr->jr.JobId,
811         jcr->jr.Job,
812         level_to_str(jcr->getJobLevel()), jcr->since,
813         jcr->client->name(), cr.Uname,
814         jcr->fileset->name(), jcr->FSCreateTime,
815         jcr->pool->name(), jcr->pool_source,
816         jcr->catalog->name(), jcr->catalog_source,
817         jcr->wstore->name(), jcr->wstore_source,
818         schedt,
819         sdt,
820         edt,
821         edit_utime(RunTime, elapsed, sizeof(elapsed)),
822         jcr->JobPriority,
823         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
824         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
825         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
826         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
827         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
828         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
829         kbps,
830         compress,
831         base_info.c_str(),
832         jcr->VSS?_("yes"):_("no"),
833         jcr->Encrypt?_("yes"):_("no"),
834         jcr->accurate?_("yes"):_("no"),
835         jcr->VolumeName,
836         jcr->VolSessionId,
837         jcr->VolSessionTime,
838         edit_uint64_with_commas(mr.VolBytes, ec7),
839         edit_uint64_with_suffix(mr.VolBytes, ec8),
840         jcr->JobErrors,
841         jcr->SDErrors,
842         fd_term_msg,
843         sd_term_msg,
844         term_msg);
845
846    Dmsg0(100, "Leave backup_cleanup()\n");
847 }
848
849 void update_bootstrap_file(JCR *jcr)
850 {
851    /* Now update the bootstrap file if any */
852    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
853        jcr->job->WriteBootstrap) {
854       FILE *fd;
855       BPIPE *bpipe = NULL;
856       int got_pipe = 0;
857       POOLMEM *fname = get_pool_memory(PM_FNAME);
858       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
859
860       VOL_PARAMS *VolParams = NULL;
861       int VolCount;
862       char edt[50], ed1[50], ed2[50];
863
864       if (*fname == '|') {
865          got_pipe = 1;
866          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
867          fd = bpipe ? bpipe->wfd : NULL;
868       } else {
869          /* ***FIXME*** handle BASE */
870          fd = fopen(fname, jcr->is_JobLevel(L_FULL)?"w+b":"a+b");
871       }
872       if (fd) {
873          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
874                     &VolParams);
875          if (VolCount == 0) {
876             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
877                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
878              if (jcr->SDJobFiles != 0) {
879                 jcr->setJobStatus(JS_ErrorTerminated);
880              }
881
882          }
883          /* Start output with when and who wrote it */
884          bstrftimes(edt, sizeof(edt), time(NULL));
885          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
886                  level_to_str(jcr->getJobLevel()), jcr->since);
887          for (int i=0; i < VolCount; i++) {
888             /* Write the record */
889             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
890             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
891             if (VolParams[i].Slot > 0) {
892                fprintf(fd, "Slot=%d\n", VolParams[i].Slot);
893             }
894             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
895             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
896             fprintf(fd, "VolAddr=%s-%s\n", 
897                     edit_uint64(VolParams[i].StartAddr, ed1),
898                     edit_uint64(VolParams[i].EndAddr, ed2));
899             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
900                          VolParams[i].LastIndex);
901          }
902          if (VolParams) {
903             free(VolParams);
904          }
905          if (got_pipe) {
906             close_bpipe(bpipe);
907          } else {
908             fclose(fd);
909          }
910       } else {
911          berrno be;
912          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
913               "%s: ERR=%s\n"), fname, be.bstrerror());
914          jcr->setJobStatus(JS_ErrorTerminated);
915       }
916       free_pool_memory(fname);
917    }
918 }