]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
kes Add job report indication of whether or not VSS and Encryption were
[bacula/bacula] / bacula / src / dird / backup.c
1 /*
2  *
3  *   Bacula Director -- backup.c -- responsible for doing backup jobs
4  *
5  *     Kern Sibbald, March MM
6  *
7  *  Basic tasks done here:
8  *     Open DB and create records for this job.
9  *     Open Message Channel with Storage daemon to tell him a job will be starting.
10  *     Open connection with File daemon and pass him commands
11  *       to do the backup.
12  *     When the File daemon finishes the job, update the DB.
13  *
14  *   Version $Id$
15  */
16 /*
17    Bacula® - The Network Backup Solution
18
19    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
20
21    The main author of Bacula is Kern Sibbald, with contributions from
22    many others, a complete list can be found in the file AUTHORS.
23    This program is Free Software; you can redistribute it and/or
24    modify it under the terms of version two of the GNU General Public
25    License as published by the Free Software Foundation plus additions
26    that are listed in the file LICENSE.
27
28    This program is distributed in the hope that it will be useful, but
29    WITHOUT ANY WARRANTY; without even the implied warranty of
30    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31    General Public License for more details.
32
33    You should have received a copy of the GNU General Public License
34    along with this program; if not, write to the Free Software
35    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
36    02110-1301, USA.
37
38    Bacula® is a registered trademark of John Walker.
39    The licensor of Bacula is the Free Software Foundation Europe
40    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
41    Switzerland, email:ftf@fsfeurope.org.
42 */
43
44 #include "bacula.h"
45 #include "dird.h"
46 #include "ua.h"
47
48 /* Commands sent to File daemon */
49 static char backupcmd[] = "backup\n";
50 static char storaddr[]  = "storage address=%s port=%d ssl=%d\n";
51
52 /* Responses received from File daemon */
53 static char OKbackup[]   = "2000 OK backup\n";
54 static char OKstore[]    = "2000 OK storage\n";
55 static char EndJob[]     = "2800 End Job TermCode=%d JobFiles=%u "
56                            "ReadBytes=%lld JobBytes=%lld Errors=%u "  
57                            "VSS=%d Encrypt=%d\n";
58 /* Pre 1.39.29 (04Dec06) EndJob */
59 static char OldEndJob[]  = "2800 End Job TermCode=%d JobFiles=%u "
60                            "ReadBytes=%lld JobBytes=%lld Errors=%u\n";
61 /* 
62  * Called here before the job is run to do the job
63  *   specific setup.
64  */
65 bool do_backup_init(JCR *jcr)
66 {
67
68    free_rstorage(jcr);                   /* we don't read so release */
69
70    if (!get_or_create_fileset_record(jcr)) {
71       return false;
72    }
73
74    /* 
75     * Get definitive Job level and since time
76     */
77    get_level_since_time(jcr, jcr->since, sizeof(jcr->since));
78
79    apply_pool_overrides(jcr);
80
81    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->hdr.name);
82    if (jcr->jr.PoolId == 0) {
83       return false;
84    }
85
86    /* If pool storage specified, use it instead of job storage */
87    copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
88
89    if (!jcr->wstorage) {
90       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
91       return false;
92    }
93
94    create_clones(jcr);                /* run any clone jobs */
95
96    return true;
97 }
98
99 /*
100  * Do a backup of the specified FileSet
101  *
102  *  Returns:  false on failure
103  *            true  on success
104  */
105 bool do_backup(JCR *jcr)
106 {
107    int stat;
108    int tls_need = BNET_TLS_NONE;
109    BSOCK   *fd;
110    STORE *store;
111    char ed1[100];
112
113
114    /* Print Job Start message */
115    Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
116         edit_uint64(jcr->JobId, ed1), jcr->Job);
117
118    set_jcr_job_status(jcr, JS_Running);
119    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
120    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
121       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
122       return false;
123    }
124
125    /*
126     * Open a message channel connection with the Storage
127     * daemon. This is to let him know that our client
128     * will be contacting him for a backup  session.
129     *
130     */
131    Dmsg0(110, "Open connection with storage daemon\n");
132    set_jcr_job_status(jcr, JS_WaitSD);
133    /*
134     * Start conversation with Storage daemon
135     */
136    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
137       return false;
138    }
139    /*
140     * Now start a job with the Storage daemon
141     */
142    if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) {
143       return false;
144    }
145
146    /*
147     * Start the job prior to starting the message thread below
148     * to avoid two threads from using the BSOCK structure at
149     * the same time.
150     */
151    if (!bnet_fsend(jcr->store_bsock, "run")) {
152       return false;
153    }
154
155    /*
156     * Now start a Storage daemon message thread.  Note,
157     *   this thread is used to provide the catalog services
158     *   for the backup job, including inserting the attributes
159     *   into the catalog.  See catalog_update() in catreq.c
160     */
161    if (!start_storage_daemon_message_thread(jcr)) {
162       return false;
163    }
164    Dmsg0(150, "Storage daemon connection OK\n");
165
166    set_jcr_job_status(jcr, JS_WaitFD);
167    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
168       goto bail_out;
169    }
170
171    set_jcr_job_status(jcr, JS_Running);
172    fd = jcr->file_bsock;
173
174    if (!send_include_list(jcr)) {
175       goto bail_out;
176    }
177
178    if (!send_exclude_list(jcr)) {
179       goto bail_out;
180    }
181
182    if (!send_level_command(jcr)) {
183       goto bail_out;
184    }
185
186    /*
187     * send Storage daemon address to the File daemon
188     */
189    store = jcr->wstore;
190    if (store->SDDport == 0) {
191       store->SDDport = store->SDport;
192    }
193
194    /* TLS Requirement */
195    if (store->tls_enable) {
196       if (store->tls_require) {
197          tls_need = BNET_TLS_REQUIRED;
198       } else {
199          tls_need = BNET_TLS_OK;
200       }
201    }
202
203    bnet_fsend(fd, storaddr, store->address, store->SDDport, tls_need);
204    if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
205       goto bail_out;
206    }
207
208    if (!send_runscripts_commands(jcr)) {
209       goto bail_out;
210    }
211
212    /*    
213     * We re-update the job start record so that the start
214     *  time is set after the run before job.  This avoids 
215     *  that any files created by the run before job will
216     *  be saved twice.  They will be backed up in the current
217     *  job, but not in the next one unless they are changed.
218     *  Without this, they will be backed up in this job and
219     *  in the next job run because in that case, their date 
220     *   is after the start of this run.
221     */
222    jcr->start_time = time(NULL);
223    jcr->jr.StartTime = jcr->start_time;
224    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
225       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
226    }
227
228    /* Send backup command */
229    bnet_fsend(fd, backupcmd);
230    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
231       goto bail_out;
232    }
233
234    /* Pickup Job termination data */
235    stat = wait_for_job_termination(jcr);
236    if (stat == JS_Terminated) {
237       backup_cleanup(jcr, stat);
238       return true;
239    }     
240    return false;
241
242 /* Come here only after starting SD thread */
243 bail_out:
244    set_jcr_job_status(jcr, JS_ErrorTerminated);
245    Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
246    wait_for_storage_daemon_termination(jcr);
247    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
248    return false;
249 }
250
251
252 /*
253  * Here we wait for the File daemon to signal termination,
254  *   then we wait for the Storage daemon.  When both
255  *   are done, we return the job status.
256  * Also used by restore.c
257  */
258 int wait_for_job_termination(JCR *jcr)
259 {
260    int32_t n = 0;
261    BSOCK *fd = jcr->file_bsock;
262    bool fd_ok = false;
263    uint32_t JobFiles, Errors;
264    uint64_t ReadBytes, JobBytes;
265    int VSS = 0;
266    int Encrypt = 0;
267
268    set_jcr_job_status(jcr, JS_Running);
269    /* Wait for Client to terminate */
270    while ((n = bget_dirmsg(fd)) >= 0) {
271       if (!fd_ok && 
272           (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
273               &ReadBytes, &JobBytes, &Errors, &VSS, &Encrypt) == 7 ||
274            sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
275                  &ReadBytes, &JobBytes, &Errors) == 5)) {
276          fd_ok = true;
277          set_jcr_job_status(jcr, jcr->FDJobStatus);
278          Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
279       } else {
280          Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
281             fd->msg);
282       }
283       if (job_canceled(jcr)) {
284          break;
285       }
286    }
287    if (is_bnet_error(fd)) {
288       Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
289           job_type_to_str(jcr->JobType), bnet_strerror(fd));
290    }
291    bnet_sig(fd, BNET_TERMINATE);   /* tell Client we are terminating */
292
293    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
294    wait_for_storage_daemon_termination(jcr);
295
296
297    /* Return values from FD */
298    if (fd_ok) {
299       jcr->JobFiles = JobFiles;
300       jcr->Errors = Errors;
301       jcr->ReadBytes = ReadBytes;
302       jcr->JobBytes = JobBytes;
303       jcr->VSS = VSS;
304       jcr->Encrypt = Encrypt;
305    } else {
306       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
307    }
308
309 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
310 //   jcr->JobStatus, jcr->SDJobStatus);
311
312    /* Return the first error status we find Dir, FD, or SD */
313    if (!fd_ok || is_bnet_error(fd)) {
314       jcr->FDJobStatus = JS_ErrorTerminated;
315    }
316    if (jcr->JobStatus != JS_Terminated) {
317       return jcr->JobStatus;
318    }
319    if (jcr->FDJobStatus != JS_Terminated) {
320       return jcr->FDJobStatus;
321    }
322    return jcr->SDJobStatus;
323 }
324
325 /*
326  * Release resources allocated during backup.
327  */
328 void backup_cleanup(JCR *jcr, int TermCode)
329 {
330    char sdt[50], edt[50], schedt[50];
331    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
332    char ec6[30], ec7[30], ec8[30], elapsed[50];
333    char term_code[100], fd_term_msg[100], sd_term_msg[100];
334    const char *term_msg;
335    int msg_type = M_INFO;
336    MEDIA_DBR mr;
337    CLIENT_DBR cr;
338    double kbps, compression;
339    utime_t RunTime;
340
341    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
342    dequeue_messages(jcr);             /* display any queued messages */
343    memset(&mr, 0, sizeof(mr));
344    memset(&cr, 0, sizeof(cr));
345    set_jcr_job_status(jcr, TermCode);
346
347    update_job_end_record(jcr);        /* update database */
348
349    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
350       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
351          db_strerror(jcr->db));
352       set_jcr_job_status(jcr, JS_ErrorTerminated);
353    }
354
355    bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
356    if (!db_get_client_record(jcr, jcr->db, &cr)) {
357       Jmsg(jcr, M_WARNING, 0, _("Error getting client record for stats: %s"),
358          db_strerror(jcr->db));
359    }
360
361    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
362    if (!db_get_media_record(jcr, jcr->db, &mr)) {
363       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
364          mr.VolumeName, db_strerror(jcr->db));
365       set_jcr_job_status(jcr, JS_ErrorTerminated);
366    }
367
368    update_bootstrap_file(jcr);
369
370    switch (jcr->JobStatus) {
371       case JS_Terminated:
372          if (jcr->Errors || jcr->SDErrors) {
373             term_msg = _("Backup OK -- with warnings");
374          } else {
375             term_msg = _("Backup OK");
376          }
377          break;
378       case JS_FatalError:
379       case JS_ErrorTerminated:
380          term_msg = _("*** Backup Error ***");
381          msg_type = M_ERROR;          /* Generate error message */
382          if (jcr->store_bsock) {
383             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
384             if (jcr->SD_msg_chan) {
385                pthread_cancel(jcr->SD_msg_chan);
386             }
387          }
388          break;
389       case JS_Canceled:
390          term_msg = _("Backup Canceled");
391          if (jcr->store_bsock) {
392             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
393             if (jcr->SD_msg_chan) {
394                pthread_cancel(jcr->SD_msg_chan);
395             }
396          }
397          break;
398       default:
399          term_msg = term_code;
400          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
401          break;
402    }
403    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
404    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
405    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
406    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
407    if (RunTime <= 0) {
408       kbps = 0;
409    } else {
410       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
411    }
412    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
413       /*
414        * Note, if the job has erred, most likely it did not write any
415        *  tape, so suppress this "error" message since in that case
416        *  it is normal.  Or look at it the other way, only for a
417        *  normal exit should we complain about this error.
418        */
419       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
420          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
421       }
422       jcr->VolumeName[0] = 0;         /* none */
423    }
424
425    if (jcr->ReadBytes == 0) {
426       bstrncpy(compress, "None", sizeof(compress));
427    } else {
428       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
429       if (compression < 0.5) {
430          bstrncpy(compress, "None", sizeof(compress));
431       } else {
432          bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
433       }
434    }
435    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
436    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
437
438 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
439
440    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
441 "  JobId:                  %d\n"
442 "  Job:                    %s\n"
443 "  Backup Level:           %s%s\n"
444 "  Client:                 \"%s\" %s\n"
445 "  FileSet:                \"%s\" %s\n"
446 "  Pool:                   \"%s\" (From %s)\n"
447 "  Storage:                \"%s\" (From %s)\n"
448 "  Scheduled time:         %s\n"
449 "  Start time:             %s\n"
450 "  End time:               %s\n"
451 "  Elapsed time:           %s\n"
452 "  Priority:               %d\n"
453 "  FD Files Written:       %s\n"
454 "  SD Files Written:       %s\n"
455 "  FD Bytes Written:       %s (%sB)\n"
456 "  SD Bytes Written:       %s (%sB)\n"
457 "  Rate:                   %.1f KB/s\n"
458 "  Software Compression:   %s\n"
459 "  VSS:                    %s\n"
460 "  Encryption:             %s\n"
461 "  Volume name(s):         %s\n"
462 "  Volume Session Id:      %d\n"
463 "  Volume Session Time:    %d\n"
464 "  Last Volume Bytes:      %s (%sB)\n"
465 "  Non-fatal FD errors:    %d\n"
466 "  SD Errors:              %d\n"
467 "  FD termination status:  %s\n"
468 "  SD termination status:  %s\n"
469 "  Termination:            %s\n\n"),
470         VERSION,
471         LSMDATE,
472         edt,
473         jcr->jr.JobId,
474         jcr->jr.Job,
475         level_to_str(jcr->JobLevel), jcr->since,
476         jcr->client->name(), cr.Uname,
477         jcr->fileset->name(), jcr->FSCreateTime,
478         jcr->pool->name(), jcr->pool_source,
479         jcr->wstore->name(), jcr->wstore_source,
480         schedt,
481         sdt,
482         edt,
483         edit_utime(RunTime, elapsed, sizeof(elapsed)),
484         jcr->JobPriority,
485         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
486         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
487         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
488         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
489         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
490         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
491         (float)kbps,
492         compress,
493         jcr->VSS?"yes":"no",
494         jcr->Encrypt?"yes":"no",
495         jcr->VolumeName,
496         jcr->VolSessionId,
497         jcr->VolSessionTime,
498         edit_uint64_with_commas(mr.VolBytes, ec7),
499         edit_uint64_with_suffix(mr.VolBytes, ec8),
500         jcr->Errors,
501         jcr->SDErrors,
502         fd_term_msg,
503         sd_term_msg,
504         term_msg);
505
506    Dmsg0(100, "Leave backup_cleanup()\n");
507 }
508
509 void update_bootstrap_file(JCR *jcr)
510 {
511    /* Now update the bootstrap file if any */
512    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
513        jcr->job->WriteBootstrap) {
514       FILE *fd;
515       BPIPE *bpipe = NULL;
516       int got_pipe = 0;
517       POOLMEM *fname = get_pool_memory(PM_FNAME);
518       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
519
520       VOL_PARAMS *VolParams = NULL;
521       int VolCount;
522       char edt[50];
523
524       if (*fname == '|') {
525          got_pipe = 1;
526          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
527          fd = bpipe ? bpipe->wfd : NULL;
528       } else {
529          /* ***FIXME*** handle BASE */
530          fd = fopen(fname, jcr->JobLevel==L_FULL?"w+b":"a+b");
531       }
532       if (fd) {
533          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
534                     &VolParams);
535          if (VolCount == 0) {
536             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
537                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
538              if (jcr->SDJobFiles != 0) {
539                 set_jcr_job_status(jcr, JS_ErrorTerminated);
540              }
541
542          }
543          /* Start output with when and who wrote it */
544          bstrftimes(edt, sizeof(edt), time(NULL));
545          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
546                  level_to_str(jcr->JobLevel), jcr->since);
547          for (int i=0; i < VolCount; i++) {
548             /* Write the record */
549             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
550             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
551             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
552             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
553             fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
554                          VolParams[i].EndFile);
555             fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
556                          VolParams[i].EndBlock);
557             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
558                          VolParams[i].LastIndex);
559          }
560          if (VolParams) {
561             free(VolParams);
562          }
563          if (got_pipe) {
564             close_bpipe(bpipe);
565          } else {
566             fclose(fd);
567          }
568       } else {
569          berrno be;
570          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
571               "%s: ERR=%s\n"), fname, be.strerror());
572          set_jcr_job_status(jcr, JS_ErrorTerminated);
573       }
574       free_pool_memory(fname);
575    }
576 }