]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
17Mar06
[bacula/bacula] / bacula / src / dird / migrate.c
1 /*
2  *
3  *   Bacula Director -- migrate.c -- responsible for doing
4  *     migration 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 #include <regex.h>
36
37 static char OKbootstrap[] = "3000 OK bootstrap\n";
38 static bool get_job_to_migrate(JCR *jcr);
39
40 /* 
41  * Called here before the job is run to do the job
42  *   specific setup.
43  */
44 bool do_migration_init(JCR *jcr)
45 {
46    POOL_DBR pr;
47
48    if (!get_job_to_migrate(jcr)) {
49       return false;
50    }
51
52    if (jcr->previous_jr.JobId == 0) {
53       return true;                    /* no work */
54    }
55
56    if (!get_or_create_fileset_record(jcr)) {
57       return false;
58    }
59
60    /*
61     * Get the Pool record -- first apply any level defined pools
62     */
63    switch (jcr->previous_jr.JobLevel) {
64    case L_FULL:
65       if (jcr->full_pool) {
66          jcr->pool = jcr->full_pool;
67       }
68       break;
69    case L_INCREMENTAL:
70       if (jcr->inc_pool) {
71          jcr->pool = jcr->inc_pool;
72       }
73       break;
74    case L_DIFFERENTIAL:
75       if (jcr->dif_pool) {
76          jcr->pool = jcr->dif_pool;
77       }
78       break;
79    }
80    memset(&pr, 0, sizeof(pr));
81    bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
82
83    while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
84       /* Try to create the pool */
85       if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
86          Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
87             db_strerror(jcr->db));
88          return false;
89       } else {
90          Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
91       }
92    }
93    jcr->jr.PoolId = pr.PoolId;
94
95    /* If pool storage specified, use it instead of job storage */
96    copy_storage(jcr, jcr->pool->storage);
97
98    if (!jcr->storage) {
99       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
100       return false;
101    }
102
103    if (!create_restore_bootstrap_file(jcr)) {
104       return false;
105    }
106    return true;
107 }
108
109 /*
110  * Do a Migration of a previous job
111  *
112  *  Returns:  false on failure
113  *            true  on success
114  */
115 bool do_migration(JCR *jcr)
116 {
117    POOL_DBR pr;
118    POOL *pool;
119    char ed1[100];
120    BSOCK *sd;
121    JOB *job, *tjob;
122    JCR *tjcr;
123
124    if (jcr->previous_jr.JobId == 0) {
125       jcr->JobStatus = JS_Terminated;
126       migration_cleanup(jcr, jcr->JobStatus);
127       return true;                    /* no work */
128    }
129    Dmsg4(100, "Target: Name=%s JobId=%d Type=%c Level=%c\n",
130       jcr->previous_jr.Name, jcr->previous_jr.JobId, 
131       jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
132
133    Dmsg4(100, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
134       jcr->jr.Name, jcr->jr.JobId, 
135       jcr->jr.JobType, jcr->jr.JobLevel);
136
137    LockRes();
138    job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
139    tjob = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
140    UnlockRes();
141    if (!job || !tjob) {
142       return false;
143    }
144
145    /* 
146     * Target jcr is the new Job that corresponds to the original
147     *  target job. It "runs" at the same time as the current 
148     *  migration job and becomes a new backup job that replaces
149     *  the original backup job.  Most operations on the current
150     *  migration jcr are also done on the target jcr.
151     */
152    tjcr = jcr->previous_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
153    memcpy(&tjcr->previous_jr, &jcr->previous_jr, sizeof(tjcr->previous_jr));
154
155    /* Turn the tjcr into a "real" job */
156    set_jcr_defaults(tjcr, tjob);
157    if (!setup_job(tjcr)) {
158       return false;
159    }
160    /* Set output PoolId and FileSetId. */
161    tjcr->jr.PoolId = jcr->jr.PoolId;
162    tjcr->jr.FileSetId = jcr->jr.FileSetId;
163
164    /*
165     * Get the PoolId used with the original job. Then
166     *  find the pool name from the database record.
167     */
168    memset(&pr, 0, sizeof(pr));
169    pr.PoolId = tjcr->previous_jr.PoolId;
170    if (!db_get_pool_record(jcr, jcr->db, &pr)) {
171       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
172             edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
173          return false;
174    }
175    /* Get the pool resource corresponding to the original job */
176    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
177    if (!pool) {
178       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
179       return false;
180    }
181
182    /* Check Migration time and High/Low water marks */
183    /* ***FIXME*** */
184
185    /* If pool storage specified, use it for restore */
186    copy_storage(tjcr, pool->storage);
187
188    /* If the original backup pool has a NextPool, make sure a 
189     *  record exists in the database.
190     */
191    if (pool->NextPool) {
192       memset(&pr, 0, sizeof(pr));
193       bstrncpy(pr.Name, pool->NextPool->hdr.name, sizeof(pr.Name));
194
195       while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
196          /* Try to create the pool */
197          if (create_pool(jcr, jcr->db, pool->NextPool, POOL_OP_CREATE) < 0) {
198             Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. %s"), pr.Name,
199                db_strerror(jcr->db));
200             return false;
201          } else {
202             Jmsg(jcr, M_INFO, 0, _("Pool \"%s\" created in database.\n"), pr.Name);
203          }
204       }
205       /*
206        * put the "NextPool" resource pointer in our jcr so that we
207        * can pull the Storage reference from it.
208        */
209       tjcr->pool = jcr->pool = pool->NextPool;
210       tjcr->jr.PoolId = jcr->jr.PoolId = pr.PoolId;
211    }
212
213    /* If pool storage specified, use it instead of job storage for backup */
214    copy_storage(jcr, jcr->pool->storage);
215
216    /* Print Job Start message */
217    Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
218         edit_uint64(jcr->JobId, ed1), jcr->Job);
219
220    set_jcr_job_status(jcr, JS_Running);
221    set_jcr_job_status(tjcr, JS_Running);
222    Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
223    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
224       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
225       return false;
226    }
227
228    if (!db_update_job_start_record(tjcr, tjcr->db, &tjcr->jr)) {
229       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(tjcr->db));
230       return false;
231    }
232
233
234    /*
235     * Open a message channel connection with the Storage
236     * daemon. This is to let him know that our client
237     * will be contacting him for a backup  session.
238     *
239     */
240    Dmsg0(110, "Open connection with storage daemon\n");
241    set_jcr_job_status(jcr, JS_WaitSD);
242    set_jcr_job_status(tjcr, JS_WaitSD);
243    /*
244     * Start conversation with Storage daemon
245     */
246    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
247       return false;
248    }
249    sd = jcr->store_bsock;
250    /*
251     * Now start a job with the Storage daemon
252     */
253    Dmsg2(000, "Read store=%s, write store=%s\n", 
254       ((STORE *)tjcr->storage->first())->hdr.name,
255       ((STORE *)jcr->storage->first())->hdr.name);
256    if (!start_storage_daemon_job(jcr, tjcr->storage, jcr->storage)) {
257       return false;
258    }
259    Dmsg0(150, "Storage daemon connection OK\n");
260
261    if (!send_bootstrap_file(jcr, sd) ||
262        !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
263       return false;
264    }
265
266    /*
267     * Now start a Storage daemon message thread
268     */
269    if (!start_storage_daemon_message_thread(jcr)) {
270       return false;
271    }
272
273    if (!bnet_fsend(sd, "run")) {
274       return false;
275    }
276
277    set_jcr_job_status(jcr, JS_Running);
278    set_jcr_job_status(tjcr, JS_Running);
279
280    /* Pickup Job termination data */
281    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
282    wait_for_storage_daemon_termination(jcr);
283
284    jcr->JobStatus = jcr->SDJobStatus;
285    if (jcr->JobStatus == JS_Terminated) {
286       migration_cleanup(jcr, jcr->JobStatus);
287       return true;
288    }
289    return false;
290 }
291
292 /*
293  * Callback handler make list of JobIds
294  */
295 static int jobid_handler(void *ctx, int num_fields, char **row)
296 {
297    POOLMEM *JobIds = (POOLMEM *)ctx;
298
299    if (JobIds[0] != 0) {
300       pm_strcat(JobIds, ",");
301    }
302    pm_strcat(JobIds, row[0]);
303    return 0;
304 }
305
306
307 struct uitem {
308    dlink link;   
309    char *item;
310 };
311
312 static int item_compare(void *item1, void *item2)
313 {
314    uitem *i1 = (uitem *)item1;
315    uitem *i2 = (uitem *)item2;
316    return strcmp(i1->item, i2->item);
317 }
318
319 static int unique_name_handler(void *ctx, int num_fields, char **row)
320 {
321    dlist *list = (dlist *)ctx;
322
323    uitem *new_item = (uitem *)malloc(sizeof(uitem));
324    uitem *item;
325    
326    memset(new_item, 0, sizeof(uitem));
327    new_item->item = bstrdup(row[0]);
328
329    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
330    if (item != new_item) {            /* already in list */
331       free(new_item->item);
332       free((char *)new_item);
333       return 0;
334    }
335    return 0;
336 }
337
338 const char *sql_smallest_vol = 
339    "SELECT MediaId FROM Media,Pool WHERE"
340    " VolStatus in ('Full','Used','Error') AND"
341    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
342    " ORDER BY VolBytes ASC LIMIT 1";
343
344 const char *sql_oldest_vol = 
345    "SELECT MediaId FROM Media,Pool WHERE"
346    " VolStatus in ('Full','Used','Error') AND"
347    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
348    " ORDER BY LastWritten ASC LIMIT 1";
349
350 const char *sql_jobids_from_mediaid =
351    "SELECT DISTINCT Job.JobId FROM JobMedia,Job"
352    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
353    " ORDER by Job.StartTime";
354
355 const char *sql_pool_bytes =
356    "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
357    " VolStatus in ('Full','Used','Error','Append') AND"
358    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
359
360 const char *sql_vol_bytes =
361    "SELECT MediaId FROM Media,Pool WHERE"
362    " VolStatus in ('Full','Used','Error') AND"
363    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
364    " VolBytes<%s ORDER BY LastWritten ASC LIMIT 1";
365
366 const char *sql_client =
367    "SELECT DISTINCT Client.Name from Client,Pool,Media,Job,JobMedia "
368    " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
369    " JobMedia.JobId=Job.JobId AND Job.ClientId=Client.ClientId AND"
370    " Job.PoolId=Media.PoolId";
371
372 const char *sql_job =
373    "SELECT DISTINCT Job.Name from Pool,Media,Job,JobMedia "
374    " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
375    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
376
377 const char *sql_ujobid =
378    "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
379    " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
380    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
381
382 const char *sql_vol = 
383    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
384    " VolStatus in ('Full','Used','Error') AND"
385    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
386
387
388 /*
389  * Returns: false on error
390  *          true  if OK and jcr->previous_jr filled in
391  */
392 static bool get_job_to_migrate(JCR *jcr)
393 {
394    char ed1[30];
395    POOL_MEM query(PM_MESSAGE);
396    POOLMEM *JobIds = get_pool_memory(PM_MESSAGE);
397    JobId_t JobId;
398    int stat, rc;
399    char *p;
400    dlist *item_chain;
401    uitem *item; 
402    char prbuf[500];
403    regex_t preg;
404
405    if (jcr->MigrateJobId != 0) {
406       jcr->previous_jr.JobId = jcr->MigrateJobId;
407    } else {
408       switch (jcr->job->selection_type) {
409       case MT_SMALLEST_VOL:
410          Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
411          JobIds = get_pool_memory(PM_MESSAGE);
412          JobIds[0] = 0;
413          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
414             Jmsg(jcr, M_FATAL, 0,
415                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
416             goto bail_out;
417          }
418          if (JobIds[0] == 0) {
419             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
420             goto ok_out;
421          }
422          /* ***FIXME*** must loop over JobIds */
423          Mmsg(query, sql_jobids_from_mediaid, JobIds);
424          JobIds[0] = 0;
425          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
426             Jmsg(jcr, M_FATAL, 0,
427                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
428             goto bail_out;
429          }
430          Dmsg1(000, "Jobids=%s\n", JobIds);
431          break;
432       case MT_OLDEST_VOL:
433          Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
434          JobIds = get_pool_memory(PM_MESSAGE);
435          JobIds[0] = 0;
436          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
437             Jmsg(jcr, M_FATAL, 0,
438                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
439             goto bail_out;
440          }
441          if (JobIds[0] == 0) {
442             Jmsg(jcr, M_INFO, 0, _("No Volume found to migrate.\n"));
443             goto ok_out;
444          }
445          Mmsg(query, sql_jobids_from_mediaid, JobIds);
446          JobIds[0] = 0;
447          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
448             Jmsg(jcr, M_FATAL, 0,
449                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
450             goto bail_out;
451          }
452          Dmsg1(000, "Jobids=%s\n", JobIds);
453          break;
454       case MT_POOL_OCCUPANCY:
455          Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
456          JobIds = get_pool_memory(PM_MESSAGE);
457          JobIds[0] = 0;
458          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
459             Jmsg(jcr, M_FATAL, 0,
460                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
461             goto bail_out;
462          }
463          if (JobIds[0] == 0) {
464             Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
465             goto ok_out;
466          }
467          break;
468       case MT_POOL_TIME:
469          break;
470       case MT_CLIENT:
471          if (!jcr->job->selection_pattern) {
472             Jmsg(jcr, M_FATAL, 0, _("No selection pattern specified.\n"));
473             goto bail_out;
474          }
475          rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
476          if (rc != 0) {
477             regerror(rc, &preg, prbuf, sizeof(prbuf));
478             Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
479                  jcr->job->selection_pattern, prbuf);
480          }
481          item_chain = New(dlist(item, &item->link));
482          Mmsg(query, sql_client, jcr->pool->hdr.name);
483          Dmsg1(100, "query=%s\n", query.c_str());
484          if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
485               (void *)item_chain)) {
486             Jmsg(jcr, M_FATAL, 0,
487                  _("SQL to get Client failed. ERR=%s\n"), db_strerror(jcr->db));
488             goto bail_out;
489          }
490          /* Now apply the regex and create the jobs */
491          foreach_dlist(item, item_chain) {
492             const int nmatch = 30;
493             regmatch_t pmatch[nmatch];
494             rc = regexec(&preg, item->item, nmatch, pmatch,  0);
495             if (rc == 0) {
496                Dmsg1(000, "Do Client=%s\n", item->item);
497             }
498             free(item->item);
499          }
500          regfree(&preg);
501          delete item_chain;
502          break;
503       case MT_VOLUME:
504          if (!jcr->job->selection_pattern) {
505             Jmsg(jcr, M_FATAL, 0, _("No selection pattern specified.\n"));
506             goto bail_out;
507          }
508          rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
509          if (rc != 0) {
510             regerror(rc, &preg, prbuf, sizeof(prbuf));
511             Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
512                  jcr->job->selection_pattern, prbuf);
513          }
514          item_chain = New(dlist(item, &item->link));
515          Mmsg(query, sql_vol, jcr->pool->hdr.name);
516          Dmsg1(100, "query=%s\n", query.c_str());
517          if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
518               (void *)item_chain)) {
519             Jmsg(jcr, M_FATAL, 0,
520                  _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
521             goto bail_out;
522          }
523          /* Now apply the regex and create the jobs */
524          foreach_dlist(item, item_chain) {
525             const int nmatch = 30;
526             regmatch_t pmatch[nmatch];
527             rc = regexec(&preg, item->item, nmatch, pmatch,  0);
528             if (rc == 0) {
529                Dmsg1(000, "Do Vol=%s\n", item->item);
530             }
531             free(item->item);
532          }
533          regfree(&preg);
534          delete item_chain;
535          break;
536       case MT_JOB:
537          if (!jcr->job->selection_pattern) {
538             Jmsg(jcr, M_FATAL, 0, _("No selection pattern specified.\n"));
539             goto bail_out;
540          }
541          rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
542          if (rc != 0) {
543             regerror(rc, &preg, prbuf, sizeof(prbuf));
544             Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
545                  jcr->job->selection_pattern, prbuf);
546          }
547          item_chain = New(dlist(item, &item->link));
548          Mmsg(query, sql_job, jcr->pool->hdr.name);
549          Dmsg1(100, "query=%s\n", query.c_str());
550          if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
551               (void *)item_chain)) {
552             Jmsg(jcr, M_FATAL, 0,
553                  _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
554             goto bail_out;
555          }
556          /* Now apply the regex and create the jobs */
557          foreach_dlist(item, item_chain) {
558             const int nmatch = 30;
559             regmatch_t pmatch[nmatch];
560             rc = regexec(&preg, item->item, nmatch, pmatch,  0);
561             if (rc == 0) {
562                Dmsg1(000, "Do Job=%s\n", item->item);
563             }
564             free(item->item);
565          }
566          regfree(&preg);
567          delete item_chain;
568          break;
569       case MT_SQLQUERY:
570          JobIds[0] = 0;
571          if (!jcr->job->selection_pattern) {
572             Jmsg(jcr, M_FATAL, 0, _("No selection pattern specified.\n"));
573             goto bail_out;
574          }
575          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
576             Jmsg(jcr, M_FATAL, 0,
577                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
578             goto bail_out;
579          }
580          if (JobIds[0] == 0) {
581             Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
582             goto ok_out;
583          }
584          Dmsg1(000, "Jobids=%s\n", JobIds);
585          goto bail_out;
586          break;
587       default:
588          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
589          goto bail_out;
590       }
591    }
592
593    p = JobIds;
594    stat = get_next_jobid_from_list(&p, &JobId);
595    Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
596    if (stat < 0) {
597       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
598       goto bail_out;
599    } else if (stat == 0) {
600       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
601       goto ok_out;
602    }
603    
604    jcr->previous_jr.JobId = JobId;
605    Dmsg1(000, "Last jobid=%d\n", jcr->previous_jr.JobId);
606
607    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
608       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
609            edit_int64(jcr->previous_jr.JobId, ed1),
610            db_strerror(jcr->db));
611       goto bail_out;
612    }
613    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
614       jcr->previous_jr.JobId, jcr->previous_jr.Job);
615
616 ok_out:
617    free_pool_memory(JobIds);
618    return true;
619
620 bail_out:
621    free_pool_memory(JobIds);
622    return false;
623 }
624
625
626 /*
627  * Release resources allocated during backup.
628  */
629 void migration_cleanup(JCR *jcr, int TermCode)
630 {
631    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
632    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
633    char term_code[100], sd_term_msg[100];
634    const char *term_msg;
635    int msg_type;
636    MEDIA_DBR mr;
637    double kbps;
638    utime_t RunTime;
639    JCR *tjcr = jcr->previous_jcr;
640    POOL_MEM query(PM_MESSAGE);
641
642    /* Ensure target is defined to avoid a lot of testing */
643    if (!tjcr) {
644       tjcr = jcr;
645    }
646    tjcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
647    tjcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
648    tjcr->VolSessionId = jcr->VolSessionId;
649    tjcr->VolSessionTime = jcr->VolSessionTime;
650
651    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
652    dequeue_messages(jcr);             /* display any queued messages */
653    memset(&mr, 0, sizeof(mr));
654    set_jcr_job_status(jcr, TermCode);
655    set_jcr_job_status(tjcr, TermCode);
656
657
658    update_job_end_record(jcr);        /* update database */
659    update_job_end_record(tjcr);
660
661    Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
662                "JobTDate=%s WHERE JobId=%s", 
663       jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
664       edit_uint64(jcr->previous_jr.JobTDate, ec1),
665       edit_uint64(tjcr->jr.JobId, ec2));
666    db_sql_query(tjcr->db, query.c_str(), NULL, NULL);
667
668    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
669       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
670          db_strerror(jcr->db));
671       set_jcr_job_status(jcr, JS_ErrorTerminated);
672    }
673
674    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
675    if (!db_get_media_record(jcr, jcr->db, &mr)) {
676       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
677          mr.VolumeName, db_strerror(jcr->db));
678       set_jcr_job_status(jcr, JS_ErrorTerminated);
679    }
680
681    update_bootstrap_file(tjcr);
682
683    msg_type = M_INFO;                 /* by default INFO message */
684    switch (jcr->JobStatus) {
685       case JS_Terminated:
686          if (jcr->Errors || jcr->SDErrors) {
687             term_msg = _("%s OK -- with warnings");
688          } else {
689             term_msg = _("%s OK");
690          }
691          break;
692       case JS_FatalError:
693       case JS_ErrorTerminated:
694          term_msg = _("*** %s Error ***");
695          msg_type = M_ERROR;          /* Generate error message */
696          if (jcr->store_bsock) {
697             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
698             if (jcr->SD_msg_chan) {
699                pthread_cancel(jcr->SD_msg_chan);
700             }
701          }
702          break;
703       case JS_Canceled:
704          term_msg = _("%s Canceled");
705          if (jcr->store_bsock) {
706             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
707             if (jcr->SD_msg_chan) {
708                pthread_cancel(jcr->SD_msg_chan);
709             }
710          }
711          break;
712       default:
713          term_msg = _("Inappropriate %s term code");
714          break;
715    }
716    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
717    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
718    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
719    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
720    if (RunTime <= 0) {
721       kbps = 0;
722    } else {
723       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
724    }
725    if (!db_get_job_volume_names(tjcr, tjcr->db, tjcr->jr.JobId, &tjcr->VolumeName)) {
726       /*
727        * Note, if the job has erred, most likely it did not write any
728        *  tape, so suppress this "error" message since in that case
729        *  it is normal.  Or look at it the other way, only for a
730        *  normal exit should we complain about this error.
731        */
732       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
733          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(tjcr->db));
734       }
735       tjcr->VolumeName[0] = 0;         /* none */
736    }
737
738    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
739
740 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
741
742    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
743 "  Old Backup JobId:       %u\n"
744 "  New Backup JobId:       %u\n"
745 "  JobId:                  %u\n"
746 "  Job:                    %s\n"
747 "  Backup Level:           %s%s\n"
748 "  Client:                 %s\n"
749 "  FileSet:                \"%s\" %s\n"
750 "  Pool:                   \"%s\"\n"
751 "  Start time:             %s\n"
752 "  End time:               %s\n"
753 "  Elapsed time:           %s\n"
754 "  Priority:               %d\n"
755 "  SD Files Written:       %s\n"
756 "  SD Bytes Written:       %s (%sB)\n"
757 "  Rate:                   %.1f KB/s\n"
758 "  Volume name(s):         %s\n"
759 "  Volume Session Id:      %d\n"
760 "  Volume Session Time:    %d\n"
761 "  Last Volume Bytes:      %s (%sB)\n"
762 "  SD Errors:              %d\n"
763 "  SD termination status:  %s\n"
764 "  Termination:            %s\n\n"),
765    VERSION,
766    LSMDATE,
767         edt, 
768         jcr->previous_jr.JobId,
769         tjcr->jr.JobId,
770         jcr->jr.JobId,
771         jcr->jr.Job,
772         level_to_str(jcr->JobLevel), jcr->since,
773         jcr->client->hdr.name,
774         jcr->fileset->hdr.name, jcr->FSCreateTime,
775         jcr->pool->hdr.name,
776         sdt,
777         edt,
778         edit_utime(RunTime, elapsed, sizeof(elapsed)),
779         jcr->JobPriority,
780         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
781         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
782         edit_uint64_with_suffix(jcr->jr.JobBytes, ec3),
783         (float)kbps,
784         tjcr->VolumeName,
785         jcr->VolSessionId,
786         jcr->VolSessionTime,
787         edit_uint64_with_commas(mr.VolBytes, ec4),
788         edit_uint64_with_suffix(mr.VolBytes, ec5),
789         jcr->SDErrors,
790         sd_term_msg,
791         term_code);
792
793    Dmsg1(100, "Leave migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
794    if (jcr->previous_jcr) {
795       free_jcr(jcr->previous_jcr);
796    }
797 }