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