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