]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
c90f7b5141422668cc6c3f8deebb7623da33afb1
[bacula/bacula] / bacula / src / dird / backup.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- backup.c -- responsible for doing backup jobs
31  *
32  *     Kern Sibbald, March MM
33  *
34  *  Basic tasks done here:
35  *     Open DB and create records for this job.
36  *     Open Message Channel with Storage daemon to tell him a job will be starting.
37  *     Open connection with File daemon and pass him commands
38  *       to do the backup.
39  *     When the File daemon finishes the job, update the DB.
40  *
41  *   Version $Id$
42  */
43
44 #include "bacula.h"
45 #include "dird.h"
46 #include "ua.h"
47
48 /* Commands sent to File daemon */
49 static char backupcmd[] = "backup\n";
50 static char storaddr[]  = "storage address=%s port=%d ssl=%d\n";
51
52 /* Responses received from File daemon */
53 static char OKbackup[]   = "2000 OK backup\n";
54 static char OKstore[]    = "2000 OK storage\n";
55 static char EndJob[]     = "2800 End Job TermCode=%d JobFiles=%u "
56                            "ReadBytes=%llu JobBytes=%llu Errors=%u "  
57                            "VSS=%d Encrypt=%d\n";
58 /* Pre 1.39.29 (04Dec06) EndJob */
59 static char OldEndJob[]  = "2800 End Job TermCode=%d JobFiles=%u "
60                            "ReadBytes=%llu JobBytes=%llu Errors=%u\n";
61 /* 
62  * Called here before the job is run to do the job
63  *   specific setup.
64  */
65 bool do_backup_init(JCR *jcr)
66 {
67
68    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->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    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
237    if (stat == JS_Terminated) {
238       backup_cleanup(jcr, stat);
239       return true;
240    }     
241    return false;
242
243 /* Come here only after starting SD thread */
244 bail_out:
245    set_jcr_job_status(jcr, JS_ErrorTerminated);
246    Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count());
247    wait_for_storage_daemon_termination(jcr);
248    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
249    return false;
250 }
251
252
253 /*
254  * Here we wait for the File daemon to signal termination,
255  *   then we wait for the Storage daemon.  When both
256  *   are done, we return the job status.
257  * Also used by restore.c
258  */
259 int wait_for_job_termination(JCR *jcr)
260 {
261    int32_t n = 0;
262    BSOCK *fd = jcr->file_bsock;
263    bool fd_ok = false;
264    uint32_t JobFiles, Errors;
265    uint64_t ReadBytes = 0;
266    uint64_t JobBytes = 0;
267    int VSS = 0;
268    int Encrypt = 0;
269
270    set_jcr_job_status(jcr, JS_Running);
271    /* Wait for Client to terminate */
272    while ((n = bget_dirmsg(fd)) >= 0) {
273       if (!fd_ok && 
274           (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
275               &ReadBytes, &JobBytes, &Errors, &VSS, &Encrypt) == 7 ||
276            sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
277                  &ReadBytes, &JobBytes, &Errors) == 5)) {
278          fd_ok = true;
279          set_jcr_job_status(jcr, jcr->FDJobStatus);
280          Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
281       } else {
282          Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
283             fd->msg);
284       }
285       if (job_canceled(jcr)) {
286          break;
287       }
288    }
289
290    if (is_bnet_error(fd)) {
291       Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
292           job_type_to_str(jcr->JobType), bnet_strerror(fd));
293    }
294    bnet_sig(fd, BNET_TERMINATE);   /* tell Client we are terminating */
295
296    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
297    wait_for_storage_daemon_termination(jcr);
298
299
300    /* Return values from FD */
301    if (fd_ok) {
302       jcr->JobFiles = JobFiles;
303       jcr->Errors = Errors;
304       jcr->ReadBytes = ReadBytes;
305       jcr->JobBytes = JobBytes;
306       jcr->VSS = VSS;
307       jcr->Encrypt = Encrypt;
308    } else {
309       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
310    }
311
312 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
313 //   jcr->JobStatus, jcr->SDJobStatus);
314
315    /* Return the first error status we find Dir, FD, or SD */
316    if (!fd_ok || is_bnet_error(fd)) {
317       jcr->FDJobStatus = JS_ErrorTerminated;
318    }
319    if (jcr->JobStatus != JS_Terminated) {
320       return jcr->JobStatus;
321    }
322    if (jcr->FDJobStatus != JS_Terminated) {
323       return jcr->FDJobStatus;
324    }
325    return jcr->SDJobStatus;
326 }
327
328 /*
329  * Release resources allocated during backup.
330  */
331 void backup_cleanup(JCR *jcr, int TermCode)
332 {
333    char sdt[50], edt[50], schedt[50];
334    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
335    char ec6[30], ec7[30], ec8[30], elapsed[50];
336    char term_code[100], fd_term_msg[100], sd_term_msg[100];
337    const char *term_msg;
338    int msg_type = M_INFO;
339    MEDIA_DBR mr;
340    CLIENT_DBR cr;
341    double kbps, compression;
342    utime_t RunTime;
343
344    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
345    memset(&mr, 0, sizeof(mr));
346    memset(&cr, 0, sizeof(cr));
347
348    update_job_end(jcr, TermCode);
349
350    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
351       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
352          db_strerror(jcr->db));
353       set_jcr_job_status(jcr, JS_ErrorTerminated);
354    }
355
356    bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
357    if (!db_get_client_record(jcr, jcr->db, &cr)) {
358       Jmsg(jcr, M_WARNING, 0, _("Error getting client record for stats: %s"),
359          db_strerror(jcr->db));
360    }
361
362    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
363    if (!db_get_media_record(jcr, jcr->db, &mr)) {
364       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
365          mr.VolumeName, db_strerror(jcr->db));
366       set_jcr_job_status(jcr, JS_ErrorTerminated);
367    }
368
369    update_bootstrap_file(jcr);
370
371    switch (jcr->JobStatus) {
372       case JS_Terminated:
373          if (jcr->Errors || jcr->SDErrors) {
374             term_msg = _("Backup OK -- with warnings");
375          } else {
376             term_msg = _("Backup OK");
377          }
378          break;
379       case JS_FatalError:
380       case JS_ErrorTerminated:
381          term_msg = _("*** Backup Error ***");
382          msg_type = M_ERROR;          /* Generate error message */
383          if (jcr->store_bsock) {
384             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
385             if (jcr->SD_msg_chan) {
386                pthread_cancel(jcr->SD_msg_chan);
387             }
388          }
389          break;
390       case JS_Canceled:
391          term_msg = _("Backup Canceled");
392          if (jcr->store_bsock) {
393             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
394             if (jcr->SD_msg_chan) {
395                pthread_cancel(jcr->SD_msg_chan);
396             }
397          }
398          break;
399       default:
400          term_msg = term_code;
401          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
402          break;
403    }
404    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
405    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
406    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
407    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
408    if (RunTime <= 0) {
409       kbps = 0;
410    } else {
411       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
412    }
413    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
414       /*
415        * Note, if the job has erred, most likely it did not write any
416        *  tape, so suppress this "error" message since in that case
417        *  it is normal.  Or look at it the other way, only for a
418        *  normal exit should we complain about this error.
419        */
420       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
421          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
422       }
423       jcr->VolumeName[0] = 0;         /* none */
424    }
425
426    if (jcr->ReadBytes == 0) {
427       bstrncpy(compress, "None", sizeof(compress));
428    } else {
429       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
430       if (compression < 0.5) {
431          bstrncpy(compress, "None", sizeof(compress));
432       } else {
433          bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
434       }
435    }
436    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
437    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
438
439 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
440
441    Jmsg(jcr, msg_type, 0, _("Bacula %s Version: %s (%s) %s %s %s at %s\n"
442 "  JobId:                  %d\n"
443 "  Job:                    %s\n"
444 "  Backup Level:           %s%s\n"
445 "  Client:                 \"%s\" %s\n"
446 "  FileSet:                \"%s\" %s\n"
447 "  Pool:                   \"%s\" (From %s)\n"
448 "  Storage:                \"%s\" (From %s)\n"
449 "  Scheduled time:         %s\n"
450 "  Start time:             %s\n"
451 "  End time:               %s\n"
452 "  Elapsed time:           %s\n"
453 "  Priority:               %d\n"
454 "  FD Files Written:       %s\n"
455 "  SD Files Written:       %s\n"
456 "  FD Bytes Written:       %s (%sB)\n"
457 "  SD Bytes Written:       %s (%sB)\n"
458 "  Rate:                   %.1f KB/s\n"
459 "  Software Compression:   %s\n"
460 "  VSS:                    %s\n"
461 "  Encryption:             %s\n"
462 "  Volume name(s):         %s\n"
463 "  Volume Session Id:      %d\n"
464 "  Volume Session Time:    %d\n"
465 "  Last Volume Bytes:      %s (%sB)\n"
466 "  Non-fatal FD errors:    %d\n"
467 "  SD Errors:              %d\n"
468 "  FD termination status:  %s\n"
469 "  SD termination status:  %s\n"
470 "  Termination:            %s\n\n"),
471         my_name, VERSION, BDATE, HOST_OS, DISTNAME, DISTVER,
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 }