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