]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/mac.c
This commit was manufactured by cvs2svn to create tag
[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->previous_jr.JobId = input_jobid;
84    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_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->previous_jr.JobStatus != 'T') {
90       Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"),
91          input_jobid, jcr->previous_jr.JobStatus);
92       return false;
93    }
94    Jmsg(jcr, M_INFO, 0, _("%s using JobId=%d Job=%s\n"),
95       Type, jcr->previous_jr.JobId, jcr->previous_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->previous_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->previous_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    free_ua_context(ua);
150    free_bsr(rx.bsr);
151
152    jcr->needs_sd = true;
153    return true;
154 }
155
156 /*
157  * Do a Migration, Archive, or Copy of a previous job
158  *
159  *  Returns:  false on failure
160  *            true  on success
161  */
162 bool do_mac(JCR *jcr)
163 {
164    int stat;
165    const char *Type;
166    char ed1[100];
167
168    switch(jcr->JobType) {
169    case JT_MIGRATE:
170       Type = "Migration";
171       break;
172    case JT_ARCHIVE:
173       Type = "Archive";
174       break;
175    case JT_COPY:
176       Type = "Copy";
177       break;
178    default:
179       Type = "Unknown";
180       break;
181    }
182
183
184    /* Print Job Start message */
185    Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
186         Type, edit_uint64(jcr->JobId, ed1), jcr->Job);
187
188    set_jcr_job_status(jcr, JS_Running);
189    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
190    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
191       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
192       return false;
193    }
194
195    /*
196     * Open a message channel connection with the Storage
197     * daemon. This is to let him know that our client
198     * will be contacting him for a backup  session.
199     *
200     */
201    Dmsg0(110, "Open connection with storage daemon\n");
202    set_jcr_job_status(jcr, JS_WaitSD);
203    /*
204     * Start conversation with Storage daemon
205     */
206    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
207       return false;
208    }
209    /*
210     * Now start a job with the Storage daemon
211     */
212    if (!start_storage_daemon_job(jcr, jcr->storage, jcr->storage)) {
213       return false;
214    }
215    /*
216     * Now start a Storage daemon message thread
217     */
218    if (!start_storage_daemon_message_thread(jcr)) {
219       return false;
220    }
221    Dmsg0(150, "Storage daemon connection OK\n");
222
223    /* Pickup Job termination data */
224    set_jcr_job_status(jcr, JS_Running);
225
226    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
227    wait_for_storage_daemon_termination(jcr);
228
229    if (jcr->JobStatus != JS_Terminated) {
230       stat = jcr->JobStatus;
231    } else {
232       stat = jcr->SDJobStatus;
233    }
234    if (stat == JS_Terminated) {
235       mac_cleanup(jcr, stat);
236       return true;
237    }
238    return false;
239 }
240
241
242 /*
243  * Release resources allocated during backup.
244  */
245 void mac_cleanup(JCR *jcr, int TermCode)
246 {
247    char sdt[50], edt[50];
248    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
249    char term_code[100], fd_term_msg[100], sd_term_msg[100];
250    const char *term_msg;
251    int msg_type;
252    MEDIA_DBR mr;
253    double kbps, compression;
254    utime_t RunTime;
255    const char *Type;
256
257    switch(jcr->JobType) {
258    case JT_MIGRATE:
259       Type = "Migration";
260       break;
261    case JT_ARCHIVE:
262       Type = "Archive";
263       break;
264    case JT_COPY:
265       Type = "Copy";
266       break;
267    default:
268       Type = "Unknown";
269       break;
270    }
271
272    Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
273    dequeue_messages(jcr);             /* display any queued messages */
274    memset(&mr, 0, sizeof(mr));
275    set_jcr_job_status(jcr, TermCode);
276
277    update_job_end_record(jcr);        /* update database */
278
279    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
280       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
281          db_strerror(jcr->db));
282       set_jcr_job_status(jcr, JS_ErrorTerminated);
283    }
284
285    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
286    if (!db_get_media_record(jcr, jcr->db, &mr)) {
287       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
288          mr.VolumeName, db_strerror(jcr->db));
289       set_jcr_job_status(jcr, JS_ErrorTerminated);
290    }
291
292    /* Now update the bootstrap file if any */
293    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
294        jcr->job->WriteBootstrap) {
295       FILE *fd;
296       BPIPE *bpipe = NULL;
297       int got_pipe = 0;
298       char *fname = jcr->job->WriteBootstrap;
299       VOL_PARAMS *VolParams = NULL;
300       int VolCount;
301
302       if (*fname == '|') {
303          fname++;
304          got_pipe = 1;
305          bpipe = open_bpipe(fname, 0, "w");
306          fd = bpipe ? bpipe->wfd : NULL;
307       } else {
308          /* ***FIXME*** handle BASE */
309          fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
310       }
311       if (fd) {
312          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
313                     &VolParams);
314          if (VolCount == 0) {
315             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
316                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
317              if (jcr->SDJobFiles != 0) {
318                 set_jcr_job_status(jcr, JS_ErrorTerminated);
319              }
320
321          }
322          for (int i=0; i < VolCount; i++) {
323             /* Write the record */
324             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
325             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
326             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
327             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
328             fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
329                          VolParams[i].EndFile);
330             fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
331                          VolParams[i].EndBlock);
332             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
333                          VolParams[i].LastIndex);
334          }
335          if (VolParams) {
336             free(VolParams);
337          }
338          if (got_pipe) {
339             close_bpipe(bpipe);
340          } else {
341             fclose(fd);
342          }
343       } else {
344          berrno be;
345          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
346               "%s: ERR=%s\n"), fname, be.strerror());
347          set_jcr_job_status(jcr, JS_ErrorTerminated);
348       }
349    }
350
351    msg_type = M_INFO;                 /* by default INFO message */
352    switch (jcr->JobStatus) {
353       case JS_Terminated:
354          if (jcr->Errors || jcr->SDErrors) {
355             term_msg = _("Backup OK -- with warnings");
356          } else {
357             term_msg = _("Backup OK");
358          }
359          break;
360       case JS_FatalError:
361       case JS_ErrorTerminated:
362          term_msg = _("*** Backup Error ***");
363          msg_type = M_ERROR;          /* Generate error message */
364          if (jcr->store_bsock) {
365             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
366             if (jcr->SD_msg_chan) {
367                pthread_cancel(jcr->SD_msg_chan);
368             }
369          }
370          break;
371       case JS_Canceled:
372          term_msg = _("Backup Canceled");
373          if (jcr->store_bsock) {
374             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
375             if (jcr->SD_msg_chan) {
376                pthread_cancel(jcr->SD_msg_chan);
377             }
378          }
379          break;
380       default:
381          term_msg = term_code;
382          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
383          break;
384    }
385    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
386    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
387    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
388    if (RunTime <= 0) {
389       kbps = 0;
390    } else {
391       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
392    }
393    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
394       /*
395        * Note, if the job has erred, most likely it did not write any
396        *  tape, so suppress this "error" message since in that case
397        *  it is normal.  Or look at it the other way, only for a
398        *  normal exit should we complain about this error.
399        */
400       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
401          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
402       }
403       jcr->VolumeName[0] = 0;         /* none */
404    }
405
406    if (jcr->ReadBytes == 0) {
407       bstrncpy(compress, "None", sizeof(compress));
408    } else {
409       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
410       if (compression < 0.5) {
411          bstrncpy(compress, "None", sizeof(compress));
412       } else {
413          bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
414       }
415    }
416    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
417    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
418
419 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
420
421    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
422 "  JobId:                  %d\n"
423 "  Job:                    %s\n"
424 "  Backup Level:           %s%s\n"
425 "  Client:                 %s\n"
426 "  FileSet:                \"%s\" %s\n"
427 "  Pool:                   \"%s\"\n"
428 "  Start time:             %s\n"
429 "  End time:               %s\n"
430 "  FD Files Written:       %s\n"
431 "  SD Files Written:       %s\n"
432 "  FD Bytes Written:       %s\n"
433 "  SD Bytes Written:       %s\n"
434 "  Rate:                   %.1f KB/s\n"
435 "  Software Compression:   %s\n"
436 "  Volume name(s):         %s\n"
437 "  Volume Session Id:      %d\n"
438 "  Volume Session Time:    %d\n"
439 "  Last Volume Bytes:      %s\n"
440 "  Non-fatal FD errors:    %d\n"
441 "  SD Errors:              %d\n"
442 "  FD termination status:  %s\n"
443 "  SD termination status:  %s\n"
444 "  Termination:            %s\n\n"),
445    VERSION,
446    LSMDATE,
447         edt,
448         jcr->jr.JobId,
449         jcr->jr.Job,
450         level_to_str(jcr->JobLevel), jcr->since,
451         jcr->client->hdr.name,
452         jcr->fileset->hdr.name, jcr->FSCreateTime,
453         jcr->pool->hdr.name,
454         sdt,
455         edt,
456         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
457         edit_uint64_with_commas(jcr->SDJobFiles, ec4),
458         edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
459         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
460         (float)kbps,
461         compress,
462         jcr->VolumeName,
463         jcr->VolSessionId,
464         jcr->VolSessionTime,
465         edit_uint64_with_commas(mr.VolBytes, ec3),
466         jcr->Errors,
467         jcr->SDErrors,
468         fd_term_msg,
469         sd_term_msg,
470         term_msg);
471
472    Dmsg0(100, "Leave mac_cleanup()\n");
473 }