]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
kes Change error message 'illegal' to 'invalid' -- bug #707
[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 static int get_next_dbid_from_list(char **p, DBId_t *DBId);
53
54 /* 
55  * Called here before the job is run to do the job
56  *   specific setup.
57  */
58 bool do_migration_init(JCR *jcr)
59 {
60    /* If we find a job or jobs to migrate it is previous_jr.JobId */
61    if (!get_job_to_migrate(jcr)) {
62       return false;
63    }
64
65    if (jcr->previous_jr.JobId == 0) {
66       return true;                    /* no work */
67    }
68
69    if (!get_or_create_fileset_record(jcr)) {
70       return false;
71    }
72
73    apply_pool_overrides(jcr);
74
75    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->hdr.name);
76    if (jcr->jr.PoolId == 0) {
77       return false;
78    }
79
80    /* If pool storage specified, use it instead of job storage */
81    copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
82
83    if (!jcr->wstorage) {
84       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Job or Pool.\n"));
85       return false;
86    }
87
88    create_restore_bootstrap_file(jcr);
89    return true;
90 }
91
92 /*
93  * Do a Migration of a previous job
94  *
95  *  Returns:  false on failure
96  *            true  on success
97  */
98 bool do_migration(JCR *jcr)
99 {
100    POOL_DBR pr;
101    POOL *pool;
102    char ed1[100];
103    BSOCK *sd;
104    JOB *job, *prev_job;
105    JCR *mig_jcr;                   /* newly migrated job */
106
107    /* 
108     *  previous_jr refers to the job DB record of the Job that is
109     *    going to be migrated.
110     *  prev_job refers to the job resource of the Job that is
111     *    going to be migrated.
112     *  jcr is the jcr for the current "migration" job.  It is a
113     *    control job that is put in the DB as a migration job, which
114     *    means that this job migrated a previous job to a new job.
115     *    No Volume or File data is associated with this control
116     *    job.
117     *  mig_jcr refers to the newly migrated job that is run by
118     *    the current jcr.  It is a backup job that moves (migrates) the
119     *    data written for the previous_jr into the new pool.  This
120     *    job (mig_jcr) becomes the new backup job that replaces
121     *    the original backup job.
122     */
123    if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
124       set_jcr_job_status(jcr, JS_Terminated);
125       migration_cleanup(jcr, jcr->JobStatus);
126       return true;                    /* no work */
127    }
128
129    Dmsg4(dbglevel, "Previous: Name=%s JobId=%d Type=%c Level=%c\n",
130       jcr->previous_jr.Name, jcr->previous_jr.JobId, 
131       jcr->previous_jr.JobType, jcr->previous_jr.JobLevel);
132
133    Dmsg4(dbglevel, "Current: Name=%s JobId=%d Type=%c Level=%c\n",
134       jcr->jr.Name, jcr->jr.JobId, 
135       jcr->jr.JobType, jcr->jr.JobLevel);
136
137    LockRes();
138    job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
139    prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
140    UnlockRes();
141    if (!job || !prev_job) {
142       return false;
143    }
144
145    /* Create a migation jcr */
146    mig_jcr = jcr->mig_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
147    memcpy(&mig_jcr->previous_jr, &jcr->previous_jr, sizeof(mig_jcr->previous_jr));
148
149    /*
150     * Turn the mig_jcr into a "real" job that takes on the aspects of
151     *   the previous backup job "prev_job".
152     */
153    set_jcr_defaults(mig_jcr, prev_job);
154    if (!setup_job(mig_jcr)) {
155       return false;
156    }
157
158    /* Now reset the job record from the previous job */
159    memcpy(&mig_jcr->jr, &jcr->previous_jr, sizeof(mig_jcr->jr));
160    /* Update the jr to reflect the new values of PoolId, FileSetId, and JobId. */
161    mig_jcr->jr.PoolId = jcr->jr.PoolId;
162    mig_jcr->jr.FileSetId = jcr->jr.FileSetId;
163    mig_jcr->jr.JobId = mig_jcr->JobId;
164
165    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
166       mig_jcr->jr.Name, mig_jcr->jr.JobId, 
167       mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
168
169    /*
170     * Get the PoolId used with the original job. Then
171     *  find the pool name from the database record.
172     */
173    memset(&pr, 0, sizeof(pr));
174    pr.PoolId = mig_jcr->previous_jr.PoolId;
175    if (!db_get_pool_record(jcr, jcr->db, &pr)) {
176       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
177             edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
178          return false;
179    }
180    /* Get the pool resource corresponding to the original job */
181    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
182    if (!pool) {
183       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
184       return false;
185    }
186
187    /* If pool storage specified, use it for restore */
188    copy_rstorage(mig_jcr, pool->storage, _("Pool resource"));
189    copy_rstorage(jcr, pool->storage, _("Pool resource"));
190
191    /*
192     * If the original backup pool has a NextPool, make sure a 
193     *  record exists in the database. Note, in this case, we
194     *  will be migrating from pool to pool->NextPool.
195     */
196    if (pool->NextPool) {
197       jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->hdr.name);
198       if (jcr->jr.PoolId == 0) {
199          return false;
200       }
201       /*
202        * put the "NextPool" resource pointer in our jcr so that we
203        * can pull the Storage reference from it.
204        */
205       mig_jcr->pool = jcr->pool = pool->NextPool;
206       mig_jcr->jr.PoolId = jcr->jr.PoolId;
207       pm_strcpy(jcr->pool_source, _("NextPool in Pool resource"));
208    }
209
210    /* If pool storage specified, use it instead of job storage for backup */
211    copy_wstorage(jcr, jcr->pool->storage, _("Pool resource"));
212
213    /* Print Job Start message */
214    Jmsg(jcr, M_INFO, 0, _("Start Migration JobId %s, Job=%s\n"),
215         edit_uint64(jcr->JobId, ed1), jcr->Job);
216
217    set_jcr_job_status(jcr, JS_Running);
218    set_jcr_job_status(mig_jcr, JS_Running);
219    Dmsg2(dbglevel, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel);
220
221    /* Update job start record for this migration control job */
222    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
223       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
224       return false;
225    }
226
227    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
228       mig_jcr->jr.Name, mig_jcr->jr.JobId, 
229       mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
230
231    /* Update job start record for the real migration backup job */
232    if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
233       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(mig_jcr->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(mig_jcr, 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(dbglevel, "Read store=%s, write store=%s\n", 
258       ((STORE *)jcr->rstorage->first())->name(),
259       ((STORE *)jcr->wstorage->first())->name());
260    if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
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    if (!bnet_fsend(sd, "run")) {
271       return false;
272    }
273
274    /*
275     * Now start a Storage daemon message thread
276     */
277    if (!start_storage_daemon_message_thread(jcr)) {
278       return false;
279    }
280
281
282    set_jcr_job_status(jcr, JS_Running);
283    set_jcr_job_status(mig_jcr, JS_Running);
284
285    /* Pickup Job termination data */
286    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */
287    wait_for_storage_daemon_termination(jcr);
288
289    set_jcr_job_status(jcr, jcr->SDJobStatus);
290    if (jcr->JobStatus != JS_Terminated) {
291       return false;
292    }
293    migration_cleanup(jcr, jcr->JobStatus);
294    if (mig_jcr) {
295       UAContext *ua = new_ua_context(jcr);
296       purge_files_from_job(ua, jcr->previous_jr.JobId);
297       free_ua_context(ua);
298    }
299    return true;
300 }
301
302 struct idpkt {
303    POOLMEM *list;
304    uint32_t count;
305 };
306
307 /*
308  * Callback handler make list of DB Ids
309  */
310 static int dbid_handler(void *ctx, int num_fields, char **row)
311 {
312    idpkt *ids = (idpkt *)ctx;
313
314    Dmsg3(dbglevel, "count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
315    if (ids->count == 0) {
316       ids->list[0] = 0;
317    } else {
318       pm_strcat(ids->list, ",");
319    }
320    pm_strcat(ids->list, row[0]);
321    ids->count++;
322    return 0;
323 }
324
325
326 struct uitem {
327    dlink link;   
328    char *item;
329 };
330
331 static int item_compare(void *item1, void *item2)
332 {
333    uitem *i1 = (uitem *)item1;
334    uitem *i2 = (uitem *)item2;
335    return strcmp(i1->item, i2->item);
336 }
337
338 static int unique_name_handler(void *ctx, int num_fields, char **row)
339 {
340    dlist *list = (dlist *)ctx;
341
342    uitem *new_item = (uitem *)malloc(sizeof(uitem));
343    uitem *item;
344    
345    memset(new_item, 0, sizeof(uitem));
346    new_item->item = bstrdup(row[0]);
347    Dmsg1(dbglevel, "Item=%s\n", row[0]);
348    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
349    if (item != new_item) {            /* already in list */
350       free(new_item->item);
351       free((char *)new_item);
352       return 0;
353    }
354    return 0;
355 }
356
357 static int unique_dbid_handler(void *ctx, int num_fields, char **row)
358 {
359    dlist *list = (dlist *)ctx;
360
361    uitem *new_item = (uitem *)malloc(sizeof(uitem));
362    uitem *item;
363    
364    memset(new_item, 0, sizeof(uitem));
365    new_item->item = bstrdup(row[0]);
366    Dmsg1(dbglevel, "Item=%s\n", row[0]);
367    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
368    if (item != new_item) {            /* already in list */
369       free(new_item->item);
370       free((char *)new_item);
371       return 0;
372    }
373    return 0;
374 }
375
376
377
378
379 /* Get Job names in Pool */
380 const char *sql_job =
381    "SELECT DISTINCT Job.Name from Job,Pool"
382    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
383
384 /* Get JobIds from regex'ed Job names */
385 const char *sql_jobids_from_job =
386    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
387    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
388    " ORDER by Job.StartTime";
389
390 /* Get Client names in Pool */
391 const char *sql_client =
392    "SELECT DISTINCT Client.Name from Client,Pool,Job"
393    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
394    " Job.PoolId=Pool.PoolId";
395
396 /* Get JobIds from regex'ed Client names */
397 const char *sql_jobids_from_client =
398    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool,Client"
399    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
400    " AND Job.ClientId=Client.ClientId "
401    " ORDER by Job.StartTime";
402
403 /* Get Volume names in Pool */
404 const char *sql_vol = 
405    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
406    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
407    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
408
409 /* Get JobIds from regex'ed Volume names */
410 const char *sql_jobids_from_vol =
411    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
412    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
413    " AND JobMedia.JobId=Job.JobId" 
414    " ORDER by Job.StartTime";
415
416
417 const char *sql_smallest_vol = 
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'"
421    " ORDER BY VolBytes ASC LIMIT 1";
422
423 const char *sql_oldest_vol = 
424    "SELECT MediaId FROM Media,Pool WHERE"
425    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
426    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
427    " ORDER BY LastWritten ASC LIMIT 1";
428
429 /* Get JobIds when we have selected MediaId */
430 const char *sql_jobids_from_mediaid =
431    "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
432    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=%s"
433    " ORDER by Job.StartTime";
434
435 /* Get tne number of bytes in the pool */
436 const char *sql_pool_bytes =
437    "SELECT SUM(VolBytes) FROM Media,Pool WHERE"
438    " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
439    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
440
441 /* Get tne number of bytes in the Jobs */
442 const char *sql_job_bytes =
443    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)";
444
445
446 /* Get Media Ids in Pool */
447 const char *sql_mediaids =
448    "SELECT MediaId FROM Media,Pool WHERE"
449    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
450    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' ORDER BY LastWritten ASC";
451
452 /* Get JobIds in Pool longer than specified time */
453 const char *sql_pool_time = 
454    "SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE"
455    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
456    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
457    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"
458    " AND Job.RealEndTime<='%s'";
459
460 /*
461 * const char *sql_ujobid =
462 *   "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
463 *   " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
464 *   " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
465 */
466
467
468
469 /*
470  *
471  * This is the central piece of code that finds a job or jobs 
472  *   actually JobIds to migrate.  It first looks to see if one
473  *   has been "manually" specified in jcr->MigrateJobId, and if
474  *   so, it returns that JobId to be run.  Otherwise, it
475  *   examines the Selection Type to see what kind of migration
476  *   we are doing (Volume, Job, Client, ...) and applies any
477  *   Selection Pattern if appropriate to obtain a list of JobIds.
478  *   Finally, it will loop over all the JobIds found, except the last
479  *   one starting a new job with MigrationJobId set to that JobId, and
480  *   finally, it returns the last JobId to the caller.
481  *
482  * Returns: false on error
483  *          true  if OK and jcr->previous_jr filled in
484  */
485 static bool get_job_to_migrate(JCR *jcr)
486 {
487    char ed1[30];
488    POOL_MEM query(PM_MESSAGE);
489    JobId_t JobId;
490    DBId_t  MediaId = 0;
491    int stat;
492    char *p;
493    idpkt ids, mid, jids;
494    db_int64_ctx ctx;
495    int64_t pool_bytes;
496    bool ok;
497    time_t ttime;
498    struct tm tm;
499    char dt[MAX_TIME_LENGTH];
500
501    ids.list = get_pool_memory(PM_MESSAGE);
502    ids.list[0] = 0;
503    ids.count = 0;
504    mid.list = get_pool_memory(PM_MESSAGE);
505    mid.list[0] = 0;
506    mid.count = 0;
507    jids.list = get_pool_memory(PM_MESSAGE);
508    jids.list[0] = 0;
509    jids.count = 0;
510
511
512    /*
513     * If MigrateJobId is set, then we migrate only that Job,
514     *  otherwise, we go through the full selection of jobs to
515     *  migrate.
516     */
517    if (jcr->MigrateJobId != 0) {
518       Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
519       edit_uint64(jcr->MigrateJobId, ids.list);
520       ids.count = 1;
521    } else {
522       switch (jcr->job->selection_type) {
523       case MT_JOB:
524          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
525             goto bail_out;
526          } 
527          break;
528       case MT_CLIENT:
529          if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
530             goto bail_out;
531          } 
532          break;
533       case MT_VOLUME:
534          if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
535             goto bail_out;
536          } 
537          break;
538       case MT_SQLQUERY:
539          if (!jcr->job->selection_pattern) {
540             Jmsg(jcr, M_FATAL, 0, _("No Migration SQL selection pattern specified.\n"));
541             goto bail_out;
542          }
543          Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
544          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
545               dbid_handler, (void *)&ids)) {
546             Jmsg(jcr, M_FATAL, 0,
547                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
548             goto bail_out;
549          }
550          break;
551       case MT_SMALLEST_VOL:
552          if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
553             goto bail_out;
554          }
555          break;
556       case MT_OLDEST_VOL:
557          if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
558             goto bail_out;
559          }
560          break;
561
562       case MT_POOL_OCCUPANCY:
563          ctx.count = 0;
564          /* Find count of bytes in pool */
565          Mmsg(query, sql_pool_bytes, jcr->pool->hdr.name);
566          if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
567             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
568             goto bail_out;
569          }
570          if (ctx.count == 0) {
571             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
572             goto ok_out;
573          }
574          pool_bytes = ctx.value;
575          Dmsg2(dbglevel, "highbytes=%d pool=%d\n", (int)jcr->pool->MigrationHighBytes,
576                (int)pool_bytes);
577          if (pool_bytes < (int64_t)jcr->pool->MigrationHighBytes) {
578             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
579             goto ok_out;
580          }
581          Dmsg0(dbglevel, "We should do Occupation migration.\n");
582
583          ids.count = 0;
584          /* Find a list of MediaIds that could be migrated */
585          Mmsg(query, sql_mediaids, jcr->pool->hdr.name);
586 //       Dmsg1(dbglevel, "query=%s\n", query.c_str());
587          if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)&ids)) {
588             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
589             goto bail_out;
590          }
591          if (ids.count == 0) {
592             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
593             goto ok_out;
594          }
595          Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list);
596
597          /*
598           * Now loop over MediaIds getting more JobIds to migrate until
599           *  we reduce the pool occupancy below the low water mark.
600           */
601          p = ids.list;
602          for (int i=0; i < (int)ids.count; i++) {
603             stat = get_next_dbid_from_list(&p, &MediaId);
604             Dmsg2(dbglevel, "get_next_dbid stat=%d MediaId=%u\n", stat, MediaId);
605             if (stat < 0) {
606                Jmsg(jcr, M_FATAL, 0, _("Invalid MediaId found.\n"));
607                goto bail_out;
608             } else if (stat == 0) {
609                break;
610             }
611             mid.count = 1;
612             Mmsg(mid.list, "%s", edit_int64(MediaId, ed1));
613             ok = find_jobids_from_mediaid_list(jcr, &mid, "Volumes");
614             if (!ok) {
615                continue;
616             }
617             if (i != 0) {
618                pm_strcat(jids.list, ",");
619             }
620             pm_strcat(jids.list, mid.list);
621             jids.count += mid.count;
622
623             /* Now get the count of bytes added */
624             ctx.count = 0;
625             /* Find count of bytes from Jobs */
626             Mmsg(query, sql_job_bytes, mid.list);
627             if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
628                Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
629                goto bail_out;
630             }
631             pool_bytes -= ctx.value;
632             Dmsg1(dbglevel, "Job bytes=%d\n", (int)ctx.value);
633             Dmsg2(dbglevel, "lowbytes=%d pool=%d\n", (int)jcr->pool->MigrationLowBytes,
634                   (int)pool_bytes);
635             if (pool_bytes <= (int64_t)jcr->pool->MigrationLowBytes) {
636                Dmsg0(dbglevel, "We should be done.\n");
637                break;
638             }
639
640          }
641          Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", jids.count, jids.list);
642
643          break;
644
645       case MT_POOL_TIME:
646          ttime = time(NULL) - (time_t)jcr->pool->MigrationTime;
647          (void)localtime_r(&ttime, &tm);
648          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
649
650          ids.count = 0;
651          Mmsg(query, sql_pool_time, jcr->pool->hdr.name, dt);
652 //       Dmsg1(000, "query=%s\n", query.c_str());
653          if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)&ids)) {
654             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
655             goto bail_out;
656          }
657          if (ids.count == 0) {
658             Jmsg(jcr, M_INFO, 0, _("No Volumes found to migrate.\n"));
659             goto ok_out;
660          }
661          Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list);
662          break;
663
664       default:
665          Jmsg(jcr, M_FATAL, 0, _("Unknown Migration Selection Type.\n"));
666          goto bail_out;
667       }
668    }
669
670    /*
671     * Loop over all jobids except the last one, sending
672     *  them to start_migration_job(), which will start a job
673     *  for each of them.  For the last JobId, we handle it below.
674     */
675    p = ids.list;
676    Jmsg(jcr, M_INFO, 0, _("The following %u JobIds will be migrated: %s\n"),
677       ids.count, ids.list);
678    for (int i=1; i < (int)ids.count; i++) {
679       JobId = 0;
680       stat = get_next_jobid_from_list(&p, &JobId);
681       Dmsg2(dbglevel, "get_next_jobid stat=%d JobId=%u\n", stat, JobId);
682       jcr->MigrateJobId = JobId;
683       start_migration_job(jcr);
684       if (stat < 0) {
685          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
686          goto bail_out;
687       } else if (stat == 0) {
688          Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
689          goto ok_out;
690       }
691    }
692    
693    /* Now get the last JobId and handle it in the current job */
694    JobId = 0;
695    stat = get_next_jobid_from_list(&p, &JobId);
696    Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, JobId);
697    if (stat < 0) {
698       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
699       goto bail_out;
700    } else if (stat == 0) {
701       Jmsg(jcr, M_INFO, 0, _("No JobIds found to migrate.\n"));
702       goto ok_out;
703    }
704
705    jcr->previous_jr.JobId = JobId;
706    Dmsg1(100, "Previous jobid=%d\n", jcr->previous_jr.JobId);
707
708    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
709       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to migrate. ERR=%s"),
710            edit_int64(jcr->previous_jr.JobId, ed1),
711            db_strerror(jcr->db));
712       goto bail_out;
713    }
714    Jmsg(jcr, M_INFO, 0, _("Migration using JobId=%d Job=%s\n"),
715       jcr->previous_jr.JobId, jcr->previous_jr.Job);
716
717 ok_out:
718    free_pool_memory(ids.list);
719    free_pool_memory(mid.list);
720    free_pool_memory(jids.list);
721    return true;
722
723 bail_out:
724    free_pool_memory(ids.list);
725    free_pool_memory(mid.list);
726    free_pool_memory(jids.list);
727    return false;
728 }
729
730 static void start_migration_job(JCR *jcr)
731 {
732    UAContext *ua = new_ua_context(jcr);
733    char ed1[50];
734    ua->batch = true;
735    Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name, 
736         edit_uint64(jcr->MigrateJobId, ed1));
737    Dmsg1(dbglevel, "=============== Migration cmd=%s\n", ua->cmd);
738    parse_ua_args(ua);                 /* parse command */
739    int stat = run_cmd(ua, ua->cmd);
740    if (stat == 0) {
741       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
742    } else {
743       Jmsg(jcr, M_INFO, 0, _("Migration JobId %d started.\n"), stat);
744    }
745    free_ua_context(ua);
746 }
747
748 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
749                  const char *type) 
750 {
751    bool ok = false;
752    POOL_MEM query(PM_MESSAGE);
753
754    ids->count = 0;
755    /* Basic query for MediaId */
756    Mmsg(query, query1, jcr->pool->hdr.name);
757    if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
758       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
759       goto bail_out;
760    }
761    if (ids->count == 0) {
762       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
763    }
764    if (ids->count != 1) {
765       Jmsg(jcr, M_FATAL, 0, _("SQL logic error. Count should be 1 but is %d\n"), 
766          ids->count);
767       goto bail_out;
768    }
769    Dmsg1(dbglevel, "Smallest Vol Jobids=%s\n", ids->list);
770
771    ok = find_jobids_from_mediaid_list(jcr, ids, type);
772
773 bail_out:
774    return ok;
775 }
776
777 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type) 
778 {
779    bool ok = false;
780    POOL_MEM query(PM_MESSAGE);
781
782    Mmsg(query, sql_jobids_from_mediaid, ids->list);
783    ids->count = 0;
784    if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
785       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
786       goto bail_out;
787    }
788    if (ids->count == 0) {
789       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
790    }
791    ok = true;
792 bail_out:
793    return ok;
794 }
795
796 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
797                  const char *query2, const char *type) 
798 {
799    dlist *item_chain;
800    uitem *item = NULL;
801    uitem *last_item = NULL;
802    regex_t preg;
803    char prbuf[500];
804    int rc;
805    bool ok = false;
806    POOL_MEM query(PM_MESSAGE);
807
808    item_chain = New(dlist(item, &item->link));
809    if (!jcr->job->selection_pattern) {
810       Jmsg(jcr, M_FATAL, 0, _("No Migration %s selection pattern specified.\n"),
811          type);
812       goto bail_out;
813    }
814    Dmsg1(dbglevel, "regex=%s\n", jcr->job->selection_pattern);
815    /* Compile regex expression */
816    rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
817    if (rc != 0) {
818       regerror(rc, &preg, prbuf, sizeof(prbuf));
819       Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
820            jcr->job->selection_pattern, prbuf);
821       goto bail_out;
822    }
823    /* Basic query for names */
824    Mmsg(query, query1, jcr->pool->hdr.name);
825    Dmsg1(dbglevel, "query1=%s\n", query.c_str());
826    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
827         (void *)item_chain)) {
828       Jmsg(jcr, M_FATAL, 0,
829            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
830       goto bail_out;
831    }
832    /* Now apply the regex to the names and remove any item not matched */
833    foreach_dlist(item, item_chain) {
834       const int nmatch = 30;
835       regmatch_t pmatch[nmatch];
836       if (last_item) {
837          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
838          free(last_item->item);
839          item_chain->remove(last_item);
840       }
841       Dmsg1(dbglevel, "Item=%s\n", item->item);
842       rc = regexec(&preg, item->item, nmatch, pmatch,  0);
843       if (rc == 0) {
844          last_item = NULL;   /* keep this one */
845       } else {   
846          last_item = item;
847       }
848    }
849    if (last_item) {
850       free(last_item->item);
851       Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
852       item_chain->remove(last_item);
853    }
854    regfree(&preg);
855    /* 
856     * At this point, we have a list of items in item_chain
857     *  that have been matched by the regex, so now we need
858     *  to look up their jobids.
859     */
860    ids->count = 0;
861    foreach_dlist(item, item_chain) {
862       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
863       Mmsg(query, query2, item->item, jcr->pool->hdr.name);
864       Dmsg1(dbglevel, "query2=%s\n", query.c_str());
865       if (!db_sql_query(jcr->db, query.c_str(), dbid_handler, (void *)ids)) {
866          Jmsg(jcr, M_FATAL, 0,
867               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
868          goto bail_out;
869       }
870    }
871    if (ids->count == 0) {
872       Jmsg(jcr, M_INFO, 0, _("No %ss found to migrate.\n"), type);
873    }
874    ok = true;
875 bail_out:
876    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
877    delete item_chain;
878    Dmsg0(dbglevel, "After delete item_chain\n");
879    return ok;
880 }
881
882
883 /*
884  * Release resources allocated during backup.
885  */
886 void migration_cleanup(JCR *jcr, int TermCode)
887 {
888    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
889    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
890    char ec6[50], ec7[50], ec8[50];
891    char term_code[100], sd_term_msg[100];
892    const char *term_msg;
893    int msg_type;
894    MEDIA_DBR mr;
895    double kbps;
896    utime_t RunTime;
897    JCR *mig_jcr = jcr->mig_jcr;
898    POOL_MEM query(PM_MESSAGE);
899
900    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
901    dequeue_messages(jcr);             /* display any queued messages */
902    memset(&mr, 0, sizeof(mr));
903    set_jcr_job_status(jcr, TermCode);
904    update_job_end_record(jcr);        /* update database */
905
906    /* 
907     * Check if we actually did something.  
908     *  mig_jcr is jcr of the newly migrated job.
909     */
910    if (mig_jcr) {
911       mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
912       mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
913       mig_jcr->VolSessionId = jcr->VolSessionId;
914       mig_jcr->VolSessionTime = jcr->VolSessionTime;
915       mig_jcr->jr.RealEndTime = 0; 
916       mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
917
918       set_jcr_job_status(mig_jcr, TermCode);
919
920   
921       update_job_end_record(mig_jcr);
922      
923       /* Update final items to set them to the previous job's values */
924       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
925                   "JobTDate=%s WHERE JobId=%s", 
926          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
927          edit_uint64(jcr->previous_jr.JobTDate, ec1),
928          edit_uint64(mig_jcr->jr.JobId, ec2));
929       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
930
931       /* Now marke the previous job as migrated */
932       Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
933            (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
934       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
935
936       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
937          Jmsg(jcr, M_WARNING, 0, _("Error getting job record for stats: %s"),
938             db_strerror(jcr->db));
939          set_jcr_job_status(jcr, JS_ErrorTerminated);
940       }
941
942       bstrncpy(mr.VolumeName, jcr->VolumeName, sizeof(mr.VolumeName));
943       if (!db_get_media_record(jcr, jcr->db, &mr)) {
944          Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
945             mr.VolumeName, db_strerror(jcr->db));
946          set_jcr_job_status(jcr, JS_ErrorTerminated);
947       }
948
949       update_bootstrap_file(mig_jcr);
950
951       if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
952          /*
953           * Note, if the job has erred, most likely it did not write any
954           *  tape, so suppress this "error" message since in that case
955           *  it is normal.  Or look at it the other way, only for a
956           *  normal exit should we complain about this error.
957           */
958          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
959             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
960          }
961          mig_jcr->VolumeName[0] = 0;         /* none */
962       }
963   }
964
965    msg_type = M_INFO;                 /* by default INFO message */
966    switch (jcr->JobStatus) {
967    case JS_Terminated:
968       if (jcr->Errors || jcr->SDErrors) {
969          term_msg = _("%s OK -- with warnings");
970       } else {
971          term_msg = _("%s OK");
972       }
973       break;
974    case JS_FatalError:
975    case JS_ErrorTerminated:
976       term_msg = _("*** %s Error ***");
977       msg_type = M_ERROR;          /* Generate error message */
978       if (jcr->store_bsock) {
979          bnet_sig(jcr->store_bsock, BNET_TERMINATE);
980          if (jcr->SD_msg_chan) {
981             pthread_cancel(jcr->SD_msg_chan);
982          }
983       }
984       break;
985    case JS_Canceled:
986       term_msg = _("%s Canceled");
987       if (jcr->store_bsock) {
988          bnet_sig(jcr->store_bsock, BNET_TERMINATE);
989          if (jcr->SD_msg_chan) {
990             pthread_cancel(jcr->SD_msg_chan);
991          }
992       }
993       break;
994    default:
995       term_msg = _("Inappropriate %s term code");
996       break;
997    }
998    bsnprintf(term_code, sizeof(term_code), term_msg, "Migration");
999    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
1000    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
1001    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
1002    if (RunTime <= 0) {
1003       kbps = 0;
1004    } else {
1005       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
1006    }
1007
1008
1009    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1010
1011    Jmsg(jcr, msg_type, 0, _("Bacula %s (%s): %s\n"
1012 "  Prev Backup JobId:      %s\n"
1013 "  New Backup JobId:       %s\n"
1014 "  Migration JobId:        %s\n"
1015 "  Migration Job:          %s\n"
1016 "  Backup Level:           %s%s\n"
1017 "  Client:                 %s\n"
1018 "  FileSet:                \"%s\" %s\n"
1019 "  Pool:                   \"%s\" (From %s)\n"
1020 "  Storage:                \"%s\" (From %s)\n"
1021 "  Start time:             %s\n"
1022 "  End time:               %s\n"
1023 "  Elapsed time:           %s\n"
1024 "  Priority:               %d\n"
1025 "  SD Files Written:       %s\n"
1026 "  SD Bytes Written:       %s (%sB)\n"
1027 "  Rate:                   %.1f KB/s\n"
1028 "  Volume name(s):         %s\n"
1029 "  Volume Session Id:      %d\n"
1030 "  Volume Session Time:    %d\n"
1031 "  Last Volume Bytes:      %s (%sB)\n"
1032 "  SD Errors:              %d\n"
1033 "  SD termination status:  %s\n"
1034 "  Termination:            %s\n\n"),
1035    VERSION,
1036    LSMDATE,
1037         edt, 
1038         mig_jcr ? edit_uint64(jcr->previous_jr.JobId, ec6) : "0", 
1039         mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
1040         edit_uint64(jcr->jr.JobId, ec8),
1041         jcr->jr.Job,
1042         level_to_str(jcr->JobLevel), jcr->since,
1043         jcr->client->name(),
1044         jcr->fileset->name(), jcr->FSCreateTime,
1045         jcr->pool->name(), jcr->pool_source,
1046         jcr->wstore->name(), jcr->storage_source,
1047         sdt,
1048         edt,
1049         edit_utime(RunTime, elapsed, sizeof(elapsed)),
1050         jcr->JobPriority,
1051         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1052         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1053         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1054         (float)kbps,
1055         mig_jcr ? mig_jcr->VolumeName : "",
1056         jcr->VolSessionId,
1057         jcr->VolSessionTime,
1058         edit_uint64_with_commas(mr.VolBytes, ec4),
1059         edit_uint64_with_suffix(mr.VolBytes, ec5),
1060         jcr->SDErrors,
1061         sd_term_msg,
1062         term_code);
1063
1064    Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
1065    if (jcr->mig_jcr) {
1066       free_jcr(jcr->mig_jcr);
1067       jcr->mig_jcr = NULL;
1068    }
1069    Dmsg0(100, "Leave migrate_cleanup()\n");
1070 }
1071
1072 /* 
1073  * Return next DBId from comma separated list   
1074  *
1075  * Returns:
1076  *   1 if next DBId returned
1077  *   0 if no more DBIds are in list
1078  *  -1 there is an error
1079  */
1080 static int get_next_dbid_from_list(char **p, DBId_t *DBId)
1081 {
1082    char id[30];
1083    char *q = *p;
1084
1085    id[0] = 0;
1086    for (int i=0; i<(int)sizeof(id); i++) {
1087       if (*q == 0) {
1088          break;
1089       } else if (*q == ',') {
1090          q++;
1091          break;
1092       }
1093       id[i] = *q++;
1094       id[i+1] = 0;
1095    }
1096    if (id[0] == 0) {
1097       return 0;
1098    } else if (!is_a_number(id)) {
1099       return -1;                      /* error */
1100    }
1101    *p = q;
1102    *DBId = str_to_int64(id);
1103    return 1;
1104 }