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