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