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