]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
9d0726f0dd593fcbde975125381b0958dbdff978
[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 /*
18    Copyright (C) 2000-2004 Kern Sibbald and John Walker
19
20    This program is free software; you can redistribute it and/or
21    modify it under the terms of the GNU General Public License as
22    published by the Free Software Foundation; either version 2 of
23    the License, or (at your option) any later version.
24
25    This program is distributed in the hope that it will be useful,
26    but WITHOUT ANY WARRANTY; without even the implied warranty of
27    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28    General Public License for more details.
29
30    You should have received a copy of the GNU General Public
31    License along with this program; if not, write to the Free
32    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
33    MA 02111-1307, USA.
34
35  */
36
37 #include "bacula.h"
38 #include "dird.h"
39 #include "ua.h"
40
41 /* Commands sent to File daemon */
42 static char backupcmd[] = "backup\n";
43 static char storaddr[]  = "storage address=%s port=%d ssl=%d\n";
44
45 /* Responses received from File daemon */
46 static char OKbackup[]   = "2000 OK backup\n";
47 static char OKstore[]    = "2000 OK storage\n";
48 static char EndJob[]     = "2800 End Job TermCode=%d JobFiles=%u "
49                            "ReadBytes=%" lld " JobBytes=%" lld " Errors=%u\n";
50
51
52 /* Forward referenced functions */
53 static void backup_cleanup(JCR *jcr, int TermCode, char *since, FILESET_DBR *fsr);
54
55 /* External functions */
56
57 /* 
58  * Do a backup of the specified FileSet
59  *    
60  *  Returns:  0 on failure
61  *            1 on success
62  */
63 int do_backup(JCR *jcr) 
64 {
65    char since[MAXSTRING];
66    int stat;
67    BSOCK   *fd;
68    POOL_DBR pr;
69    FILESET_DBR fsr;
70
71    since[0] = 0;
72
73    if (!get_or_create_client_record(jcr)) {
74       goto bail_out;
75    }
76
77    if (!get_or_create_fileset_record(jcr, &fsr)) {
78       goto bail_out;
79    }
80
81    get_level_since_time(jcr, since, sizeof(since));
82
83    jcr->fname = (char *) get_pool_memory(PM_FNAME);
84
85    /* 
86     * Get the Pool record -- first apply any level defined pools  
87     */
88    switch (jcr->JobLevel) {
89    case L_FULL:
90       if (jcr->full_pool) {
91          jcr->pool = jcr->full_pool;   
92       }
93       break;
94    case L_INCREMENTAL:
95       if (jcr->inc_pool) {
96          jcr->pool = jcr->inc_pool;   
97       }
98       break;
99    case L_DIFFERENTIAL:
100       if (jcr->dif_pool) {
101          jcr->pool = jcr->dif_pool;   
102       }
103       break;
104    }
105    memset(&pr, 0, sizeof(pr));
106    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
107
108    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
109       /* Try to create the pool */
110       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
111          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name, 
112             db_strerror(jcr->db));
113          goto bail_out;
114       } else {
115          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
116       }
117    }
118    jcr->PoolId = pr.PoolId;               /****FIXME**** this can go away */
119    jcr->jr.PoolId = pr.PoolId;
120
121
122    /* Print Job Start message */
123    Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %u, Job=%s\n"),
124         jcr->JobId, jcr->Job);
125
126    set_jcr_job_status(jcr, JS_Running);
127    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
128    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
129       Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
130       goto bail_out;
131    }
132
133
134    /*
135     * Open a message channel connection with the Storage
136     * daemon. This is to let him know that our client
137     * will be contacting him for a backup  session.
138     *
139     */
140    Dmsg0(110, "Open connection with storage daemon\n");
141    set_jcr_job_status(jcr, JS_WaitSD);
142    /*
143     * Start conversation with Storage daemon  
144     */
145    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
146       goto bail_out;
147    }
148    /*
149     * Now start a job with the Storage daemon
150     */
151    if (!start_storage_daemon_job(jcr)) {
152       goto bail_out;
153    }
154    /*
155     * Now start a Storage daemon message thread
156     */
157    if (!start_storage_daemon_message_thread(jcr)) {
158       goto bail_out;
159    }
160    Dmsg0(150, "Storage daemon connection OK\n");
161
162    set_jcr_job_status(jcr, JS_WaitFD);
163    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
164       goto bail_out;
165    }
166
167    set_jcr_job_status(jcr, JS_Running);
168    fd = jcr->file_bsock;
169
170    if (!send_include_list(jcr)) {
171       goto bail_out;
172    }
173
174    if (!send_exclude_list(jcr)) {
175       goto bail_out;
176    }
177
178    if (!send_level_command(jcr)) {
179       goto bail_out;
180    }
181
182    /* 
183     * send Storage daemon address to the File daemon
184     */
185    if (jcr->store->SDDport == 0) {
186       jcr->store->SDDport = jcr->store->SDport;
187    }
188    bnet_fsend(fd, storaddr, jcr->store->address, jcr->store->SDDport,
189               jcr->store->enable_ssl);
190    if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
191       goto bail_out;
192    }
193
194
195    if (!send_run_before_and_after_commands(jcr)) {
196       goto bail_out;
197    }
198
199    /* Send backup command */
200    bnet_fsend(fd, backupcmd);
201    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
202       goto bail_out;
203    }
204
205    /* Pickup Job termination data */        
206    stat = wait_for_job_termination(jcr);
207    backup_cleanup(jcr, stat, since, &fsr);
208    return 1;
209
210 bail_out:
211    backup_cleanup(jcr, JS_ErrorTerminated, since, &fsr);
212    return 0;
213 }
214
215 /*
216  * Here we wait for the File daemon to signal termination,
217  *   then we wait for the Storage daemon.  When both
218  *   are done, we return the job status.
219  * Also used by restore.c 
220  */
221 int wait_for_job_termination(JCR *jcr)
222 {
223    int32_t n = 0;
224    BSOCK *fd = jcr->file_bsock;
225    bool fd_ok = false;
226    uint32_t JobFiles, Errors;
227    uint64_t ReadBytes, JobBytes;
228
229    set_jcr_job_status(jcr, JS_Running);
230    /* Wait for Client to terminate */
231    while ((n = bget_dirmsg(fd)) >= 0) {
232       if (!fd_ok && sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
233           &ReadBytes, &JobBytes, &Errors) == 5) {
234          fd_ok = true;
235          set_jcr_job_status(jcr, jcr->FDJobStatus);
236          Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
237       } else {
238          Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
239             fd->msg);
240       }
241       if (job_canceled(jcr)) {
242          break;
243       }
244    }
245    if (is_bnet_error(fd)) {
246       Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
247           job_type_to_str(jcr->JobType), bnet_strerror(fd));
248    }
249    bnet_sig(fd, BNET_TERMINATE);   /* tell Client we are terminating */
250
251    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
252    wait_for_storage_daemon_termination(jcr);
253
254
255    /* Return values from FD */
256    if (fd_ok) {
257       jcr->JobFiles = JobFiles;
258       jcr->Errors = Errors;
259       jcr->ReadBytes = ReadBytes;
260       jcr->JobBytes = JobBytes;
261    } else {
262       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
263    }
264
265 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
266 //   jcr->JobStatus, jcr->SDJobStatus);
267
268    /* Return the first error status we find Dir, FD, or SD */
269    if (!fd_ok || is_bnet_error(fd)) {                          
270       jcr->FDJobStatus = JS_ErrorTerminated;
271    }
272    if (jcr->JobStatus != JS_Terminated) {
273       return jcr->JobStatus;
274    }
275    if (jcr->FDJobStatus != JS_Terminated) {
276       return jcr->FDJobStatus;
277    }
278    return jcr->SDJobStatus;
279 }
280
281 /*
282  * Release resources allocated during backup.
283  */
284 static void backup_cleanup(JCR *jcr, int TermCode, char *since, FILESET_DBR *fsr) 
285 {
286    char sdt[50], edt[50];
287    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
288    char term_code[100], fd_term_msg[100], sd_term_msg[100];
289    const char *term_msg;
290    int msg_type;
291    MEDIA_DBR mr;
292    double kbps, compression;
293    utime_t RunTime;
294
295    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
296    memset(&mr, 0, sizeof(mr));
297    set_jcr_job_status(jcr, TermCode);
298
299    update_job_end_record(jcr);        /* update database */
300    
301    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
302       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"), 
303          db_strerror(jcr->db));
304       set_jcr_job_status(jcr, JS_ErrorTerminated);
305    }
306
307    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
308    if (!db_get_media_record(jcr, jcr->db, &mr)) {
309       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"), 
310          mr.VolumeName, db_strerror(jcr->db));
311       set_jcr_job_status(jcr, JS_ErrorTerminated);
312    }
313
314    /* Now update the bootstrap file if any */
315    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes && 
316        jcr->job->WriteBootstrap) {
317       FILE *fd;
318       BPIPE *bpipe = NULL;
319       int got_pipe = 0;
320       char *fname = jcr->job->WriteBootstrap;
321       VOL_PARAMS *VolParams = NULL;
322       int VolCount;
323
324       if (*fname == '|') {
325          fname++;
326          got_pipe = 1;
327          bpipe = open_bpipe(fname, 0, "w");
328          fd = bpipe ? bpipe->wfd : NULL;
329       } else {
330          /* ***FIXME*** handle BASE */
331          fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
332       }
333       if (fd) {
334          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
335                     &VolParams);
336          if (VolCount == 0) {
337             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "      
338                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
339              if (jcr->SDJobFiles != 0) {
340                 set_jcr_job_status(jcr, JS_ErrorTerminated);
341              }
342
343          }
344          for (int i=0; i < VolCount; i++) {
345             /* Write the record */
346             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
347             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
348             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
349             fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
350                          VolParams[i].EndFile);
351             fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
352                          VolParams[i].EndBlock);
353             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
354                          VolParams[i].LastIndex);
355          }
356          if (VolParams) {
357             free(VolParams);
358          }
359          if (got_pipe) {
360             close_bpipe(bpipe);
361          } else {
362             fclose(fd);
363          }
364       } else {
365          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
366               "%s: ERR=%s\n"), fname, strerror(errno));
367          set_jcr_job_status(jcr, JS_ErrorTerminated);
368       }
369    }
370
371    msg_type = M_INFO;                 /* by default INFO message */
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    bstrftime(sdt, sizeof(sdt), jcr->jr.StartTime);
406    bstrftime(edt, sizeof(edt), jcr->jr.EndTime);
407    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
408    if (RunTime <= 0) {
409       kbps = 0;
410    } else {
411       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
412    }
413    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
414       /*
415        * Note, if the job has erred, most likely it did not write any
416        *  tape, so suppress this "error" message since in that case
417        *  it is normal.  Or look at it the other way, only for a
418        *  normal exit should we complain about this error.
419        */
420       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {                                
421          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
422       }
423       jcr->VolumeName[0] = 0;         /* none */
424    }
425
426    if (jcr->ReadBytes == 0) {
427       bstrncpy(compress, "None", sizeof(compress));
428    } else {
429       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
430       if (compression < 0.5) {
431          bstrncpy(compress, "None", sizeof(compress));
432       } else {
433          bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
434       }
435    }
436    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
437    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
438
439 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
440
441    Jmsg(jcr, msg_type, 0, _("Bacula " VERSION " (" LSMDATE "): %s\n\
442 JobId:                  %d\n\
443 Job:                    %s\n\
444 Backup Level:           %s%s\n\
445 Client:                 %s\n\
446 FileSet:                \"%s\" %s\n\
447 Start time:             %s\n\
448 End time:               %s\n\
449 FD Files Written:       %s\n\
450 SD Files Written:       %s\n\
451 FD Bytes Written:       %s\n\
452 SD Bytes Written:       %s\n\
453 Rate:                   %.1f KB/s\n\
454 Software Compression:   %s\n\
455 Volume name(s):         %s\n\
456 Volume Session Id:      %d\n\
457 Volume Session Time:    %d\n\
458 Last Volume Bytes:      %s\n\
459 Non-fatal FD errors:    %d\n\
460 SD Errors:              %d\n\
461 FD termination status:  %s\n\
462 SD termination status:  %s\n\
463 Termination:            %s\n\n"),
464         edt,
465         jcr->jr.JobId,
466         jcr->jr.Job,
467         level_to_str(jcr->JobLevel), since,
468         jcr->client->hdr.name,
469         jcr->fileset->hdr.name, fsr->cCreateTime,
470         sdt,
471         edt,
472         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
473         edit_uint64_with_commas(jcr->SDJobFiles, ec4),
474         edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
475         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
476         (float)kbps,
477         compress,
478         jcr->VolumeName,
479         jcr->VolSessionId,
480         jcr->VolSessionTime,
481         edit_uint64_with_commas(mr.VolBytes, ec3),
482         jcr->Errors,
483         jcr->SDErrors,
484         fd_term_msg,
485         sd_term_msg,
486         term_msg);
487
488    Dmsg0(100, "Leave backup_cleanup()\n");
489 }