]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/mac.c
03Dec05
[bacula/bacula] / bacula / src / dird / mac.c
1 /*
2  *
3  *   Bacula Director -- mac.c -- responsible for doing
4  *     migration, archive, and copy jobs.
5  *
6  *     Kern Sibbald, September MMIV
7  *
8  *  Basic tasks done here:
9  *     Open DB and create records for this job.
10  *     Open Message Channel with Storage daemon to tell him a job will be starting.
11  *     Open connection with File daemon and pass him commands
12  *       to do the backup.
13  *     When the File daemon finishes the job, update the DB.
14  *
15  *   Version $Id$
16  */
17 /*
18    Copyright (C) 2004-2005 Kern Sibbald
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
22    version 2 as amended with additional clauses defined in the
23    file LICENSE in the main source directory.
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 
28    the file LICENSE for additional details.
29
30  */
31
32 #include "bacula.h"
33 #include "dird.h"
34 #include "ua.h"
35
36 /* 
37  * Called here before the job is run to do the job
38  *   specific setup.
39  */
40 bool do_mac_init(JCR *jcr)
41 {
42    POOL_DBR pr;
43    JOB_DBR jr;
44    JobId_t input_jobid;
45    char *Name;
46    RESTORE_CTX rx;
47    UAContext *ua;
48    const char *Type;
49
50    switch(jcr->JobType) {
51    case JT_MIGRATE:
52       Type = "Migration";
53       break;
54    case JT_ARCHIVE:
55       Type = "Archive";
56       break;
57    case JT_COPY:
58       Type = "Copy";
59       break;
60    default:
61       Type = "Unknown";
62       break;
63    }
64
65    if (!get_or_create_fileset_record(jcr)) {
66       return false;
67    }
68
69    /*
70     * Find JobId of last job that ran.
71     */
72    memcpy(&jr, &jcr->jr, sizeof(jr));
73    Name = jcr->job->migration_job->hdr.name;
74    Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));
75    if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) {
76       Jmsg(jcr, M_FATAL, 0, _(
77            _("Unable to find JobId of previous Job for this client.\n")));
78       return false;
79    }
80    input_jobid = jr.JobId;
81    Dmsg1(100, "Last jobid=%d\n", input_jobid);
82
83    jcr->target_jr.JobId = input_jobid;
84    if (!db_get_job_record(jcr, jcr->db, &jcr->target_jr)) {
85       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"),
86            db_strerror(jcr->db));
87       return false;
88    }
89    if (jcr->target_jr.JobStatus != 'T') {
90       Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"),
91          input_jobid, jcr->target_jr.JobStatus);
92       return false;
93    }
94    Jmsg(jcr, M_INFO, 0, _("%s using JobId=%d Job=%s\n"),
95       Type, jcr->target_jr.JobId, jcr->target_jr.Job);
96
97
98    /*
99     * Get the Pool record -- first apply any level defined pools
100     */
101    switch (jcr->JobLevel) {
102    case L_FULL:
103       if (jcr->full_pool) {
104          jcr->pool = jcr->full_pool;
105       }
106       break;
107    case L_INCREMENTAL:
108       if (jcr->inc_pool) {
109          jcr->pool = jcr->inc_pool;
110       }
111       break;
112    case L_DIFFERENTIAL:
113       if (jcr->dif_pool) {
114          jcr->pool = jcr->dif_pool;
115       }
116       break;
117    }
118    memset(&pr, 0, sizeof(pr));
119    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
120
121    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
122       /* Try to create the pool */
123       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
124          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
125             db_strerror(jcr->db));
126          return false;
127       } else {
128          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
129       }
130    }
131    jcr->PoolId = pr.PoolId;               /****FIXME**** this can go away */
132    jcr->jr.PoolId = pr.PoolId;
133
134    memset(&rx, 0, sizeof(rx));
135    rx.bsr = new_bsr();
136    rx.JobIds = "";                       
137    rx.bsr->JobId = jcr->target_jr.JobId;
138    ua = new_ua_context(jcr);
139    complete_bsr(ua, rx.bsr);
140    rx.bsr->fi = new_findex();
141    rx.bsr->fi->findex = 1;
142    rx.bsr->fi->findex2 = jcr->target_jr.JobFiles;
143    jcr->ExpectedFiles = write_bsr_file(ua, rx);
144    if (jcr->ExpectedFiles == 0) {
145       free_ua_context(ua);
146       free_bsr(rx.bsr);
147       return false;
148    }
149    if (jcr->RestoreBootstrap) {
150       free(jcr->RestoreBootstrap);
151    }
152    POOLMEM *fname = get_pool_memory(PM_MESSAGE);
153    make_unique_restore_filename(ua, &fname);
154    jcr->RestoreBootstrap = bstrdup(fname);
155    free_ua_context(ua);
156    free_bsr(rx.bsr);
157    free_pool_memory(fname);
158
159    jcr->needs_sd = true;
160    return true;
161 }
162
163 /*
164  * Do a Migration, Archive, or Copy of a previous job
165  *
166  *  Returns:  false on failure
167  *            true  on success
168  */
169 bool do_mac(JCR *jcr)
170 {
171    int stat;
172    const char *Type;
173    char ed1[100];
174
175    switch(jcr->JobType) {
176    case JT_MIGRATE:
177       Type = "Migration";
178       break;
179    case JT_ARCHIVE:
180       Type = "Archive";
181       break;
182    case JT_COPY:
183       Type = "Copy";
184       break;
185    default:
186       Type = "Unknown";
187       break;
188    }
189
190
191    /* Print Job Start message */
192    Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
193         Type, edit_uint64(jcr->JobId, ed1), jcr->Job);
194
195    set_jcr_job_status(jcr, JS_Running);
196    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
197    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
198       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
199       return false;
200    }
201
202    /*
203     * Open a message channel connection with the Storage
204     * daemon. This is to let him know that our client
205     * will be contacting him for a backup  session.
206     *
207     */
208    Dmsg0(110, "Open connection with storage daemon\n");
209    set_jcr_job_status(jcr, JS_WaitSD);
210    /*
211     * Start conversation with Storage daemon
212     */
213    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
214       return false;
215    }
216    /*
217     * Now start a job with the Storage daemon
218     */
219    if (!start_storage_daemon_job(jcr, jcr->storage, jcr->storage)) {
220       return false;
221    }
222    /*
223     * Now start a Storage daemon message thread
224     */
225    if (!start_storage_daemon_message_thread(jcr)) {
226       return false;
227    }
228    Dmsg0(150, "Storage daemon connection OK\n");
229
230    /* Pickup Job termination data */
231    set_jcr_job_status(jcr, JS_Running);
232
233    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
234    wait_for_storage_daemon_termination(jcr);
235
236    if (jcr->JobStatus != JS_Terminated) {
237       stat = jcr->JobStatus;
238    } else {
239       stat = jcr->SDJobStatus;
240    }
241    if (stat == JS_Terminated) {
242       mac_cleanup(jcr, stat);
243       return true;
244    }
245    return false;
246 }
247
248
249 /*
250  * Release resources allocated during backup.
251  */
252 void mac_cleanup(JCR *jcr, int TermCode)
253 {
254    char sdt[50], edt[50];
255    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
256    char term_code[100], fd_term_msg[100], sd_term_msg[100];
257    const char *term_msg;
258    int msg_type;
259    MEDIA_DBR mr;
260    double kbps, compression;
261    utime_t RunTime;
262    const char *Type;
263
264    switch(jcr->JobType) {
265    case JT_MIGRATE:
266       Type = "Migration";
267       break;
268    case JT_ARCHIVE:
269       Type = "Archive";
270       break;
271    case JT_COPY:
272       Type = "Copy";
273       break;
274    default:
275       Type = "Unknown";
276       break;
277    }
278
279    Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
280    dequeue_messages(jcr);             /* display any queued messages */
281    memset(&mr, 0, sizeof(mr));
282    set_jcr_job_status(jcr, TermCode);
283
284    update_job_end_record(jcr);        /* update database */
285
286    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
287       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
288          db_strerror(jcr->db));
289       set_jcr_job_status(jcr, JS_ErrorTerminated);
290    }
291
292    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
293    if (!db_get_media_record(jcr, jcr->db, &mr)) {
294       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
295          mr.VolumeName, db_strerror(jcr->db));
296       set_jcr_job_status(jcr, JS_ErrorTerminated);
297    }
298
299    /* Now update the bootstrap file if any */
300    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
301        jcr->job->WriteBootstrap) {
302       FILE *fd;
303       BPIPE *bpipe = NULL;
304       int got_pipe = 0;
305       char *fname = jcr->job->WriteBootstrap;
306       VOL_PARAMS *VolParams = NULL;
307       int VolCount;
308
309       if (*fname == '|') {
310          fname++;
311          got_pipe = 1;
312          bpipe = open_bpipe(fname, 0, "w");
313          fd = bpipe ? bpipe->wfd : NULL;
314       } else {
315          /* ***FIXME*** handle BASE */
316          fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
317       }
318       if (fd) {
319          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
320                     &VolParams);
321          if (VolCount == 0) {
322             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
323                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
324              if (jcr->SDJobFiles != 0) {
325                 set_jcr_job_status(jcr, JS_ErrorTerminated);
326              }
327
328          }
329          for (int i=0; i < VolCount; i++) {
330             /* Write the record */
331             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
332             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
333             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
334             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
335             fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
336                          VolParams[i].EndFile);
337             fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
338                          VolParams[i].EndBlock);
339             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
340                          VolParams[i].LastIndex);
341          }
342          if (VolParams) {
343             free(VolParams);
344          }
345          if (got_pipe) {
346             close_bpipe(bpipe);
347          } else {
348             fclose(fd);
349          }
350       } else {
351          berrno be;
352          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
353               "%s: ERR=%s\n"), fname, be.strerror());
354          set_jcr_job_status(jcr, JS_ErrorTerminated);
355       }
356    }
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(sdt, sizeof(sdt), jcr->jr.StartTime);
393    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
394    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
395    if (RunTime <= 0) {
396       kbps = 0;
397    } else {
398       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
399    }
400    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
401       /*
402        * Note, if the job has erred, most likely it did not write any
403        *  tape, so suppress this "error" message since in that case
404        *  it is normal.  Or look at it the other way, only for a
405        *  normal exit should we complain about this error.
406        */
407       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
408          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
409       }
410       jcr->VolumeName[0] = 0;         /* none */
411    }
412
413    if (jcr->ReadBytes == 0) {
414       bstrncpy(compress, "None", sizeof(compress));
415    } else {
416       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
417       if (compression < 0.5) {
418          bstrncpy(compress, "None", sizeof(compress));
419       } else {
420          bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
421       }
422    }
423    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
424    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
425
426 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
427
428    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
429 "  JobId:                  %d\n"
430 "  Job:                    %s\n"
431 "  Backup Level:           %s%s\n"
432 "  Client:                 %s\n"
433 "  FileSet:                \"%s\" %s\n"
434 "  Pool:                   \"%s\"\n"
435 "  Start time:             %s\n"
436 "  End time:               %s\n"
437 "  FD Files Written:       %s\n"
438 "  SD Files Written:       %s\n"
439 "  FD Bytes Written:       %s\n"
440 "  SD Bytes Written:       %s\n"
441 "  Rate:                   %.1f KB/s\n"
442 "  Software Compression:   %s\n"
443 "  Volume name(s):         %s\n"
444 "  Volume Session Id:      %d\n"
445 "  Volume Session Time:    %d\n"
446 "  Last Volume Bytes:      %s\n"
447 "  Non-fatal FD errors:    %d\n"
448 "  SD Errors:              %d\n"
449 "  FD termination status:  %s\n"
450 "  SD termination status:  %s\n"
451 "  Termination:            %s\n\n"),
452    VERSION,
453    LSMDATE,
454         edt,
455         jcr->jr.JobId,
456         jcr->jr.Job,
457         level_to_str(jcr->JobLevel), jcr->since,
458         jcr->client->hdr.name,
459         jcr->fileset->hdr.name, jcr->FSCreateTime,
460         jcr->pool->hdr.name,
461         sdt,
462         edt,
463         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
464         edit_uint64_with_commas(jcr->SDJobFiles, ec4),
465         edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
466         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
467         (float)kbps,
468         compress,
469         jcr->VolumeName,
470         jcr->VolSessionId,
471         jcr->VolSessionTime,
472         edit_uint64_with_commas(mr.VolBytes, ec3),
473         jcr->Errors,
474         jcr->SDErrors,
475         fd_term_msg,
476         sd_term_msg,
477         term_msg);
478
479    Dmsg0(100, "Leave mac_cleanup()\n");
480 }