]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/mac.c
aeaeca655a51c1ac0f38ea5a85dba64d103b2067
[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    tjcr = jcr->target_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
193    memcpy(&tjcr->target_jr, &jcr->target_jr, sizeof(tjcr->target_jr));
194    set_jcr_defaults(tjcr, tjob);
195
196    if (!setup_job(tjcr)) {
197       return false;
198    }
199    /* Set output PoolId and FileSetId. */
200    tjcr->jr.PoolId = jcr->jr.PoolId;
201    tjcr->jr.FileSetId = jcr->jr.FileSetId;
202
203    /*
204     * Get the PoolId used with the original job. Then
205     *  find the pool name from the database record.
206     */
207    memset(&pr, 0, sizeof(pr));
208    pr.PoolId = tjcr->target_jr.PoolId;
209    if (!db_get_pool_record(jcr, jcr->db, &pr)) {
210       char ed1[50];
211       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
212             edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
213          return false;
214    }
215    /* Get the pool resource corresponding to the original job */
216    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
217    if (!pool) {
218       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
219       return false;
220    }
221
222    /* If pool storage specified, use it for restore */
223    copy_storage(tjcr, pool->storage);
224
225    /* If the original backup pool has a NextPool, make sure a 
226     *  record exists in the database.
227     */
228    if (pool->NextPool) {
229       memset(&pr, 0, sizeof(pr));
230       bstrncpy(pr.Name, pool->NextPool->hdr.name, sizeof(pr.Name));
231
232       while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
233          /* Try to create the pool */
234          if (create_pool(jcr, jcr->db, pool->NextPool, POOL_OP_CREATE) < 0) {
235             Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. %s"), pr.Name,
236                db_strerror(jcr->db));
237             return false;
238          } else {
239             Jmsg(jcr, M_INFO, 0, _("Pool \"%s\" created in database.\n"), pr.Name);
240          }
241       }
242       /*
243        * put the "NextPool" resource pointer in our jcr so that we
244        * can pull the Storage reference from it.
245        */
246       tjcr->pool = jcr->pool = pool->NextPool;
247       tjcr->jr.PoolId = jcr->jr.PoolId = pr.PoolId;
248    }
249
250    /* If pool storage specified, use it instead of job storage for backup */
251    copy_storage(jcr, jcr->pool->storage);
252
253    /* Print Job Start message */
254    Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
255         Type, edit_uint64(jcr->JobId, ed1), jcr->Job);
256
257    set_jcr_job_status(jcr, JS_Running);
258    set_jcr_job_status(jcr, JS_Running);
259    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
260    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
261       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
262       return false;
263    }
264
265    if (!db_update_job_start_record(tjcr, tjcr->db, &tjcr->jr)) {
266       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(tjcr->db));
267       return false;
268    }
269
270
271    /*
272     * Open a message channel connection with the Storage
273     * daemon. This is to let him know that our client
274     * will be contacting him for a backup  session.
275     *
276     */
277    Dmsg0(110, "Open connection with storage daemon\n");
278    set_jcr_job_status(jcr, JS_WaitSD);
279    set_jcr_job_status(tjcr, JS_WaitSD);
280    /*
281     * Start conversation with Storage daemon
282     */
283    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
284       return false;
285    }
286    sd = jcr->store_bsock;
287    /*
288     * Now start a job with the Storage daemon
289     */
290    Dmsg2(000, "Read store=%s, write store=%s\n", 
291       ((STORE *)tjcr->storage->first())->hdr.name,
292       ((STORE *)jcr->storage->first())->hdr.name);
293    if (!start_storage_daemon_job(jcr, tjcr->storage, jcr->storage)) {
294       return false;
295    }
296    Dmsg0(150, "Storage daemon connection OK\n");
297
298    if (!send_bootstrap_file(jcr, sd) ||
299        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
300       return false;
301    }
302
303
304    /*
305     * Now start a Storage daemon message thread
306     */
307    if (!start_storage_daemon_message_thread(jcr)) {
308       return false;
309    }
310
311    if (!bnet_fsend(sd, "run")) {
312       return false;
313    }
314
315    set_jcr_job_status(jcr, JS_Running);
316    set_jcr_job_status(tjcr, JS_Running);
317
318    /* Pickup Job termination data */
319    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
320    wait_for_storage_daemon_termination(jcr);
321
322    jcr->JobStatus = jcr->SDJobStatus;
323    if (jcr->JobStatus == JS_Terminated) {
324       mac_cleanup(jcr, jcr->JobStatus);
325       return true;
326    }
327    return false;
328 }
329
330
331 /*
332  * Release resources allocated during backup.
333  */
334 void mac_cleanup(JCR *jcr, int TermCode)
335 {
336    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
337    char ec1[30], ec2[30], ec3[30], ec4[30], elapsed[50];
338    char term_code[100], sd_term_msg[100];
339    const char *term_msg;
340    int msg_type;
341    MEDIA_DBR mr;
342    double kbps;
343    utime_t RunTime;
344    const char *Type;
345    JCR *tjcr = jcr->target_jcr;
346    POOL_MEM query(PM_MESSAGE);
347
348    switch(jcr->JobType) {
349    case JT_MIGRATE:
350       Type = "Migration";
351       break;
352    case JT_ARCHIVE:
353       Type = "Archive";
354       break;
355    case JT_COPY:
356       Type = "Copy";
357       break;
358    default:
359       Type = "Unknown";
360       break;
361    }
362
363    /* Ensure target is defined to avoid a lot of testing */
364    if (!tjcr) {
365       tjcr = jcr;
366    }
367    tjcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
368    tjcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
369    tjcr->VolSessionId = jcr->VolSessionId;
370    tjcr->VolSessionTime = jcr->VolSessionTime;
371
372    Dmsg2(100, "Enter mac_cleanup %d %c\n", TermCode, TermCode);
373    dequeue_messages(jcr);             /* display any queued messages */
374    memset(&mr, 0, sizeof(mr));
375    set_jcr_job_status(jcr, TermCode);
376    set_jcr_job_status(tjcr, TermCode);
377
378
379    update_job_end_record(jcr);        /* update database */
380    update_job_end_record(tjcr);
381
382    Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
383                "JobTDate=%s WHERE JobId=%s", 
384       jcr->target_jr.cStartTime, jcr->target_jr.cEndTime, 
385       edit_uint64(jcr->target_jr.JobTDate, ec1),
386       edit_uint64(tjcr->jr.JobId, ec2));
387    db_sql_query(tjcr->db, query.c_str(), NULL, NULL);
388
389    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
390       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
391          db_strerror(jcr->db));
392       set_jcr_job_status(jcr, JS_ErrorTerminated);
393    }
394
395    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
396    if (!db_get_media_record(jcr, jcr->db, &mr)) {
397       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
398          mr.VolumeName, db_strerror(jcr->db));
399       set_jcr_job_status(jcr, JS_ErrorTerminated);
400    }
401
402    update_bootstrap_file(tjcr);
403
404    msg_type = M_INFO;                 /* by default INFO message */
405    switch (jcr->JobStatus) {
406       case JS_Terminated:
407          if (jcr->Errors || jcr->SDErrors) {
408             term_msg = _("%s OK -- with warnings");
409          } else {
410             term_msg = _("%s OK");
411          }
412          break;
413       case JS_FatalError:
414       case JS_ErrorTerminated:
415          term_msg = _("*** %s Error ***");
416          msg_type = M_ERROR;          /* Generate error message */
417          if (jcr->store_bsock) {
418             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
419             if (jcr->SD_msg_chan) {
420                pthread_cancel(jcr->SD_msg_chan);
421             }
422          }
423          break;
424       case JS_Canceled:
425          term_msg = _("%s Canceled");
426          if (jcr->store_bsock) {
427             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
428             if (jcr->SD_msg_chan) {
429                pthread_cancel(jcr->SD_msg_chan);
430             }
431          }
432          break;
433       default:
434          term_msg = _("Inappropriate %s term code");
435          break;
436    }
437    bsnprintf(term_code, sizeof(term_code), term_msg, Type);
438    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
439    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
440    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
441    if (RunTime <= 0) {
442       kbps = 0;
443    } else {
444       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
445    }
446    if (!db_get_job_volume_names(tjcr, tjcr->db, tjcr->jr.JobId, &tjcr->VolumeName)) {
447       /*
448        * Note, if the job has erred, most likely it did not write any
449        *  tape, so suppress this "error" message since in that case
450        *  it is normal.  Or look at it the other way, only for a
451        *  normal exit should we complain about this error.
452        */
453       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
454          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(tjcr->db));
455       }
456       tjcr->VolumeName[0] = 0;         /* none */
457    }
458
459    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
460
461 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
462
463    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
464 "  Old Backup JobId:       %u\n"
465 "  New Backup JobId:       %u\n"
466 "  JobId:                  %u\n"
467 "  Job:                    %s\n"
468 "  Backup Level:           %s%s\n"
469 "  Client:                 %s\n"
470 "  FileSet:                \"%s\" %s\n"
471 "  Pool:                   \"%s\"\n"
472 "  Start time:             %s\n"
473 "  End time:               %s\n"
474 "  Elapsed time:           %s\n"
475 "  Priority:               %d\n"
476 "  SD Files Written:       %s\n"
477 "  SD Bytes Written:       %s (%sB)\n"
478 "  Rate:                   %.1f KB/s\n"
479 "  Volume name(s):         %s\n"
480 "  Volume Session Id:      %d\n"
481 "  Volume Session Time:    %d\n"
482 "  Last Volume Bytes:      %s\n"
483 "  SD Errors:              %d\n"
484 "  SD termination status:  %s\n"
485 "  Termination:            %s\n\n"),
486    VERSION,
487    LSMDATE,
488         edt, 
489         jcr->target_jr.JobId,
490         tjcr->jr.JobId,
491         jcr->jr.JobId,
492         jcr->jr.Job,
493         level_to_str(jcr->JobLevel), jcr->since,
494         jcr->client->hdr.name,
495         jcr->fileset->hdr.name, jcr->FSCreateTime,
496         jcr->pool->hdr.name,
497         sdt,
498         edt,
499         edit_utime(RunTime, elapsed, sizeof(elapsed)),
500         jcr->JobPriority,
501         edit_uint64_with_commas(jcr->SDJobFiles, ec2),
502         edit_uint64_with_commas(jcr->SDJobBytes, ec3),
503         edit_uint64_with_suffix(jcr->jr.JobBytes, ec4),
504         (float)kbps,
505         tjcr->VolumeName,
506         jcr->VolSessionId,
507         jcr->VolSessionTime,
508         edit_uint64_with_commas(mr.VolBytes, ec1),
509         jcr->SDErrors,
510         sd_term_msg,
511         term_code);
512
513    Dmsg1(100, "Leave mac_cleanup() target_jcr=0x%x\n", jcr->target_jcr);
514    if (jcr->target_jcr) {
515       free_jcr(jcr->target_jcr);
516    }
517 }