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