]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
Update
[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 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 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    fd->fsend(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    fd->fsend(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    /* Cancel SD */
248    cancel_storage_daemon_job(jcr);
249    wait_for_storage_daemon_termination(jcr);
250    Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count());
251    return false;
252 }
253
254
255 /*
256  * Here we wait for the File daemon to signal termination,
257  *   then we wait for the Storage daemon.  When both
258  *   are done, we return the job status.
259  * Also used by restore.c
260  */
261 int wait_for_job_termination(JCR *jcr)
262 {
263    int32_t n = 0;
264    BSOCK *fd = jcr->file_bsock;
265    bool fd_ok = false;
266    uint32_t JobFiles, Errors;
267    uint64_t ReadBytes = 0;
268    uint64_t JobBytes = 0;
269    int VSS = 0;
270    int Encrypt = 0;
271
272    set_jcr_job_status(jcr, JS_Running);
273    /* Wait for Client to terminate */
274    while ((n = bget_dirmsg(fd)) >= 0) {
275       if (!fd_ok && 
276           (sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
277               &ReadBytes, &JobBytes, &Errors, &VSS, &Encrypt) == 7 ||
278            sscanf(fd->msg, OldEndJob, &jcr->FDJobStatus, &JobFiles,
279                  &ReadBytes, &JobBytes, &Errors) == 5)) {
280          fd_ok = true;
281          set_jcr_job_status(jcr, jcr->FDJobStatus);
282          Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
283       } else {
284          Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
285             fd->msg);
286       }
287       if (job_canceled(jcr)) {
288          break;
289       }
290    }
291
292    if (is_bnet_error(fd)) {
293       Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
294           job_type_to_str(jcr->JobType), fd->bstrerror());
295    }
296    fd->signal(BNET_TERMINATE);   /* tell Client we are terminating */
297
298    /* Force cancel in SD if failing */
299    if (job_canceled(jcr) || !fd_ok) {
300       cancel_storage_daemon_job(jcr);
301    }
302
303    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
304    wait_for_storage_daemon_termination(jcr);
305
306
307    /* Return values from FD */
308    if (fd_ok) {
309       jcr->JobFiles = JobFiles;
310       jcr->Errors = Errors;
311       jcr->ReadBytes = ReadBytes;
312       jcr->JobBytes = JobBytes;
313       jcr->VSS = VSS;
314       jcr->Encrypt = Encrypt;
315    } else {
316       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
317    }
318
319 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
320 //   jcr->JobStatus, jcr->SDJobStatus);
321
322    /* Return the first error status we find Dir, FD, or SD */
323    if (!fd_ok || is_bnet_error(fd)) {
324       jcr->FDJobStatus = JS_ErrorTerminated;
325    }
326    if (jcr->JobStatus != JS_Terminated) {
327       return jcr->JobStatus;
328    }
329    if (jcr->FDJobStatus != JS_Terminated) {
330       return jcr->FDJobStatus;
331    }
332    return jcr->SDJobStatus;
333 }
334
335 /*
336  * Release resources allocated during backup.
337  */
338 void backup_cleanup(JCR *jcr, int TermCode)
339 {
340    char sdt[50], edt[50], schedt[50];
341    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
342    char ec6[30], ec7[30], ec8[30], elapsed[50];
343    char term_code[100], fd_term_msg[100], sd_term_msg[100];
344    const char *term_msg;
345    int msg_type = M_INFO;
346    MEDIA_DBR mr;
347    CLIENT_DBR cr;
348    double kbps, compression;
349    utime_t RunTime;
350
351    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
352    memset(&mr, 0, sizeof(mr));
353    memset(&cr, 0, sizeof(cr));
354
355    update_job_end(jcr, TermCode);
356
357    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
358       Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
359          db_strerror(jcr->db));
360       set_jcr_job_status(jcr, JS_ErrorTerminated);
361    }
362
363    bstrncpy(cr.Name, jcr->client->name(), sizeof(cr.Name));
364    if (!db_get_client_record(jcr, jcr->db, &cr)) {
365       Jmsg(jcr, M_WARNING, 0, _("Error getting Client record for Job report: ERR=%s"),
366          db_strerror(jcr->db));
367    }
368
369    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
370    if (!db_get_media_record(jcr, jcr->db, &mr)) {
371       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
372          mr.VolumeName, db_strerror(jcr->db));
373       set_jcr_job_status(jcr, JS_ErrorTerminated);
374    }
375
376    update_bootstrap_file(jcr);
377
378    switch (jcr->JobStatus) {
379       case JS_Terminated:
380          if (jcr->Errors || jcr->SDErrors) {
381             term_msg = _("Backup OK -- with warnings");
382          } else {
383             term_msg = _("Backup OK");
384          }
385          break;
386       case JS_FatalError:
387       case JS_ErrorTerminated:
388          term_msg = _("*** Backup Error ***");
389          msg_type = M_ERROR;          /* Generate error message */
390          if (jcr->store_bsock) {
391             jcr->store_bsock->signal(BNET_TERMINATE);
392             if (jcr->SD_msg_chan) {
393                pthread_cancel(jcr->SD_msg_chan);
394             }
395          }
396          break;
397       case JS_Canceled:
398          term_msg = _("Backup Canceled");
399          if (jcr->store_bsock) {
400             jcr->store_bsock->signal(BNET_TERMINATE);
401             if (jcr->SD_msg_chan) {
402                pthread_cancel(jcr->SD_msg_chan);
403             }
404          }
405          break;
406       default:
407          term_msg = term_code;
408          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
409          break;
410    }
411    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
412    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
413    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
414    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
415    if (RunTime <= 0) {
416       kbps = 0;
417    } else {
418       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
419    }
420    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
421       /*
422        * Note, if the job has erred, most likely it did not write any
423        *  tape, so suppress this "error" message since in that case
424        *  it is normal.  Or look at it the other way, only for a
425        *  normal exit should we complain about this error.
426        */
427       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
428          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
429       }
430       jcr->VolumeName[0] = 0;         /* none */
431    }
432
433    if (jcr->ReadBytes == 0) {
434       bstrncpy(compress, "None", sizeof(compress));
435    } else {
436       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
437       if (compression < 0.5) {
438          bstrncpy(compress, "None", sizeof(compress));
439       } else {
440          bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
441       }
442    }
443    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
444    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
445
446 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
447
448    Jmsg(jcr, msg_type, 0, _("Bacula %s %s (%s): %s\n"
449 "  Build OS:               %s %s %s\n"
450 "  JobId:                  %d\n"
451 "  Job:                    %s\n"
452 "  Backup Level:           %s%s\n"
453 "  Client:                 \"%s\" %s\n"
454 "  FileSet:                \"%s\" %s\n"
455 "  Pool:                   \"%s\" (From %s)\n"
456 "  Storage:                \"%s\" (From %s)\n"
457 "  Scheduled time:         %s\n"
458 "  Start time:             %s\n"
459 "  End time:               %s\n"
460 "  Elapsed time:           %s\n"
461 "  Priority:               %d\n"
462 "  FD Files Written:       %s\n"
463 "  SD Files Written:       %s\n"
464 "  FD Bytes Written:       %s (%sB)\n"
465 "  SD Bytes Written:       %s (%sB)\n"
466 "  Rate:                   %.1f KB/s\n"
467 "  Software Compression:   %s\n"
468 "  VSS:                    %s\n"
469 "  Encryption:             %s\n"
470 "  Volume name(s):         %s\n"
471 "  Volume Session Id:      %d\n"
472 "  Volume Session Time:    %d\n"
473 "  Last Volume Bytes:      %s (%sB)\n"
474 "  Non-fatal FD errors:    %d\n"
475 "  SD Errors:              %d\n"
476 "  FD termination status:  %s\n"
477 "  SD termination status:  %s\n"
478 "  Termination:            %s\n\n"),
479         my_name, VERSION, LSMDATE, edt,
480         HOST_OS, DISTNAME, DISTVER,
481         jcr->jr.JobId,
482         jcr->jr.Job,
483         level_to_str(jcr->JobLevel), jcr->since,
484         jcr->client->name(), cr.Uname,
485         jcr->fileset->name(), jcr->FSCreateTime,
486         jcr->pool->name(), jcr->pool_source,
487         jcr->wstore->name(), jcr->wstore_source,
488         schedt,
489         sdt,
490         edt,
491         edit_utime(RunTime, elapsed, sizeof(elapsed)),
492         jcr->JobPriority,
493         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
494         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
495         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
496         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
497         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
498         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
499         (float)kbps,
500         compress,
501         jcr->VSS?"yes":"no",
502         jcr->Encrypt?"yes":"no",
503         jcr->VolumeName,
504         jcr->VolSessionId,
505         jcr->VolSessionTime,
506         edit_uint64_with_commas(mr.VolBytes, ec7),
507         edit_uint64_with_suffix(mr.VolBytes, ec8),
508         jcr->Errors,
509         jcr->SDErrors,
510         fd_term_msg,
511         sd_term_msg,
512         term_msg);
513
514    Dmsg0(100, "Leave backup_cleanup()\n");
515 }
516
517 void update_bootstrap_file(JCR *jcr)
518 {
519    /* Now update the bootstrap file if any */
520    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
521        jcr->job->WriteBootstrap) {
522       FILE *fd;
523       BPIPE *bpipe = NULL;
524       int got_pipe = 0;
525       POOLMEM *fname = get_pool_memory(PM_FNAME);
526       fname = edit_job_codes(jcr, fname, jcr->job->WriteBootstrap, "");
527
528       VOL_PARAMS *VolParams = NULL;
529       int VolCount;
530       char edt[50];
531
532       if (*fname == '|') {
533          got_pipe = 1;
534          bpipe = open_bpipe(fname+1, 0, "w"); /* skip first char "|" */
535          fd = bpipe ? bpipe->wfd : NULL;
536       } else {
537          /* ***FIXME*** handle BASE */
538          fd = fopen(fname, jcr->JobLevel==L_FULL?"w+b":"a+b");
539       }
540       if (fd) {
541          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
542                     &VolParams);
543          if (VolCount == 0) {
544             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
545                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
546              if (jcr->SDJobFiles != 0) {
547                 set_jcr_job_status(jcr, JS_ErrorTerminated);
548              }
549
550          }
551          /* Start output with when and who wrote it */
552          bstrftimes(edt, sizeof(edt), time(NULL));
553          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
554                  level_to_str(jcr->JobLevel), jcr->since);
555          for (int i=0; i < VolCount; i++) {
556             /* Write the record */
557             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
558             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
559             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
560             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
561             fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
562                          VolParams[i].EndFile);
563             fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
564                          VolParams[i].EndBlock);
565             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
566                          VolParams[i].LastIndex);
567          }
568          if (VolParams) {
569             free(VolParams);
570          }
571          if (got_pipe) {
572             close_bpipe(bpipe);
573          } else {
574             fclose(fd);
575          }
576       } else {
577          berrno be;
578          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
579               "%s: ERR=%s\n"), fname, be.bstrerror());
580          set_jcr_job_status(jcr, JS_ErrorTerminated);
581       }
582       free_pool_memory(fname);
583    }
584 }