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