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