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