]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/mac.c
20Feb06
[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 Storage daemon and pass him commands
12  *       to do the backup.
13  *     When the Storage daemon finishes the job, update the DB.
14  *
15  *   Version $Id$
16  */
17 /*
18    Copyright (C) 2004-2006 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 static char OKbootstrap[] = "3000 OK bootstrap\n";
37
38 /* 
39  * Called here before the job is run to do the job
40  *   specific setup.
41  */
42 bool do_mac_init(JCR *jcr)
43 {
44    POOL_DBR pr;
45    char *Name;
46    const char *Type;
47
48    switch(jcr->JobType) {
49    case JT_MIGRATE:
50       Type = "Migration";
51       break;
52    case JT_ARCHIVE:
53       Type = "Archive";
54       break;
55    case JT_COPY:
56       Type = "Copy";
57       break;
58    default:
59       Type = "Unknown";
60       break;
61    }
62
63    if (!get_or_create_fileset_record(jcr)) {
64       return false;
65    }
66
67    /*
68     * Find JobId of last job that ran.
69     */
70    Name = jcr->job->migration_job->hdr.name;
71    Dmsg1(100, "find last jobid for: %s\n", NPRT(Name));
72    jcr->target_jr.JobType = JT_BACKUP;
73    if (!db_find_last_jobid(jcr, jcr->db, Name, &jcr->target_jr)) {
74       Jmsg(jcr, M_FATAL, 0, 
75            _("Previous job \"%s\" not found. ERR=%s\n"), Name,
76            db_strerror(jcr->db));
77       return false;
78    }
79    Dmsg1(100, "Last jobid=%d\n", jcr->target_jr.JobId);
80
81    if (!db_get_job_record(jcr, jcr->db, &jcr->target_jr)) {
82       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"),
83            db_strerror(jcr->db));
84       return false;
85    }
86    if (jcr->target_jr.JobStatus != 'T') {
87       Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"),
88          jcr->target_jr.JobId, jcr->target_jr.JobStatus);
89       return false;
90    }
91    Jmsg(jcr, M_INFO, 0, _("%s using JobId=%d Job=%s\n"),
92       Type, jcr->target_jr.JobId, jcr->target_jr.Job);
93
94
95    /*
96     * Get the Pool record -- first apply any level defined pools
97     */
98    switch (jcr->target_jr.JobLevel) {
99    case L_FULL:
100       if (jcr->full_pool) {
101          jcr->pool = jcr->full_pool;
102       }
103       break;
104    case L_INCREMENTAL:
105       if (jcr->inc_pool) {
106          jcr->pool = jcr->inc_pool;
107       }
108       break;
109    case L_DIFFERENTIAL:
110       if (jcr->dif_pool) {
111          jcr->pool = jcr->dif_pool;
112       }
113       break;
114    }
115    memset(&pr, 0, sizeof(pr));
116    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
117
118    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
119       /* Try to create the pool */
120       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
121          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
122             db_strerror(jcr->db));
123          return false;
124       } else {
125          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
126       }
127    }
128    jcr->jr.PoolId = pr.PoolId;
129
130    /* If pool storage specified, use it instead of job storage */
131    copy_storage(jcr, jcr->pool->storage);
132
133    if (!jcr->storage) {
134       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
135       return false;
136    }
137
138    if (!create_restore_bootstrap_file(jcr)) {
139       return false;
140    }
141    return true;
142 }
143
144 /*
145  * Do a Migration, Archive, or Copy of a previous job
146  *
147  *  Returns:  false on failure
148  *            true  on success
149  */
150 bool do_mac(JCR *jcr)
151 {
152    POOL_DBR pr;
153    POOL *pool;
154    const char *Type;
155    char ed1[100];
156    BSOCK *sd;
157    JOB *job, *tjob;
158    JCR *tjcr;
159
160    switch(jcr->JobType) {
161    case JT_MIGRATE:
162       Type = "Migration";
163       break;
164    case JT_ARCHIVE:
165       Type = "Archive";
166       break;
167    case JT_COPY:
168       Type = "Copy";
169       break;
170    default:
171       Type = "Unknown";
172       break;
173    }
174
175
176    Dmsg4(100, "Target: Name=%s JobId=%d Type=%c Level=%c\n",
177       jcr->target_jr.Name, jcr->target_jr.JobId, 
178       jcr->target_jr.JobType, jcr->target_jr.JobLevel);
179
180    Dmsg4(100, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
181       jcr->jr.Name, jcr->jr.JobId, 
182       jcr->jr.JobType, jcr->jr.JobLevel);
183
184    LockRes();
185    job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
186    tjob = (JOB *)GetResWithName(R_JOB, jcr->target_jr.Name);
187    UnlockRes();
188    if (!job || !tjob) {
189       return false;
190    }
191
192    /* 
193     * Target jcr is the new Job that corresponds to the original
194     *  target job. It "runs" at the same time as the current 
195     *  migration job and becomes a new backup job that replaces
196     *  the original backup job.  Most operations on the current
197     *  migration jcr are also done on the target jcr.
198     */
199    tjcr = jcr->target_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
200    memcpy(&tjcr->target_jr, &jcr->target_jr, sizeof(tjcr->target_jr));
201
202    /* Turn the tjcr into a "real" job */
203    set_jcr_defaults(tjcr, tjob);
204    if (!setup_job(tjcr)) {
205       return false;
206    }
207    /* Set output PoolId and FileSetId. */
208    tjcr->jr.PoolId = jcr->jr.PoolId;
209    tjcr->jr.FileSetId = jcr->jr.FileSetId;
210
211    /*
212     * Get the PoolId used with the original job. Then
213     *  find the pool name from the database record.
214     */
215    memset(&pr, 0, sizeof(pr));
216    pr.PoolId = tjcr->target_jr.PoolId;
217    if (!db_get_pool_record(jcr, jcr->db, &pr)) {
218       char ed1[50];
219       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
220             edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
221          return false;
222    }
223    /* Get the pool resource corresponding to the original job */
224    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
225    if (!pool) {
226       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
227       return false;
228    }
229
230    /* Check Migration time and High/Low water marks */
231    /* ***FIXME*** */
232
233    /* If pool storage specified, use it for restore */
234    copy_storage(tjcr, pool->storage);
235
236    /* If the original backup pool has a NextPool, make sure a 
237     *  record exists in the database.
238     */
239    if (pool->NextPool) {
240       memset(&pr, 0, sizeof(pr));
241       bstrncpy(pr.Name, pool->NextPool->hdr.name, sizeof(pr.Name));
242
243       while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
244          /* Try to create the pool */
245          if (create_pool(jcr, jcr->db, pool->NextPool, POOL_OP_CREATE) < 0) {
246             Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. %s"), pr.Name,
247                db_strerror(jcr->db));
248             return false;
249          } else {
250             Jmsg(jcr, M_INFO, 0, _("Pool \"%s\" created in database.\n"), pr.Name);
251          }
252       }
253       /*
254        * put the "NextPool" resource pointer in our jcr so that we
255        * can pull the Storage reference from it.
256        */
257       tjcr->pool = jcr->pool = pool->NextPool;
258       tjcr->jr.PoolId = jcr->jr.PoolId = pr.PoolId;
259    }
260
261    /* If pool storage specified, use it instead of job storage for backup */
262    copy_storage(jcr, jcr->pool->storage);
263
264    /* Print Job Start message */
265    Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
266         Type, edit_uint64(jcr->JobId, ed1), jcr->Job);
267
268    set_jcr_job_status(jcr, JS_Running);
269    set_jcr_job_status(jcr, JS_Running);
270    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
271    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
272       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
273       return false;
274    }
275
276    if (!db_update_job_start_record(tjcr, tjcr->db, &tjcr->jr)) {
277       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(tjcr->db));
278       return false;
279    }
280
281
282    /*
283     * Open a message channel connection with the Storage
284     * daemon. This is to let him know that our client
285     * will be contacting him for a backup  session.
286     *
287     */
288    Dmsg0(110, "Open connection with storage daemon\n");
289    set_jcr_job_status(jcr, JS_WaitSD);
290    set_jcr_job_status(tjcr, JS_WaitSD);
291    /*
292     * Start conversation with Storage daemon
293     */
294    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
295       return false;
296    }
297    sd = jcr->store_bsock;
298    /*
299     * Now start a job with the Storage daemon
300     */
301    Dmsg2(000, "Read store=%s, write store=%s\n", 
302       ((STORE *)tjcr->storage->first())->hdr.name,
303       ((STORE *)jcr->storage->first())->hdr.name);
304    if (!start_storage_daemon_job(jcr, tjcr->storage, jcr->storage)) {
305       return false;
306    }
307    Dmsg0(150, "Storage daemon connection OK\n");
308
309    if (!send_bootstrap_file(jcr, sd) ||
310        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
311       return false;
312    }
313
314
315    /*
316     * Now start a Storage daemon message thread
317     */
318    if (!start_storage_daemon_message_thread(jcr)) {
319       return false;
320    }
321
322    if (!bnet_fsend(sd, "run")) {
323       return false;
324    }
325
326    set_jcr_job_status(jcr, JS_Running);
327    set_jcr_job_status(tjcr, JS_Running);
328
329    /* Pickup Job termination data */
330    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
331    wait_for_storage_daemon_termination(jcr);
332
333    jcr->JobStatus = jcr->SDJobStatus;
334    if (jcr->JobStatus == JS_Terminated) {
335       mac_cleanup(jcr, jcr->JobStatus);
336       return true;
337    }
338    return false;
339 }
340
341
342 /*
343  * Release resources allocated during backup.
344  */
345 void mac_cleanup(JCR *jcr, int TermCode)
346 {
347    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
348    char ec1[30], ec2[30], ec3[30], ec4[30], elapsed[50];
349    char term_code[100], sd_term_msg[100];
350    const char *term_msg;
351    int msg_type;
352    MEDIA_DBR mr;
353    double kbps;
354    utime_t RunTime;
355    const char *Type;
356    JCR *tjcr = jcr->target_jcr;
357    POOL_MEM query(PM_MESSAGE);
358
359    switch(jcr->JobType) {
360    case JT_MIGRATE:
361       Type = "Migration";
362       break;
363    case JT_ARCHIVE:
364       Type = "Archive";
365       break;
366    case JT_COPY:
367       Type = "Copy";
368       break;
369    default:
370       Type = "Unknown";
371       break;
372    }
373
374    /* Ensure target is defined to avoid a lot of testing */
375    if (!tjcr) {
376       tjcr = jcr;
377    }
378    tjcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
379    tjcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
380    tjcr->VolSessionId = jcr->VolSessionId;
381    tjcr->VolSessionTime = jcr->VolSessionTime;
382
383    Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
384    dequeue_messages(jcr);             /* display any queued messages */
385    memset(&mr, 0, sizeof(mr));
386    set_jcr_job_status(jcr, TermCode);
387    set_jcr_job_status(tjcr, TermCode);
388
389
390    update_job_end_record(jcr);        /* update database */
391    update_job_end_record(tjcr);
392
393    Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
394                "JobTDate=%s WHERE JobId=%s", 
395       jcr->target_jr.cStartTime, jcr->target_jr.cEndTime, 
396       edit_uint64(jcr->target_jr.JobTDate, ec1),
397       edit_uint64(tjcr->jr.JobId, ec2));
398    db_sql_query(tjcr->db, query.c_str(), NULL, NULL);
399
400    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
401       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
402          db_strerror(jcr->db));
403       set_jcr_job_status(jcr, JS_ErrorTerminated);
404    }
405
406    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
407    if (!db_get_media_record(jcr, jcr->db, &mr)) {
408       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
409          mr.VolumeName, db_strerror(jcr->db));
410       set_jcr_job_status(jcr, JS_ErrorTerminated);
411    }
412
413    update_bootstrap_file(tjcr);
414
415    msg_type = M_INFO;                 /* by default INFO message */
416    switch (jcr->JobStatus) {
417       case JS_Terminated:
418          if (jcr->Errors || jcr->SDErrors) {
419             term_msg = _("%s OK -- with warnings");
420          } else {
421             term_msg = _("%s OK");
422          }
423          break;
424       case JS_FatalError:
425       case JS_ErrorTerminated:
426          term_msg = _("*** %s Error ***");
427          msg_type = M_ERROR;          /* Generate error message */
428          if (jcr->store_bsock) {
429             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
430             if (jcr->SD_msg_chan) {
431                pthread_cancel(jcr->SD_msg_chan);
432             }
433          }
434          break;
435       case JS_Canceled:
436          term_msg = _("%s Canceled");
437          if (jcr->store_bsock) {
438             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
439             if (jcr->SD_msg_chan) {
440                pthread_cancel(jcr->SD_msg_chan);
441             }
442          }
443          break;
444       default:
445          term_msg = _("Inappropriate %s term code");
446          break;
447    }
448    bsnprintf(term_code, sizeof(term_code), term_msg, Type);
449    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
450    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
451    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
452    if (RunTime <= 0) {
453       kbps = 0;
454    } else {
455       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
456    }
457    if (!db_get_job_volume_names(tjcr, tjcr->db, tjcr->jr.JobId, &tjcr->VolumeName)) {
458       /*
459        * Note, if the job has erred, most likely it did not write any
460        *  tape, so suppress this "error" message since in that case
461        *  it is normal.  Or look at it the other way, only for a
462        *  normal exit should we complain about this error.
463        */
464       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
465          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(tjcr->db));
466       }
467       tjcr->VolumeName[0] = 0;         /* none */
468    }
469
470    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
471
472 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
473
474    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
475 "  Old Backup JobId:       %u\n"
476 "  New Backup JobId:       %u\n"
477 "  JobId:                  %u\n"
478 "  Job:                    %s\n"
479 "  Backup Level:           %s%s\n"
480 "  Client:                 %s\n"
481 "  FileSet:                \"%s\" %s\n"
482 "  Pool:                   \"%s\"\n"
483 "  Start time:             %s\n"
484 "  End time:               %s\n"
485 "  Elapsed time:           %s\n"
486 "  Priority:               %d\n"
487 "  SD Files Written:       %s\n"
488 "  SD Bytes Written:       %s (%sB)\n"
489 "  Rate:                   %.1f KB/s\n"
490 "  Volume name(s):         %s\n"
491 "  Volume Session Id:      %d\n"
492 "  Volume Session Time:    %d\n"
493 "  Last Volume Bytes:      %s\n"
494 "  SD Errors:              %d\n"
495 "  SD termination status:  %s\n"
496 "  Termination:            %s\n\n"),
497    VERSION,
498    LSMDATE,
499         edt, 
500         jcr->target_jr.JobId,
501         tjcr->jr.JobId,
502         jcr->jr.JobId,
503         jcr->jr.Job,
504         level_to_str(jcr->JobLevel), jcr->since,
505         jcr->client->hdr.name,
506         jcr->fileset->hdr.name, jcr->FSCreateTime,
507         jcr->pool->hdr.name,
508         sdt,
509         edt,
510         edit_utime(RunTime, elapsed, sizeof(elapsed)),
511         jcr->JobPriority,
512         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
513         edit_uint64_with_commas(jcr->SDJobBytes, ec3),
514         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
515         (float)kbps,
516         tjcr->VolumeName,
517         jcr->VolSessionId,
518         jcr->VolSessionTime,
519         edit_uint64_with_commas(mr.VolBytes, ec1),
520         jcr->SDErrors,
521         sd_term_msg,
522         term_code);
523
524    Dmsg1(100, "Leave mac_cleanup() target_jcr=0x%x\n", jcr->target_jcr);
525    if (jcr->target_jcr) {
526       free_jcr(jcr->target_jcr);
527    }
528 }