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