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