]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/backup.c
- Implement first cut of Migration.
[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->PoolId = pr.PoolId;
101    jcr->jr.PoolId = pr.PoolId;
102
103    /*
104     * Fire off any clone jobs (run directives)
105     */
106    Dmsg2(900, "cloned=%d run_cmds=%p\n", jcr->cloned, jcr->job->run_cmds);
107    if (!jcr->cloned && jcr->job->run_cmds) {
108       char *runcmd;
109       JOB *job = jcr->job;
110       POOLMEM *cmd = get_pool_memory(PM_FNAME);
111       UAContext *ua = new_ua_context(jcr);
112       ua->batch = true;
113       foreach_alist(runcmd, job->run_cmds) {
114          cmd = edit_job_codes(jcr, cmd, runcmd, "");              
115          Mmsg(ua->cmd, "run %s cloned=yes", cmd);
116          Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
117          parse_ua_args(ua);                 /* parse command */
118          int stat = run_cmd(ua, ua->cmd);
119          if (stat == 0) {
120             Jmsg(jcr, M_ERROR, 0, _("Could not start clone job.\n"));
121          } else {
122             Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), stat);
123          }
124       }
125       free_ua_context(ua);
126       free_pool_memory(cmd);
127    }
128
129    return true;
130 }
131
132 /*
133  * Do a backup of the specified FileSet
134  *
135  *  Returns:  false on failure
136  *            true  on success
137  */
138 bool do_backup(JCR *jcr)
139 {
140    int stat;
141    int tls_need = BNET_TLS_NONE;
142    BSOCK   *fd;
143    STORE *store;
144    char ed1[100];
145
146
147    /* Print Job Start message */
148    Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"),
149         edit_uint64(jcr->JobId, ed1), jcr->Job);
150
151    set_jcr_job_status(jcr, JS_Running);
152    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
153    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
154       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
155       return false;
156    }
157
158    /*
159     * Open a message channel connection with the Storage
160     * daemon. This is to let him know that our client
161     * will be contacting him for a backup  session.
162     *
163     */
164    Dmsg0(110, "Open connection with storage daemon\n");
165    set_jcr_job_status(jcr, JS_WaitSD);
166    /*
167     * Start conversation with Storage daemon
168     */
169    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
170       return false;
171    }
172    /*
173     * Now start a job with the Storage daemon
174     */
175    if (!start_storage_daemon_job(jcr, NULL, jcr->storage)) {
176       return false;
177    }
178    /*
179     * Now start a Storage daemon message thread.  Note,
180     *   this thread is used to provide the catalog services
181     *   for the backup job, including inserting the attributes
182     *   into the catalog.  See catalog_update() in catreq.c
183     */
184    if (!start_storage_daemon_message_thread(jcr)) {
185       return false;
186    }
187    Dmsg0(150, "Storage daemon connection OK\n");
188
189    if (!bnet_fsend(jcr->store_bsock, "run")) {
190       return false;
191    }
192
193    set_jcr_job_status(jcr, JS_WaitFD);
194    if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) {
195       return false;
196    }
197
198    set_jcr_job_status(jcr, JS_Running);
199    fd = jcr->file_bsock;
200
201    if (!send_include_list(jcr)) {
202       return false;
203    }
204
205    if (!send_exclude_list(jcr)) {
206       return false;
207    }
208
209    if (!send_level_command(jcr)) {
210       return false;
211    }
212
213    /*
214     * send Storage daemon address to the File daemon
215     */
216    store = jcr->store;
217    if (store->SDDport == 0) {
218       store->SDDport = store->SDport;
219    }
220
221    /* TLS Requirement */
222    if (store->tls_enable) {
223       if (store->tls_require) {
224          tls_need = BNET_TLS_REQUIRED;
225       } else {
226          tls_need = BNET_TLS_OK;
227       }
228    }
229
230    bnet_fsend(fd, storaddr, store->address, store->SDDport, tls_need);
231    if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) {
232       return false;
233    }
234
235
236    if (!send_run_before_and_after_commands(jcr)) {
237       return false;
238    }
239
240    /* Send backup command */
241    bnet_fsend(fd, backupcmd);
242    if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
243       return false;
244    }
245
246    /* Pickup Job termination data */
247    stat = wait_for_job_termination(jcr);
248    if (stat == JS_Terminated) {
249       backup_cleanup(jcr, stat);
250       return true;
251    }     
252    return false;
253 }
254
255
256 /*
257  * Here we wait for the File daemon to signal termination,
258  *   then we wait for the Storage daemon.  When both
259  *   are done, we return the job status.
260  * Also used by restore.c
261  */
262 int wait_for_job_termination(JCR *jcr)
263 {
264    int32_t n = 0;
265    BSOCK *fd = jcr->file_bsock;
266    bool fd_ok = false;
267    uint32_t JobFiles, Errors;
268    uint64_t ReadBytes, JobBytes;
269
270    set_jcr_job_status(jcr, JS_Running);
271    /* Wait for Client to terminate */
272    while ((n = bget_dirmsg(fd)) >= 0) {
273       if (!fd_ok && sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles,
274           &ReadBytes, &JobBytes, &Errors) == 5) {
275          fd_ok = true;
276          set_jcr_job_status(jcr, jcr->FDJobStatus);
277          Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus);
278       } else {
279          Jmsg(jcr, M_WARNING, 0, _("Unexpected Client Job message: %s\n"),
280             fd->msg);
281       }
282       if (job_canceled(jcr)) {
283          break;
284       }
285    }
286    if (is_bnet_error(fd)) {
287       Jmsg(jcr, M_FATAL, 0, _("Network error with FD during %s: ERR=%s\n"),
288           job_type_to_str(jcr->JobType), bnet_strerror(fd));
289    }
290    bnet_sig(fd, BNET_TERMINATE);   /* tell Client we are terminating */
291
292    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
293    wait_for_storage_daemon_termination(jcr);
294
295
296    /* Return values from FD */
297    if (fd_ok) {
298       jcr->JobFiles = JobFiles;
299       jcr->Errors = Errors;
300       jcr->ReadBytes = ReadBytes;
301       jcr->JobBytes = JobBytes;
302    } else {
303       Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n"));
304    }
305
306 // Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus,
307 //   jcr->JobStatus, jcr->SDJobStatus);
308
309    /* Return the first error status we find Dir, FD, or SD */
310    if (!fd_ok || is_bnet_error(fd)) {
311       jcr->FDJobStatus = JS_ErrorTerminated;
312    }
313    if (jcr->JobStatus != JS_Terminated) {
314       return jcr->JobStatus;
315    }
316    if (jcr->FDJobStatus != JS_Terminated) {
317       return jcr->FDJobStatus;
318    }
319    return jcr->SDJobStatus;
320 }
321
322 /*
323  * Release resources allocated during backup.
324  */
325 void backup_cleanup(JCR *jcr, int TermCode)
326 {
327    char sdt[50], edt[50], schedt[50];
328    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
329    char ec6[30], ec7[30], elapsed[50];
330    char term_code[100], fd_term_msg[100], sd_term_msg[100];
331    const char *term_msg;
332    int msg_type;
333    MEDIA_DBR mr;
334    CLIENT_DBR cr;
335    double kbps, compression;
336    utime_t RunTime;
337
338    Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode);
339    dequeue_messages(jcr);             /* display any queued messages */
340    memset(&mr, 0, sizeof(mr));
341    memset(&cr, 0, sizeof(cr));
342    set_jcr_job_status(jcr, TermCode);
343
344    update_job_end_record(jcr);        /* update database */
345
346    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
347       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
348          db_strerror(jcr->db));
349       set_jcr_job_status(jcr, JS_ErrorTerminated);
350    }
351
352    bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
353    if (!db_get_client_record(jcr, jcr->db, &cr)) {
354       Jmsg(jcr, M_WARNING, 0, _("Error getting client record for stats: %s"),
355          db_strerror(jcr->db));
356    }
357
358    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
359    if (!db_get_media_record(jcr, jcr->db, &mr)) {
360       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
361          mr.VolumeName, db_strerror(jcr->db));
362       set_jcr_job_status(jcr, JS_ErrorTerminated);
363    }
364
365    update_bootstrap_file(jcr);
366
367
368    msg_type = M_INFO;                 /* by default INFO message */
369    switch (jcr->JobStatus) {
370       case JS_Terminated:
371          if (jcr->Errors || jcr->SDErrors) {
372             term_msg = _("Backup OK -- with warnings");
373          } else {
374             term_msg = _("Backup OK");
375          }
376          break;
377       case JS_FatalError:
378       case JS_ErrorTerminated:
379          term_msg = _("*** Backup Error ***");
380          msg_type = M_ERROR;          /* Generate error message */
381          if (jcr->store_bsock) {
382             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
383             if (jcr->SD_msg_chan) {
384                pthread_cancel(jcr->SD_msg_chan);
385             }
386          }
387          break;
388       case JS_Canceled:
389          term_msg = _("Backup Canceled");
390          if (jcr->store_bsock) {
391             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
392             if (jcr->SD_msg_chan) {
393                pthread_cancel(jcr->SD_msg_chan);
394             }
395          }
396          break;
397       default:
398          term_msg = term_code;
399          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
400          break;
401    }
402    bstrftimes(schedt, sizeof(schedt), jcr->jr.SchedTime);
403    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
404    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
405    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
406    if (RunTime <= 0) {
407       kbps = 0;
408    } else {
409       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
410    }
411    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
412       /*
413        * Note, if the job has erred, most likely it did not write any
414        *  tape, so suppress this "error" message since in that case
415        *  it is normal.  Or look at it the other way, only for a
416        *  normal exit should we complain about this error.
417        */
418       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
419          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
420       }
421       jcr->VolumeName[0] = 0;         /* none */
422    }
423
424    if (jcr->ReadBytes == 0) {
425       bstrncpy(compress, "None", sizeof(compress));
426    } else {
427       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
428       if (compression < 0.5) {
429          bstrncpy(compress, "None", sizeof(compress));
430       } else {
431          bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
432       }
433    }
434    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
435    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
436
437 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
438
439    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
440 "  JobId:                  %d\n"
441 "  Job:                    %s\n"
442 "  Backup Level:           %s%s\n"
443 "  Client:                 \"%s\" %s\n"
444 "  FileSet:                \"%s\" %s\n"
445 "  Pool:                   \"%s\"\n"
446 "  Storage:                \"%s\"\n"
447 "  Scheduled time:         %s\n"
448 "  Start time:             %s\n"
449 "  End time:               %s\n"
450 "  Elapsed time:           %s\n"
451 "  Priority:               %d\n"
452 "  FD Files Written:       %s\n"
453 "  SD Files Written:       %s\n"
454 "  FD Bytes Written:       %s (%sB)\n"
455 "  SD Bytes Written:       %s (%sB)\n"
456 "  Rate:                   %.1f KB/s\n"
457 "  Software Compression:   %s\n"
458 "  Volume name(s):         %s\n"
459 "  Volume Session Id:      %d\n"
460 "  Volume Session Time:    %d\n"
461 "  Last Volume Bytes:      %s\n"
462 "  Non-fatal FD errors:    %d\n"
463 "  SD Errors:              %d\n"
464 "  FD termination status:  %s\n"
465 "  SD termination status:  %s\n"
466 "  Termination:            %s\n\n"),
467         VERSION,
468         LSMDATE,
469         edt,
470         jcr->jr.JobId,
471         jcr->jr.Job,
472         level_to_str(jcr->JobLevel), jcr->since,
473         jcr->client->hdr.name, cr.Uname,
474         jcr->fileset->hdr.name, jcr->FSCreateTime,
475         jcr->pool->hdr.name,
476         jcr->store->hdr.name,
477         schedt,
478         sdt,
479         edt,
480         edit_utime(RunTime, elapsed, sizeof(elapsed)),
481         jcr->JobPriority,
482         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
483         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
484         edit_uint64_with_commas(jcr->jr.JobBytes, ec3),
485         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
486         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
487         edit_uint64_with_suffix(jcr->SDJobBytes, ec6),
488         (float)kbps,
489         compress,
490         jcr->VolumeName,
491         jcr->VolSessionId,
492         jcr->VolSessionTime,
493         edit_uint64_with_commas(mr.VolBytes, ec7),
494         jcr->Errors,
495         jcr->SDErrors,
496         fd_term_msg,
497         sd_term_msg,
498         term_msg);
499
500    Dmsg0(100, "Leave backup_cleanup()\n");
501 }
502
503 void update_bootstrap_file(JCR *jcr)
504 {
505    /* Now update the bootstrap file if any */
506    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
507        jcr->job->WriteBootstrap) {
508       FILE *fd;
509       BPIPE *bpipe = NULL;
510       int got_pipe = 0;
511       char *fname = jcr->job->WriteBootstrap;
512       VOL_PARAMS *VolParams = NULL;
513       int VolCount;
514       char edt[50];
515
516       if (*fname == '|') {
517          fname++;
518          got_pipe = 1;
519          bpipe = open_bpipe(fname, 0, "w");
520          fd = bpipe ? bpipe->wfd : NULL;
521       } else {
522          /* ***FIXME*** handle BASE */
523          fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
524       }
525       if (fd) {
526          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
527                     &VolParams);
528          if (VolCount == 0) {
529             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
530                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
531              if (jcr->SDJobFiles != 0) {
532                 set_jcr_job_status(jcr, JS_ErrorTerminated);
533              }
534
535          }
536          /* Start output with when and who wrote it */
537          bstrftimes(edt, sizeof(edt), time(NULL));
538          fprintf(fd, "# %s - %s - %s%s\n", edt, jcr->jr.Job,
539                  level_to_str(jcr->JobLevel), jcr->since);
540          for (int i=0; i < VolCount; i++) {
541             /* Write the record */
542             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
543             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
544             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
545             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
546             fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
547                          VolParams[i].EndFile);
548             fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
549                          VolParams[i].EndBlock);
550             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
551                          VolParams[i].LastIndex);
552          }
553          if (VolParams) {
554             free(VolParams);
555          }
556          if (got_pipe) {
557             close_bpipe(bpipe);
558          } else {
559             fclose(fd);
560          }
561       } else {
562          berrno be;
563          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
564               "%s: ERR=%s\n"), fname, be.strerror());
565          set_jcr_job_status(jcr, JS_ErrorTerminated);
566       }
567    }
568 }