]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
11Apr06
[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(000, "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(000, "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(000, "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    Dmsg1(000, "Item=%s\n", row[0]);
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 Job,Pool"
374    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
375
376 const char *sql_jobids_from_job =
377    "SELECT DISTINCT Job.JobId FROM Job,Pool"
378    " WHERE Job.Name=%s AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
379    " ORDER by Job.StartTime";
380
381
382 const char *sql_ujobid =
383    "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
384    " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
385    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
386
387 const char *sql_vol = 
388    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
389    " VolStatus in ('Full','Used','Error') AND"
390    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
391
392
393 /*
394  * Returns: false on error
395  *          true  if OK and jcr->previous_jr filled in
396  */
397 static bool get_job_to_migrate(JCR *jcr)
398 {
399    char ed1[30];
400    POOL_MEM query(PM_MESSAGE);
401    POOLMEM *JobIds = get_pool_memory(PM_MESSAGE);
402    JobId_t JobId;
403    int stat, rc;
404    char *p;
405    dlist *item_chain;
406    uitem *item = NULL;
407    uitem *last_item = NULL;
408    char prbuf[500];
409    regex_t preg;
410
411    JobIds[0] = 0;
412    if (jcr->MigrateJobId != 0) {
413       jcr->previous_jr.JobId = jcr->MigrateJobId;
414       Dmsg1(000, "previous jobid=%u\n", jcr->MigrateJobId);
415    } else {
416       switch (jcr->job->selection_type) {
417       case MT_SMALLEST_VOL:
418          Mmsg(query, sql_smallest_vol, jcr->pool->hdr.name);
419          JobIds = get_pool_memory(PM_MESSAGE);
420          JobIds[0] = 0;
421          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
422             Jmsg(jcr, M_FATAL, 0,
423                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
424             goto bail_out;
425          }
426          if (JobIds[0] == 0) {
427             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
428             goto ok_out;
429          }
430          /* ***FIXME*** must loop over JobIds */
431          Mmsg(query, sql_jobids_from_mediaid, JobIds);
432          JobIds[0] = 0;
433          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
434             Jmsg(jcr, M_FATAL, 0,
435                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
436             goto bail_out;
437          }
438          Dmsg1(000, "Smallest Vol Jobids=%s\n", JobIds);
439          break;
440       case MT_OLDEST_VOL:
441          Mmsg(query, sql_oldest_vol, jcr->pool->hdr.name);
442          JobIds = get_pool_memory(PM_MESSAGE);
443          JobIds[0] = 0;
444          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
445             Jmsg(jcr, M_FATAL, 0,
446                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
447             goto bail_out;
448          }
449          if (JobIds[0] == 0) {
450             Jmsg(jcr, M_INFO, 0, _("No Volume found to migrate.\n"));
451             goto ok_out;
452          }
453          Mmsg(query, sql_jobids_from_mediaid, JobIds);
454          JobIds[0] = 0;
455          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
456             Jmsg(jcr, M_FATAL, 0,
457                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
458             goto bail_out;
459          }
460          Dmsg1(000, "Oldest Vol Jobids=%s\n", JobIds);
461          break;
462       case MT_POOL_OCCUPANCY:
463          Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
464          JobIds = get_pool_memory(PM_MESSAGE);
465          JobIds[0] = 0;
466          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
467             Jmsg(jcr, M_FATAL, 0,
468                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
469             goto bail_out;
470          }
471          if (JobIds[0] == 0) {
472             Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
473             goto ok_out;
474          }
475          Dmsg1(000, "Pool Occupancy Jobids=%s\n", JobIds);
476          break;
477       case MT_POOL_TIME:
478          Dmsg0(000, "Pool time not implemented\n");
479          break;
480       case MT_CLIENT:
481          if (!jcr->job->selection_pattern) {
482             Jmsg(jcr, M_FATAL, 0, _("No Migration Client selection pattern specified.\n"));
483             goto bail_out;
484          }
485          Dmsg1(000, "Client regex=%s\n", jcr->job->selection_pattern);
486          rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
487          if (rc != 0) {
488             regerror(rc, &preg, prbuf, sizeof(prbuf));
489             Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
490                  jcr->job->selection_pattern, prbuf);
491          }
492          item_chain = New(dlist(item, &item->link));
493          Mmsg(query, sql_client, jcr->pool->hdr.name);
494          Dmsg1(100, "query=%s\n", query.c_str());
495          if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
496               (void *)item_chain)) {
497             Jmsg(jcr, M_FATAL, 0,
498                  _("SQL to get Client failed. ERR=%s\n"), db_strerror(jcr->db));
499             goto bail_out;
500          }
501          /* Now apply the regex and create the jobs */
502          foreach_dlist(item, item_chain) {
503             const int nmatch = 30;
504             regmatch_t pmatch[nmatch];
505             rc = regexec(&preg, item->item, nmatch, pmatch,  0);
506             if (rc == 0) {
507                Dmsg1(000, "Do Client=%s\n", item->item);
508             }
509             free(item->item);
510          }
511          regfree(&preg);
512          delete item_chain;
513          break;
514       case MT_VOLUME:
515          if (!jcr->job->selection_pattern) {
516             Jmsg(jcr, M_FATAL, 0, _("No Migration Volume selection pattern specified.\n"));
517             goto bail_out;
518          }
519          Dmsg1(000, "Volume regex=%s\n", jcr->job->selection_pattern);
520          rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
521          if (rc != 0) {
522             regerror(rc, &preg, prbuf, sizeof(prbuf));
523             Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
524                  jcr->job->selection_pattern, prbuf);
525          }
526          item_chain = New(dlist(item, &item->link));
527          Mmsg(query, sql_vol, jcr->pool->hdr.name);
528          Dmsg1(100, "query=%s\n", query.c_str());
529          if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
530               (void *)item_chain)) {
531             Jmsg(jcr, M_FATAL, 0,
532                  _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
533             goto bail_out;
534          }
535          /* Now apply the regex and create the jobs */
536          foreach_dlist(item, item_chain) {
537             const int nmatch = 30;
538             regmatch_t pmatch[nmatch];
539             rc = regexec(&preg, item->item, nmatch, pmatch,  0);
540             if (rc == 0) {
541                Dmsg1(000, "Do Vol=%s\n", item->item);
542             }
543             free(item->item);
544          }
545          regfree(&preg);
546          delete item_chain;
547          break;
548       case MT_JOB:
549          if (!jcr->job->selection_pattern) {
550             Jmsg(jcr, M_FATAL, 0, _("No Migration Job selection pattern specified.\n"));
551             goto bail_out;
552          }
553          Dmsg1(000, "Job regex=%s\n", jcr->job->selection_pattern);
554          rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
555          if (rc != 0) {
556             regerror(rc, &preg, prbuf, sizeof(prbuf));
557             Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
558                  jcr->job->selection_pattern, prbuf);
559             goto bail_out;
560          }
561          item_chain = New(dlist(item, &item->link));
562          Mmsg(query, sql_job, jcr->pool->hdr.name);
563          Dmsg1(000, "query=%s\n", query.c_str());
564          if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
565               (void *)item_chain)) {
566             Jmsg(jcr, M_FATAL, 0,
567                  _("SQL to get Job failed. ERR=%s\n"), db_strerror(jcr->db));
568             goto bail_out;
569          }
570          /* Now apply the regex and remove any item not matched */
571          foreach_dlist(item, item_chain) {
572             const int nmatch = 30;
573             regmatch_t pmatch[nmatch];
574             if (last_item) {
575                free(last_item->item);
576                item_chain->remove(last_item);
577             }
578             Dmsg1(000, "Jobitem=%s\n", item->item);
579             rc = regexec(&preg, item->item, nmatch, pmatch,  0);
580             if (rc == 0) {
581                last_item = NULL;   /* keep this one */
582             } else {   
583                last_item = item;
584             }
585          }
586          if (last_item) {
587             free(last_item->item);
588             item_chain->remove(last_item);
589          }
590          regfree(&preg);
591          /* 
592           * At this point, we have a list of items in item_chain
593           *  that have been matched by the regex, so now we need
594           *  to look up their jobids.
595           */
596          JobIds = get_pool_memory(PM_MESSAGE);
597          JobIds[0] = 0;
598          foreach_dlist(item, item_chain) {
599             Dmsg1(000, "Got Job: %s\n", item->item);
600             Mmsg(query, sql_jobids_from_job, item->item, jcr->pool->hdr.name);
601             if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
602                Jmsg(jcr, M_FATAL, 0,
603                     _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
604                goto bail_out;
605             }
606          }
607          if (JobIds[0] == 0) {
608             Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
609             goto ok_out;
610          }
611          Dmsg1(000, "Job Jobids=%s\n", JobIds);
612          free_pool_memory(JobIds);
613          delete item_chain;
614          break;
615       case MT_SQLQUERY:
616          JobIds[0] = 0;
617          if (!jcr->job->selection_pattern) {
618             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
619             goto bail_out;
620          }
621          Dmsg1(000, "SQL=%s\n", jcr->job->selection_pattern);
622          if (!db_sql_query(jcr->db, query.c_str(), jobid_handler, (void *)JobIds)) {
623             Jmsg(jcr, M_FATAL, 0,
624                  _("SQL to get Volume failed. ERR=%s\n"), db_strerror(jcr->db));
625             goto bail_out;
626          }
627          if (JobIds[0] == 0) {
628             Jmsg(jcr, M_INFO, 0, _("No jobs found to migrate.\n"));
629             goto ok_out;
630          }
631          Dmsg1(000, "Jobids=%s\n", JobIds);
632          goto bail_out;
633          break;
634       default:
635          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
636          goto bail_out;
637       }
638    }
639
640    p = JobIds;
641    JobId = 0;
642    stat = get_next_jobid_from_list(&p, &JobId);
643    Dmsg2(000, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
644    if (stat < 0) {
645       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
646       goto bail_out;
647    } else if (stat == 0) {
648       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
649       goto ok_out;
650    }
651    
652    jcr->previous_jr.JobId = JobId;
653    Dmsg1(000, "Last jobid=%d\n", jcr->previous_jr.JobId);
654
655    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
656       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
657            edit_int64(jcr->previous_jr.JobId, ed1),
658            db_strerror(jcr->db));
659       goto bail_out;
660    }
661    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
662       jcr->previous_jr.JobId, jcr->previous_jr.Job);
663
664 ok_out:
665    free_pool_memory(JobIds);
666    return true;
667
668 bail_out:
669    free_pool_memory(JobIds);
670    return false;
671 }
672
673
674 /*
675  * Release resources allocated during backup.
676  */
677 void migration_cleanup(JCR *jcr, int TermCode)
678 {
679    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
680    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
681    char term_code[100], sd_term_msg[100];
682    const char *term_msg;
683    int msg_type;
684    MEDIA_DBR mr;
685    double kbps;
686    utime_t RunTime;
687    JCR *tjcr = jcr->previous_jcr;
688    POOL_MEM query(PM_MESSAGE);
689
690    /* Ensure target is defined to avoid a lot of testing */
691    if (!tjcr) {
692       tjcr = jcr;
693    }
694    tjcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
695    tjcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
696    tjcr->VolSessionId = jcr->VolSessionId;
697    tjcr->VolSessionTime = jcr->VolSessionTime;
698
699    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
700    dequeue_messages(jcr);             /* display any queued messages */
701    memset(&mr, 0, sizeof(mr));
702    set_jcr_job_status(jcr, TermCode);
703    set_jcr_job_status(tjcr, TermCode);
704
705
706    update_job_end_record(jcr);        /* update database */
707    update_job_end_record(tjcr);
708
709    Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
710                "JobTDate=%s WHERE JobId=%s", 
711       jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
712       edit_uint64(jcr->previous_jr.JobTDate, ec1),
713       edit_uint64(tjcr->jr.JobId, ec2));
714    db_sql_query(tjcr->db, query.c_str(), NULL, NULL);
715
716    if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
717       Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
718          db_strerror(jcr->db));
719       set_jcr_job_status(jcr, JS_ErrorTerminated);
720    }
721
722    bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
723    if (!db_get_media_record(jcr, jcr->db, &mr)) {
724       Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
725          mr.VolumeName, db_strerror(jcr->db));
726       set_jcr_job_status(jcr, JS_ErrorTerminated);
727    }
728
729    update_bootstrap_file(tjcr);
730
731    msg_type = M_INFO;                 /* by default INFO message */
732    switch (jcr->JobStatus) {
733       case JS_Terminated:
734          if (jcr->Errors || jcr->SDErrors) {
735             term_msg = _("%s OK -- with warnings");
736          } else {
737             term_msg = _("%s OK");
738          }
739          break;
740       case JS_FatalError:
741       case JS_ErrorTerminated:
742          term_msg = _("*** %s Error ***");
743          msg_type = M_ERROR;          /* Generate error message */
744          if (jcr->store_bsock) {
745             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
746             if (jcr->SD_msg_chan) {
747                pthread_cancel(jcr->SD_msg_chan);
748             }
749          }
750          break;
751       case JS_Canceled:
752          term_msg = _("%s Canceled");
753          if (jcr->store_bsock) {
754             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
755             if (jcr->SD_msg_chan) {
756                pthread_cancel(jcr->SD_msg_chan);
757             }
758          }
759          break;
760       default:
761          term_msg = _("Inappropriate %s term code");
762          break;
763    }
764    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
765    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
766    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
767    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
768    if (RunTime <= 0) {
769       kbps = 0;
770    } else {
771       kbps = (double)jcr->jr.JobBytes / (1000 * RunTime);
772    }
773    if (!db_get_job_volume_names(tjcr, tjcr->db, tjcr->jr.JobId, &tjcr->VolumeName)) {
774       /*
775        * Note, if the job has erred, most likely it did not write any
776        *  tape, so suppress this "error" message since in that case
777        *  it is normal.  Or look at it the other way, only for a
778        *  normal exit should we complain about this error.
779        */
780       if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
781          Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(tjcr->db));
782       }
783       tjcr->VolumeName[0] = 0;         /* none */
784    }
785
786    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
787
788 // bmicrosleep(15, 0);                /* for debugging SIGHUP */
789
790    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
791 "  Old Backup JobId:       %u\n"
792 "  New Backup JobId:       %u\n"
793 "  JobId:                  %u\n"
794 "  Job:                    %s\n"
795 "  Backup Level:           %s%s\n"
796 "  Client:                 %s\n"
797 "  FileSet:                \"%s\" %s\n"
798 "  Pool:                   \"%s\"\n"
799 "  Start time:             %s\n"
800 "  End time:               %s\n"
801 "  Elapsed time:           %s\n"
802 "  Priority:               %d\n"
803 "  SD Files Written:       %s\n"
804 "  SD Bytes Written:       %s (%sB)\n"
805 "  Rate:                   %.1f KB/s\n"
806 "  Volume name(s):         %s\n"
807 "  Volume Session Id:      %d\n"
808 "  Volume Session Time:    %d\n"
809 "  Last Volume Bytes:      %s (%sB)\n"
810 "  SD Errors:              %d\n"
811 "  SD termination status:  %s\n"
812 "  Termination:            %s\n\n"),
813    VERSION,
814    LSMDATE,
815         edt, 
816         jcr->previous_jr.JobId,
817         tjcr->jr.JobId,
818         jcr->jr.JobId,
819         jcr->jr.Job,
820         level_to_str(jcr->JobLevel), jcr->since,
821         jcr->client->hdr.name,
822         jcr->fileset->hdr.name, jcr->FSCreateTime,
823         jcr->pool->hdr.name,
824         sdt,
825         edt,
826         edit_utime(RunTime, elapsed, sizeof(elapsed)),
827         jcr->JobPriority,
828         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
829         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
830         edit_uint64_with_suffix(jcr->jr.JobBytes, ec3),
831         (float)kbps,
832         tjcr->VolumeName,
833         jcr->VolSessionId,
834         jcr->VolSessionTime,
835         edit_uint64_with_commas(mr.VolBytes, ec4),
836         edit_uint64_with_suffix(mr.VolBytes, ec5),
837         jcr->SDErrors,
838         sd_term_msg,
839         term_code);
840
841    Dmsg1(100, "Leave migrate_cleanup() previous_jcr=0x%x\n", jcr->previous_jcr);
842    if (jcr->previous_jcr) {
843       free_jcr(jcr->previous_jcr);
844    }
845 }