]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/mac.c
- Fix how FileSet is saved in job record to correct continual
[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
47    if (!get_or_create_fileset_record(jcr)) {
48       return false;
49    }
50
51    /*
52     * Find JobId of last job that ran.
53     */
54    memcpy(&jr, &jcr->jr, sizeof(jr));
55    Name = jcr->job->hdr.name;
56    Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));
57    if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) {
58       Jmsg(jcr, M_FATAL, 0, _(
59            _("Unable to find JobId of previous Job for this client.\n")));
60       return false;
61    }
62    input_jobid = jr.JobId;
63    jcr->JobLevel = jr.JobLevel;
64    Dmsg1(100, "Last jobid=%d\n", input_jobid);
65
66    /*
67     * Get the Pool record -- first apply any level defined pools
68     */
69    switch (jcr->JobLevel) {
70    case L_FULL:
71       if (jcr->full_pool) {
72          jcr->pool = jcr->full_pool;
73       }
74       break;
75    case L_INCREMENTAL:
76       if (jcr->inc_pool) {
77          jcr->pool = jcr->inc_pool;
78       }
79       break;
80    case L_DIFFERENTIAL:
81       if (jcr->dif_pool) {
82          jcr->pool = jcr->dif_pool;
83       }
84       break;
85    }
86    memset(&pr, 0, sizeof(pr));
87    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
88
89    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
90       /* Try to create the pool */
91       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
92          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
93             db_strerror(jcr->db));
94          return false;
95       } else {
96          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
97       }
98    }
99    jcr->PoolId = pr.PoolId;               /****FIXME**** this can go away */
100    jcr->jr.PoolId = pr.PoolId;
101    jcr->needs_sd = true;
102    return true;
103 }
104
105 /*
106  * Do a Migration, Archive, or Copy of a previous job
107  *
108  *  Returns:  false on failure
109  *            true  on success
110  */
111 bool do_mac(JCR *jcr)
112 {
113    int stat;
114    const char *Type;
115
116    switch(jcr->JobType) {
117    case JT_MIGRATION:
118       Type = "Migration";
119       break;
120    case JT_ARCHIVE:
121       Type = "Archive";
122       break;
123    case JT_COPY:
124       Type = "Copy";
125       break;
126    default:
127       Type = "Unknown";
128       break;
129    }
130
131
132    /* Print Job Start message */
133    Jmsg(jcr, M_INFO, 0, _("Start %s JobId %u, Job=%s\n"),
134         Type, jcr->JobId, jcr->Job);
135
136    set_jcr_job_status(jcr, JS_Running);
137    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
138    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
139       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
140       return false;
141    }
142
143    /*
144     * Open a message channel connection with the Storage
145     * daemon. This is to let him know that our client
146     * will be contacting him for a backup  session.
147     *
148     */
149    Dmsg0(110, "Open connection with storage daemon\n");
150    set_jcr_job_status(jcr, JS_WaitSD);
151    /*
152     * Start conversation with Storage daemon
153     */
154    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
155       return false;
156    }
157    /*
158     * Now start a job with the Storage daemon
159     */
160    if (!start_storage_daemon_job(jcr, jcr->storage, SD_APPEND)) {
161       return false;
162    }
163    /*
164     * Now start a Storage daemon message thread
165     */
166    if (!start_storage_daemon_message_thread(jcr)) {
167       return false;
168    }
169    Dmsg0(150, "Storage daemon connection OK\n");
170
171    /* Pickup Job termination data */
172    set_jcr_job_status(jcr, JS_Running);
173
174    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
175    wait_for_storage_daemon_termination(jcr);
176
177    if (jcr->JobStatus != JS_Terminated) {
178       stat = jcr->JobStatus;
179    } else {
180       stat = jcr->SDJobStatus;
181    }
182    if (stat == JS_Terminated) {
183       mac_cleanup(jcr, stat);
184       return true;
185    }
186    return false;
187 }
188
189
190 /*
191  * Release resources allocated during backup.
192  */
193 void mac_cleanup(JCR *jcr, int TermCode)
194 {
195    char sdt[50], edt[50];
196    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], compress[50];
197    char term_code[100], fd_term_msg[100], sd_term_msg[100];
198    const char *term_msg;
199    int msg_type;
200    MEDIA_DBR mr;
201    double kbps, compression;
202    utime_t RunTime;
203    const char *Type;
204
205    switch(jcr->JobType) {
206    case JT_MIGRATION:
207       Type = "Migration";
208       break;
209    case JT_ARCHIVE:
210       Type = "Archive";
211       break;
212    case JT_COPY:
213       Type = "Copy";
214       break;
215    default:
216       Type = "Unknown";
217       break;
218    }
219
220    Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
221    dequeue_messages(jcr);             /* display any queued messages */
222    memset(&mr, 0, sizeof(mr));
223    set_jcr_job_status(jcr, TermCode);
224
225    update_job_end_record(jcr);        /* update database */
226
227    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
228       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
229          db_strerror(jcr->db));
230       set_jcr_job_status(jcr, JS_ErrorTerminated);
231    }
232
233    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
234    if (!db_get_media_record(jcr, jcr->db, &mr)) {
235       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
236          mr.VolumeName, db_strerror(jcr->db));
237       set_jcr_job_status(jcr, JS_ErrorTerminated);
238    }
239
240    /* Now update the bootstrap file if any */
241    if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes &&
242        jcr->job->WriteBootstrap) {
243       FILE *fd;
244       BPIPE *bpipe = NULL;
245       int got_pipe = 0;
246       char *fname = jcr->job->WriteBootstrap;
247       VOL_PARAMS *VolParams = NULL;
248       int VolCount;
249
250       if (*fname == '|') {
251          fname++;
252          got_pipe = 1;
253          bpipe = open_bpipe(fname, 0, "w");
254          fd = bpipe ? bpipe->wfd : NULL;
255       } else {
256          /* ***FIXME*** handle BASE */
257          fd = fopen(fname, jcr->JobLevel==L_FULL?"w+":"a+");
258       }
259       if (fd) {
260          VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId,
261                     &VolParams);
262          if (VolCount == 0) {
263             Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to "
264                  "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db));
265              if (jcr->SDJobFiles != 0) {
266                 set_jcr_job_status(jcr, JS_ErrorTerminated);
267              }
268
269          }
270          for (int i=0; i < VolCount; i++) {
271             /* Write the record */
272             fprintf(fd, "Volume=\"%s\"\n", VolParams[i].VolumeName);
273             fprintf(fd, "MediaType=\"%s\"\n", VolParams[i].MediaType);
274             fprintf(fd, "VolSessionId=%u\n", jcr->VolSessionId);
275             fprintf(fd, "VolSessionTime=%u\n", jcr->VolSessionTime);
276             fprintf(fd, "VolFile=%u-%u\n", VolParams[i].StartFile,
277                          VolParams[i].EndFile);
278             fprintf(fd, "VolBlock=%u-%u\n", VolParams[i].StartBlock,
279                          VolParams[i].EndBlock);
280             fprintf(fd, "FileIndex=%d-%d\n", VolParams[i].FirstIndex,
281                          VolParams[i].LastIndex);
282          }
283          if (VolParams) {
284             free(VolParams);
285          }
286          if (got_pipe) {
287             close_bpipe(bpipe);
288          } else {
289             fclose(fd);
290          }
291       } else {
292          berrno be;
293          Jmsg(jcr, M_ERROR, 0, _("Could not open WriteBootstrap file:\n"
294               "%s: ERR=%s\n"), fname, be.strerror());
295          set_jcr_job_status(jcr, JS_ErrorTerminated);
296       }
297    }
298
299    msg_type = M_INFO;                 /* by default INFO message */
300    switch (jcr->JobStatus) {
301       case JS_Terminated:
302          if (jcr->Errors || jcr->SDErrors) {
303             term_msg = _("Backup OK -- with warnings");
304          } else {
305             term_msg = _("Backup OK");
306          }
307          break;
308       case JS_FatalError:
309       case JS_ErrorTerminated:
310          term_msg = _("*** Backup Error ***");
311          msg_type = M_ERROR;          /* Generate error message */
312          if (jcr->store_bsock) {
313             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
314             if (jcr->SD_msg_chan) {
315                pthread_cancel(jcr->SD_msg_chan);
316             }
317          }
318          break;
319       case JS_Canceled:
320          term_msg = _("Backup Canceled");
321          if (jcr->store_bsock) {
322             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
323             if (jcr->SD_msg_chan) {
324                pthread_cancel(jcr->SD_msg_chan);
325             }
326          }
327          break;
328       default:
329          term_msg = term_code;
330          sprintf(term_code, _("Inappropriate term code: %c\n"), jcr->JobStatus);
331          break;
332    }
333    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
334    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
335    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
336    if (RunTime <= 0) {
337       kbps = 0;
338    } else {
339       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
340    }
341    if (!db_get_job_volume_names(jcr, jcr->db, jcr->jr.JobId, &jcr->VolumeName)) {
342       /*
343        * Note, if the job has erred, most likely it did not write any
344        *  tape, so suppress this "error" message since in that case
345        *  it is normal.  Or look at it the other way, only for a
346        *  normal exit should we complain about this error.
347        */
348       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
349          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
350       }
351       jcr->VolumeName[0] = 0;         /* none */
352    }
353
354    if (jcr->ReadBytes == 0) {
355       bstrncpy(compress, "None", sizeof(compress));
356    } else {
357       compression = (double)100 - 100.0 * ((double)jcr->JobBytes / (double)jcr->ReadBytes);
358       if (compression < 0.5) {
359          bstrncpy(compress, "None", sizeof(compress));
360       } else {
361          bsnprintf(compress, sizeof(compress), "%.1f %%", (float)compression);
362       }
363    }
364    jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg));
365    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
366
367 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
368
369    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
370 "  JobId:                  %d\n"
371 "  Job:                    %s\n"
372 "  Backup Level:           %s%s\n"
373 "  Client:                 %s\n"
374 "  FileSet:                \"%s\" %s\n"
375 "  Pool:                   \"%s\"\n"
376 "  Start time:             %s\n"
377 "  End time:               %s\n"
378 "  FD Files Written:       %s\n"
379 "  SD Files Written:       %s\n"
380 "  FD Bytes Written:       %s\n"
381 "  SD Bytes Written:       %s\n"
382 "  Rate:                   %.1f KB/s\n"
383 "  Software Compression:   %s\n"
384 "  Volume name(s):         %s\n"
385 "  Volume Session Id:      %d\n"
386 "  Volume Session Time:    %d\n"
387 "  Last Volume Bytes:      %s\n"
388 "  Non-fatal FD errors:    %d\n"
389 "  SD Errors:              %d\n"
390 "  FD termination status:  %s\n"
391 "  SD termination status:  %s\n"
392 "  Termination:            %s\n\n"),
393    VERSION,
394    LSMDATE,
395         edt,
396         jcr->jr.JobId,
397         jcr->jr.Job,
398         level_to_str(jcr->JobLevel), jcr->since,
399         jcr->client->hdr.name,
400         jcr->fileset->hdr.name, jcr->FSCreateTime,
401         jcr->pool->hdr.name,
402         sdt,
403         edt,
404         edit_uint64_with_commas(jcr->jr.JobFiles, ec1),
405         edit_uint64_with_commas(jcr->SDJobFiles, ec4),
406         edit_uint64_with_commas(jcr->jr.JobBytes, ec2),
407         edit_uint64_with_commas(jcr->SDJobBytes, ec5),
408         (float)kbps,
409         compress,
410         jcr->VolumeName,
411         jcr->VolSessionId,
412         jcr->VolSessionTime,
413         edit_uint64_with_commas(mr.VolBytes, ec3),
414         jcr->Errors,
415         jcr->SDErrors,
416         fd_term_msg,
417         sd_term_msg,
418         term_msg);
419
420    Dmsg0(100, "Leave mac_cleanup()\n");
421 }