]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
b8fb64d89e55e56d104c7d8cf875b2895d5bf285
[bacula/bacula] / bacula / src / dird / migrate.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- migrate.c -- responsible for doing
31  *     migration and copy jobs.
32  * 
33  *   Also handles Copy jobs (March MMVIII)
34  *
35  *     Kern Sibbald, September MMIV
36  *
37  *  Basic tasks done here:
38  *     Open DB and create records for this job.
39  *     Open Message Channel with Storage daemon to tell him a job will be starting.
40  *     Open connection with Storage daemon and pass him commands
41  *       to do the backup.
42  *     When the Storage daemon finishes the job, update the DB.
43  *
44  *   Version $Id$
45  */
46
47 #include "bacula.h"
48 #include "dird.h"
49 #include "ua.h"
50 #ifndef HAVE_REGEX_H
51 #include "lib/bregex.h"
52 #else
53 #include <regex.h>
54 #endif
55
56 static const int dbglevel = 10;
57
58 static int get_job_to_migrate(JCR *jcr);
59 struct idpkt;
60 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
61                  const char *query2, const char *type);
62 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
63                  const char *type);
64 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type);
65 static bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids);
66 static void start_migration_job(JCR *jcr);
67 static int get_next_dbid_from_list(char **p, DBId_t *DBId);
68 static bool set_migration_next_pool(JCR *jcr, POOL **pool);
69
70 /* 
71  * Called here before the job is run to do the job
72  *   specific setup.  Note, one of the important things to
73  *   complete in this init code is to make the definitive
74  *   choice of input and output storage devices.  This is
75  *   because immediately after the init, the job is queued
76  *   in the jobq.c code, and it checks that all the resources
77  *   (storage resources in particular) are available, so these
78  *   must all be properly defined.
79  *
80  *  previous_jr refers to the job DB record of the Job that is
81  *    going to be migrated.
82  *  prev_job refers to the job resource of the Job that is
83  *    going to be migrated.
84  *  jcr is the jcr for the current "migration" job.  It is a
85  *    control job that is put in the DB as a migration job, which
86  *    means that this job migrated a previous job to a new job.
87  *    No Volume or File data is associated with this control
88  *    job.
89  *  mig_jcr refers to the newly migrated job that is run by
90  *    the current jcr.  It is a backup job that moves (migrates) the
91  *    data written for the previous_jr into the new pool.  This
92  *    job (mig_jcr) becomes the new backup job that replaces
93  *    the original backup job. Note, this jcr is not really run. It
94  *    is simply attached to the current jcr.  It will show up in
95  *    the Director's status output, but not in the SD or FD, both of
96  *    which deal only with the current migration job (i.e. jcr).
97  */
98 bool do_migration_init(JCR *jcr)
99 {
100    POOL *pool = NULL;
101    JOB *job, *prev_job;
102    JCR *mig_jcr;                   /* newly migrated job */
103    int count;
104
105
106    apply_pool_overrides(jcr);
107
108    if (!allow_duplicate_job(jcr)) {
109       return false;
110    }
111
112    jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
113    if (jcr->jr.PoolId == 0) {
114       Dmsg1(dbglevel, "JobId=%d no PoolId\n", (int)jcr->JobId);
115       Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
116       return false;
117    }
118    /*
119     * Note, at this point, pool is the pool for this job.  We
120     *  transfer it to rpool (read pool), and a bit later,
121     *  pool will be changed to point to the write pool, 
122     *  which comes from pool->NextPool.
123     */
124    jcr->rpool = jcr->pool;            /* save read pool */
125    pm_strcpy(jcr->rpool_source, jcr->pool_source);
126
127
128    Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source);
129
130    if (!get_or_create_fileset_record(jcr)) {
131       Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId);
132       Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
133       return false;
134    }
135
136    /* If we find a job or jobs to migrate it is previous_jr.JobId */
137    count = get_job_to_migrate(jcr);
138    if (count < 0) {
139       return false;
140    }
141    if (count == 0) {
142       set_migration_next_pool(jcr, &pool);
143       return true;                    /* no work */
144    }
145
146    Dmsg1(dbglevel, "Back from get_job_to_migrate JobId=%d\n", (int)jcr->JobId);
147
148    if (jcr->previous_jr.JobId == 0) {
149       Dmsg1(dbglevel, "JobId=%d no previous JobId\n", (int)jcr->JobId);
150       Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName(0));
151       set_migration_next_pool(jcr, &pool);
152       return true;                    /* no work */
153    }
154
155    create_restore_bootstrap_file(jcr);
156
157    if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
158       set_jcr_job_status(jcr, JS_Terminated);
159       Dmsg1(dbglevel, "JobId=%d expected files == 0\n", (int)jcr->JobId);
160       if (jcr->previous_jr.JobId == 0) {
161          Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName(0));
162       } else {
163          Jmsg(jcr, M_INFO, 0, _("Previous Job has no data to %s.\n"), jcr->get_ActionName(0));
164       }
165       set_migration_next_pool(jcr, &pool);
166       return true;                    /* no work */
167    }
168
169
170    Dmsg5(dbglevel, "JobId=%d: Current: Name=%s JobId=%d Type=%c Level=%c\n",
171       (int)jcr->JobId,
172       jcr->jr.Name, (int)jcr->jr.JobId, 
173       jcr->jr.JobType, jcr->jr.JobLevel);
174
175    LockRes();
176    job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
177    prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
178    UnlockRes();
179    if (!job) {
180       Jmsg(jcr, M_FATAL, 0, _("Job resource not found for \"%s\".\n"), jcr->jr.Name);
181       return false;
182    }
183    if (!prev_job) {
184       Jmsg(jcr, M_FATAL, 0, _("Previous Job resource not found for \"%s\".\n"), 
185            jcr->previous_jr.Name);
186       return false;
187    }
188
189    jcr->spool_data = job->spool_data;     /* turn on spooling if requested in job */ 
190
191    /* Create a migration jcr */
192    mig_jcr = jcr->mig_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
193    memcpy(&mig_jcr->previous_jr, &jcr->previous_jr, sizeof(mig_jcr->previous_jr));
194
195    /*
196     * Turn the mig_jcr into a "real" job that takes on the aspects of
197     *   the previous backup job "prev_job".
198     */
199    set_jcr_defaults(mig_jcr, prev_job);
200    if (!setup_job(mig_jcr)) {
201       Jmsg(jcr, M_FATAL, 0, _("setup job failed.\n"));
202       return false;
203    }
204
205    /* Now reset the job record from the previous job */
206    memcpy(&mig_jcr->jr, &jcr->previous_jr, sizeof(mig_jcr->jr));
207    /* Update the jr to reflect the new values of PoolId and JobId. */
208    mig_jcr->jr.PoolId = jcr->jr.PoolId;
209    mig_jcr->jr.JobId = mig_jcr->JobId;
210
211    /* Don't let WatchDog checks Max*Time value on this Job */
212    mig_jcr->no_maxtime = true;
213
214    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
215       mig_jcr->jr.Name, (int)mig_jcr->jr.JobId, 
216       mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
217
218    if (set_migration_next_pool(jcr, &pool)) {
219       /* If pool storage specified, use it for restore */
220       copy_rstorage(mig_jcr, pool->storage, _("Pool resource"));
221       copy_rstorage(jcr, pool->storage, _("Pool resource"));
222
223       mig_jcr->pool = jcr->pool;
224       mig_jcr->jr.PoolId = jcr->jr.PoolId;
225    }
226
227    return true;
228 }
229
230
231 /*
232  * set_migration_next_pool() called by do_migration_init()
233  * at differents stages.
234  * The  idea here is tofactorize the NextPool's search code and
235  * to permit do_migration_init() to return with NextPool set in jcr struct.
236  */
237 static bool set_migration_next_pool(JCR *jcr, POOL **retpool)
238 {
239    POOL_DBR pr;
240    POOL *pool;
241    char ed1[100];
242
243    /*
244     * Get the PoolId used with the original job. Then
245     *  find the pool name from the database record.
246     */
247    memset(&pr, 0, sizeof(pr));
248    pr.PoolId = jcr->jr.PoolId;
249    if (!db_get_pool_record(jcr, jcr->db, &pr)) {
250       Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
251             edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
252          return false;
253    }
254    /* Get the pool resource corresponding to the original job */
255    pool = (POOL *)GetResWithName(R_POOL, pr.Name);
256    *retpool = pool;
257    if (!pool) {
258       Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
259       return false;
260    }
261
262    /*
263     * If the original backup pool has a NextPool, make sure a 
264     *  record exists in the database. Note, in this case, we
265     *  will be migrating from pool to pool->NextPool.
266     */
267    if (pool->NextPool) {
268       jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->name());
269       if (jcr->jr.PoolId == 0) {
270          return false;
271       }
272    }
273    if (!set_migration_wstorage(jcr, pool)) {
274       return false;
275    }
276    jcr->pool = pool->NextPool;
277    pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource"));
278
279    Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->pool->name(), jcr->rpool->name());
280
281    return true;
282 }
283
284
285 /*
286  * Do a Migration of a previous job
287  *
288  *  Returns:  false on failure
289  *            true  on success
290  */
291 bool do_migration(JCR *jcr)
292 {
293    char ed1[100];
294    BSOCK *sd;
295    JCR *mig_jcr = jcr->mig_jcr;    /* newly migrated job */
296
297    /*
298     * If mig_jcr is NULL, there is nothing to do for this job,
299     *  so set a normal status, cleanup and return OK.
300     */
301    if (!mig_jcr) {
302       set_jcr_job_status(jcr, JS_Terminated);
303       migration_cleanup(jcr, jcr->JobStatus);
304       return true;
305    }
306
307    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
308       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to %s. ERR=%s"),
309            edit_int64(jcr->previous_jr.JobId, ed1),
310            jcr->get_ActionName(0),
311            db_strerror(jcr->db));
312       set_jcr_job_status(jcr, JS_Terminated);
313       migration_cleanup(jcr, jcr->JobStatus);
314       return true;
315    }
316    /* Make sure this job was not already migrated */
317    if (jcr->previous_jr.JobType != JT_BACKUP) {
318       Jmsg(jcr, M_INFO, 0, _("JobId %s already %s probably by another Job. %s stopped.\n"),
319          edit_int64(jcr->previous_jr.JobId, ed1),
320          jcr->get_ActionName(1),
321          jcr->get_OperationName());
322       set_jcr_job_status(jcr, JS_Terminated);
323       migration_cleanup(jcr, jcr->JobStatus);
324       return true;
325    }
326
327    /* Print Job Start message */
328    Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
329         jcr->get_OperationName(), edit_uint64(jcr->JobId, ed1), jcr->Job);
330
331    /*
332     * Open a message channel connection with the Storage
333     * daemon. This is to let him know that our client
334     * will be contacting him for a backup  session.
335     *
336     */
337    Dmsg0(110, "Open connection with storage daemon\n");
338    set_jcr_job_status(jcr, JS_WaitSD);
339    set_jcr_job_status(mig_jcr, JS_WaitSD);
340    /*
341     * Start conversation with Storage daemon
342     */
343    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
344       return false;
345    }
346    sd = jcr->store_bsock;
347    /*
348     * Now start a job with the Storage daemon
349     */
350    Dmsg2(dbglevel, "Read store=%s, write store=%s\n", 
351       ((STORE *)jcr->rstorage->first())->name(),
352       ((STORE *)jcr->wstorage->first())->name());
353    if (((STORE *)jcr->rstorage->first())->name() == ((STORE *)jcr->wstorage->first())->name()) {
354       Jmsg(jcr, M_FATAL, 0, _("Read storage \"%s\" same as write storage.\n"),
355            ((STORE *)jcr->rstorage->first())->name());
356       return false;
357    }
358    if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage, /*send_bsr*/true)) {
359       return false;
360    }
361    Dmsg0(150, "Storage daemon connection OK\n");
362
363
364    /*    
365     * We re-update the job start record so that the start
366     *  time is set after the run before job.  This avoids 
367     *  that any files created by the run before job will
368     *  be saved twice.  They will be backed up in the current
369     *  job, but not in the next one unless they are changed.
370     *  Without this, they will be backed up in this job and
371     *  in the next job run because in that case, their date 
372     *   is after the start of this run.
373     */
374    jcr->start_time = time(NULL);
375    jcr->jr.StartTime = jcr->start_time;
376    jcr->jr.JobTDate = jcr->start_time;
377    set_jcr_job_status(jcr, JS_Running);
378
379    /* Update job start record for this migration control job */
380    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
381       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
382       return false;
383    }
384
385
386    mig_jcr->start_time = time(NULL);
387    mig_jcr->jr.StartTime = mig_jcr->start_time;
388    mig_jcr->jr.JobTDate = mig_jcr->start_time;
389    set_jcr_job_status(mig_jcr, JS_Running);
390
391    /* Update job start record for the real migration backup job */
392    if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
393       Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(mig_jcr->db));
394       return false;
395    }
396
397    Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
398       mig_jcr->jr.Name, (int)mig_jcr->jr.JobId, 
399       mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
400
401
402    /*
403     * Start the job prior to starting the message thread below
404     * to avoid two threads from using the BSOCK structure at
405     * the same time.
406     */
407    if (!sd->fsend("run")) {
408       return false;
409    }
410
411    /*
412     * Now start a Storage daemon message thread
413     */
414    if (!start_storage_daemon_message_thread(jcr)) {
415       return false;
416    }
417
418
419    set_jcr_job_status(jcr, JS_Running);
420    set_jcr_job_status(mig_jcr, JS_Running);
421
422    /* Pickup Job termination data */
423    /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
424    wait_for_storage_daemon_termination(jcr);
425    set_jcr_job_status(jcr, jcr->SDJobStatus);
426    db_write_batch_file_records(jcr);    /* used by bulk batch file insert */
427    if (jcr->JobStatus != JS_Terminated) {
428       return false;
429    }
430
431    migration_cleanup(jcr, jcr->JobStatus);
432
433    return true;
434 }
435
436 struct idpkt {
437    POOLMEM *list;
438    uint32_t count;
439 };
440
441 /* Add an item to the list if it is unique */
442 static void add_unique_id(idpkt *ids, char *item) 
443 {
444    const int maxlen = 30;
445    char id[maxlen+1];
446    char *q = ids->list;
447
448    /* Walk through current list to see if each item is the same as item */
449    for ( ; *q; ) {
450        id[0] = 0;
451        for (int i=0; i<maxlen; i++) {
452           if (*q == 0) {
453              break;
454           } else if (*q == ',') {
455              q++;
456              break;
457           }
458           id[i] = *q++;
459           id[i+1] = 0;
460        }
461        if (strcmp(item, id) == 0) {
462           return;
463        }
464    }
465    /* Did not find item, so add it to list */
466    if (ids->count == 0) {
467       ids->list[0] = 0;
468    } else {
469       pm_strcat(ids->list, ",");
470    }
471    pm_strcat(ids->list, item);
472    ids->count++;
473 // Dmsg3(0, "add_uniq count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
474    return;
475 }
476
477 /*
478  * Callback handler make list of DB Ids
479  */
480 static int unique_dbid_handler(void *ctx, int num_fields, char **row)
481 {
482    idpkt *ids = (idpkt *)ctx;
483
484    /* Sanity check */
485    if (!row || !row[0]) {
486       Dmsg0(dbglevel, "dbid_hdlr error empty row\n");
487       return 1;              /* stop calling us */
488    }
489
490    add_unique_id(ids, row[0]);
491    Dmsg3(dbglevel, "dbid_hdlr count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
492    return 0;
493 }
494
495
496 struct uitem {
497    dlink link;   
498    char *item;
499 };
500
501 static int item_compare(void *item1, void *item2)
502 {
503    uitem *i1 = (uitem *)item1;
504    uitem *i2 = (uitem *)item2;
505    return strcmp(i1->item, i2->item);
506 }
507
508 static int unique_name_handler(void *ctx, int num_fields, char **row)
509 {
510    dlist *list = (dlist *)ctx;
511
512    uitem *new_item = (uitem *)malloc(sizeof(uitem));
513    uitem *item;
514    
515    memset(new_item, 0, sizeof(uitem));
516    new_item->item = bstrdup(row[0]);
517    Dmsg1(dbglevel, "Unique_name_hdlr Item=%s\n", row[0]);
518    item = (uitem *)list->binary_insert((void *)new_item, item_compare);
519    if (item != new_item) {            /* already in list */
520       free(new_item->item);
521       free((char *)new_item);
522       return 0;
523    }
524    return 0;
525 }
526
527 /* Get Job names in Pool */
528 const char *sql_job =
529    "SELECT DISTINCT Job.Name from Job,Pool"
530    " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
531
532 /* Get JobIds from regex'ed Job names */
533 const char *sql_jobids_from_job =
534    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
535    " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
536    " ORDER by Job.StartTime";
537
538 /* Get Client names in Pool */
539 const char *sql_client =
540    "SELECT DISTINCT Client.Name from Client,Pool,Job"
541    " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
542    " Job.PoolId=Pool.PoolId";
543
544 /* Get JobIds from regex'ed Client names */
545 const char *sql_jobids_from_client =
546    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool,Client"
547    " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
548    " AND Job.ClientId=Client.ClientId AND Job.Type IN ('B','C')"
549    " AND Job.JobStatus IN ('T','W')"
550    " ORDER by Job.StartTime";
551
552 /* Get Volume names in Pool */
553 const char *sql_vol = 
554    "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
555    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
556    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
557
558 /* Get JobIds from regex'ed Volume names */
559 const char *sql_jobids_from_vol =
560    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
561    " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
562    " AND JobMedia.JobId=Job.JobId AND Job.Type IN ('B','C')"
563    " AND Job.JobStatus IN ('T','W') AND Media.Enabled=1"
564    " ORDER by Job.StartTime";
565
566 const char *sql_smallest_vol = 
567    "SELECT Media.MediaId FROM Media,Pool,JobMedia WHERE"
568    " Media.MediaId in (SELECT DISTINCT MediaId from JobMedia) AND"
569    " Media.VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
570    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
571    " ORDER BY VolBytes ASC LIMIT 1";
572
573 const char *sql_oldest_vol = 
574    "SELECT Media.MediaId FROM Media,Pool,JobMedia WHERE"
575    " Media.MediaId in (SELECT DISTINCT MediaId from JobMedia) AND"
576    " Media.VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
577    " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
578    " ORDER BY LastWritten ASC LIMIT 1";
579
580 /* Get JobIds when we have selected MediaId */
581 const char *sql_jobids_from_mediaid =
582    "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
583    " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId IN (%s)"
584    " AND Job.Type IN ('B','C') AND Job.JobStatus IN ('T','W')"
585    " ORDER by Job.StartTime";
586
587 /* Get the number of bytes in the pool */
588 const char *sql_pool_bytes =
589    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN"
590    " (SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE"
591    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
592    " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
593    " Job.Type IN ('B','C') AND Job.JobStatus IN ('T','W') AND"
594    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId)";
595
596 /* Get the number of bytes in the Jobs */
597 const char *sql_job_bytes =
598    "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)";
599
600 /* Get Media Ids in Pool */
601 const char *sql_mediaids =
602    "SELECT MediaId FROM Media,Pool WHERE"
603    " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
604    " Media.PoolId=Pool.PoolId AND Pool.Name='%s' ORDER BY LastWritten ASC";
605
606 /* Get JobIds in Pool longer than specified time */
607 const char *sql_pool_time = 
608    "SELECT DISTINCT Job.JobId FROM Pool,Job,Media,JobMedia WHERE"
609    " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
610    " VolStatus IN ('Full','Used','Error') AND Media.Enabled=1 AND"
611    " Job.Type IN ('B','C') AND Job.JobStatus IN ('T','W') AND"
612    " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"
613    " AND Job.RealEndTime<='%s'";
614
615 /* Get JobIds from successfully completed backup jobs which have not been copied before */
616 const char *sql_jobids_of_pool_uncopied_jobs =
617    "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
618    " WHERE Pool.Name = '%s' AND Pool.PoolId = Job.PoolId"
619    " AND Job.Type = 'B' AND Job.JobStatus IN ('T','W')"
620    " AND Job.jobBytes > 0"
621    " AND Job.JobId NOT IN"
622    " (SELECT PriorJobId FROM Job WHERE"
623    " Type IN ('B','C') AND Job.JobStatus IN ('T','W')"
624    " AND PriorJobId != 0)"
625    " ORDER by Job.StartTime";
626
627 /*
628 * const char *sql_ujobid =
629 *   "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
630 *   " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
631 *   " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
632 */
633
634 /*
635  *
636  * This is the central piece of code that finds a job or jobs 
637  *   actually JobIds to migrate.  It first looks to see if one
638  *   has been "manually" specified in jcr->MigrateJobId, and if
639  *   so, it returns that JobId to be run.  Otherwise, it
640  *   examines the Selection Type to see what kind of migration
641  *   we are doing (Volume, Job, Client, ...) and applies any
642  *   Selection Pattern if appropriate to obtain a list of JobIds.
643  *   Finally, it will loop over all the JobIds found, except the last
644  *   one starting a new job with MigrationJobId set to that JobId, and
645  *   finally, it returns the last JobId to the caller.
646  *
647  * Returns: -1  on error
648  *           0  if no jobs to migrate
649  *           1  if OK and jcr->previous_jr filled in
650  */
651 static int get_job_to_migrate(JCR *jcr)
652 {
653    char ed1[30], ed2[30];
654    POOL_MEM query(PM_MESSAGE);
655    JobId_t JobId;
656    DBId_t DBId = 0;
657    int stat;
658    char *p;
659    idpkt ids, mid, jids;
660    db_int64_ctx ctx;
661    int64_t pool_bytes;
662    time_t ttime;
663    struct tm tm;
664    char dt[MAX_TIME_LENGTH];
665    int count = 0;
666
667    ids.list = get_pool_memory(PM_MESSAGE);
668    ids.list[0] = 0;
669    ids.count = 0;
670    mid.list = get_pool_memory(PM_MESSAGE);
671    mid.list[0] = 0;
672    mid.count = 0;
673    jids.list = get_pool_memory(PM_MESSAGE);
674    jids.list[0] = 0;
675    jids.count = 0;
676
677
678    /*
679     * If MigrateJobId is set, then we migrate only that Job,
680     *  otherwise, we go through the full selection of jobs to
681     *  migrate.
682     */
683    if (jcr->MigrateJobId != 0) {
684       Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
685       edit_uint64(jcr->MigrateJobId, ids.list);
686       ids.count = 1;
687    } else {
688       switch (jcr->job->selection_type) {
689       case MT_JOB:
690          if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
691             goto bail_out;
692          } 
693          break;
694       case MT_CLIENT:
695          if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
696             goto bail_out;
697          } 
698          break;
699       case MT_VOLUME:
700          if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
701             goto bail_out;
702          } 
703          break;
704       case MT_SQLQUERY:
705          if (!jcr->job->selection_pattern) {
706             Jmsg(jcr, M_FATAL, 0, _("No %s SQL selection pattern specified.\n"), jcr->get_OperationName());
707             goto bail_out;
708          }
709          Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
710          if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
711               unique_dbid_handler, (void *)&ids)) {
712             Jmsg(jcr, M_FATAL, 0,
713                  _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
714             goto bail_out;
715          }
716          break;
717       case MT_SMALLEST_VOL:
718          if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
719             goto bail_out;
720          }
721          break;
722       case MT_OLDEST_VOL:
723          if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
724             goto bail_out;
725          }
726          break;
727       case MT_POOL_OCCUPANCY:
728          ctx.count = 0;
729          /* Find count of bytes in pool */
730          Mmsg(query, sql_pool_bytes, jcr->rpool->name());
731          if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
732             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
733             goto bail_out;
734          }
735          if (ctx.count == 0) {
736             Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
737             goto ok_out;
738          }
739          pool_bytes = ctx.value;
740          Dmsg2(dbglevel, "highbytes=%lld pool=%lld\n", jcr->rpool->MigrationHighBytes,
741                pool_bytes);
742          if (pool_bytes < (int64_t)jcr->rpool->MigrationHighBytes) {
743             Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
744             goto ok_out;
745          }
746          Dmsg0(dbglevel, "We should do Occupation migration.\n");
747
748          ids.count = 0;
749          /* Find a list of MediaIds that could be migrated */
750          Mmsg(query, sql_mediaids, jcr->rpool->name());
751          Dmsg1(dbglevel, "query=%s\n", query.c_str());
752          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
753             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
754             goto bail_out;
755          }
756          if (ids.count == 0) {
757             Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
758             goto ok_out;
759          }
760          Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list);
761
762          if (!find_jobids_from_mediaid_list(jcr, &ids, "Volume")) {
763             goto bail_out;
764          }
765          /* ids == list of jobs  */
766          p = ids.list;
767          for (int i=0; i < (int)ids.count; i++) {
768             stat = get_next_dbid_from_list(&p, &DBId);
769             Dmsg2(dbglevel, "get_next_dbid stat=%d JobId=%u\n", stat, (uint32_t)DBId);
770             if (stat < 0) {
771                Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
772                goto bail_out;
773             } else if (stat == 0) {
774                break;
775             }
776
777             mid.count = 1;
778             Mmsg(mid.list, "%s", edit_int64(DBId, ed1));
779             if (jids.count > 0) {
780                pm_strcat(jids.list, ",");
781             }
782             pm_strcat(jids.list, mid.list);
783             jids.count += mid.count;
784
785             /* Find count of bytes from Jobs */
786             Mmsg(query, sql_job_bytes, mid.list);
787             Dmsg1(dbglevel, "Jobbytes query: %s\n", query.c_str());
788             if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
789                Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
790                goto bail_out;
791             }
792             pool_bytes -= ctx.value;
793             Dmsg2(dbglevel, "Total %s Job bytes=%s\n", jcr->get_ActionName(0), edit_int64_with_commas(ctx.value, ed1));
794             Dmsg2(dbglevel, "lowbytes=%s poolafter=%s\n", 
795                   edit_int64_with_commas(jcr->rpool->MigrationLowBytes, ed1),
796                   edit_int64_with_commas(pool_bytes, ed2));
797             if (pool_bytes <= (int64_t)jcr->rpool->MigrationLowBytes) {
798                Dmsg0(dbglevel, "We should be done.\n");
799                break;
800             }
801          }
802          /* Transfer jids to ids, where the jobs list is expected */
803          ids.count = jids.count;
804          pm_strcpy(ids.list, jids.list);
805          Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", ids.count, ids.list);
806          break;
807       case MT_POOL_TIME:
808          ttime = time(NULL) - (time_t)jcr->rpool->MigrationTime;
809          (void)localtime_r(&ttime, &tm);
810          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
811
812          ids.count = 0;
813          Mmsg(query, sql_pool_time, jcr->rpool->name(), dt);
814          Dmsg1(dbglevel, "query=%s\n", query.c_str());
815          if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
816             Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
817             goto bail_out;
818          }
819          if (ids.count == 0) {
820             Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
821             goto ok_out;
822          }
823          Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list);
824          break;
825       case MT_POOL_UNCOPIED_JOBS:
826          if (!find_jobids_of_pool_uncopied_jobs(jcr, &ids)) {
827             goto bail_out;
828          } 
829          break;
830       default:
831          Jmsg(jcr, M_FATAL, 0, _("Unknown %s Selection Type.\n"), jcr->get_OperationName());
832          goto bail_out;
833       }
834    }
835
836    /*
837     * Loop over all jobids except the last one, sending
838     *  them to start_migration_job(), which will start a job
839     *  for each of them.  For the last JobId, we handle it below.
840     */
841    p = ids.list;
842    if (ids.count == 0) {
843       Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
844       goto ok_out;
845    }
846
847    Jmsg(jcr, M_INFO, 0, _("The following %u JobId%s chosen to be %s: %s\n"),
848       ids.count, (ids.count < 2) ? _(" was") : _("s were"),
849       jcr->get_ActionName(1), ids.list);
850
851    Dmsg2(dbglevel, "Before loop count=%d ids=%s\n", ids.count, ids.list);
852    for (int i=1; i < (int)ids.count; i++) {
853       JobId = 0;
854       stat = get_next_jobid_from_list(&p, &JobId);
855       Dmsg3(dbglevel, "get_jobid_no=%d stat=%d JobId=%u\n", i, stat, JobId);
856       if (stat < 0) {
857          Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
858          goto bail_out;
859       } else if (stat == 0) {
860          Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
861          goto ok_out;
862       }
863       jcr->MigrateJobId = JobId;
864       start_migration_job(jcr);
865       Dmsg0(dbglevel, "Back from start_migration_job\n");
866    }
867    
868    /* Now get the last JobId and handle it in the current job */
869    JobId = 0;
870    stat = get_next_jobid_from_list(&p, &JobId);
871    Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, (int)JobId);
872    if (stat < 0) {
873       Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
874       goto bail_out;
875    } else if (stat == 0) {
876       Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
877       goto ok_out;
878    }
879
880    jcr->previous_jr.JobId = JobId;
881    Dmsg1(dbglevel, "Previous jobid=%d\n", (int)jcr->previous_jr.JobId);
882
883    if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
884       Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to %s. ERR=%s"),
885            edit_int64(jcr->previous_jr.JobId, ed1),
886            jcr->get_ActionName(0),
887            db_strerror(jcr->db));
888       goto bail_out;
889    }
890    Jmsg(jcr, M_INFO, 0, _("%s using JobId=%s Job=%s\n"),
891       jcr->get_OperationName(),
892       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
893    Dmsg4(dbglevel, "%s JobId=%d  using JobId=%s Job=%s\n",
894       jcr->get_OperationName(),
895       jcr->JobId,
896       edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
897    count = 1;
898
899 ok_out:
900    goto out;
901
902 bail_out:
903    count = -1;
904            
905 out:
906    free_pool_memory(ids.list);
907    free_pool_memory(mid.list);
908    free_pool_memory(jids.list);
909    return count;
910 }
911
912 static void start_migration_job(JCR *jcr)
913 {
914    UAContext *ua = new_ua_context(jcr);
915    char ed1[50];
916    ua->batch = true;
917    Mmsg(ua->cmd, "run job=\"%s\" jobid=%s", jcr->job->name(),
918         edit_uint64(jcr->MigrateJobId, ed1));
919    Dmsg2(dbglevel, "=============== %s cmd=%s\n", jcr->get_OperationName(), ua->cmd);
920    parse_ua_args(ua);                 /* parse command */
921    JobId_t jobid = run_cmd(ua, ua->cmd);
922    if (jobid == 0) {
923       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
924    } else {
925       Jmsg(jcr, M_INFO, 0, _("%s JobId %d started.\n"), jcr->get_OperationName(), (int)jobid);
926    }
927    free_ua_context(ua);
928 }
929
930 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
931                  const char *type) 
932 {
933    bool ok = false;
934    POOL_MEM query(PM_MESSAGE);
935
936    ids->count = 0;
937    /* Basic query for MediaId */
938    Mmsg(query, query1, jcr->rpool->name());
939    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
940       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
941       goto bail_out;
942    }
943    if (ids->count == 0) {
944       Jmsg(jcr, M_INFO, 0, _("No %s found to %s.\n"), type, jcr->get_ActionName(0));
945       ok = true;         /* Not an error */
946       goto bail_out;
947    } else if (ids->count != 1) {
948       Jmsg(jcr, M_FATAL, 0, _("SQL error. Expected 1 MediaId got %d\n"), ids->count);
949       goto bail_out;
950    }
951    Dmsg2(dbglevel, "%s MediaIds=%s\n", type, ids->list);
952
953    ok = find_jobids_from_mediaid_list(jcr, ids, type);
954
955 bail_out:
956    return ok;
957 }
958
959 /* 
960  * This routine returns:
961  *    false       if an error occurred
962  *    true        otherwise
963  *    ids.count   number of jobids found (may be zero)
964  */       
965 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type) 
966 {
967    bool ok = false;
968    POOL_MEM query(PM_MESSAGE);
969
970    Mmsg(query, sql_jobids_from_mediaid, ids->list);
971    ids->count = 0;
972    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
973       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
974       goto bail_out;
975    }
976    if (ids->count == 0) {
977       Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0));
978    }
979    ok = true;
980
981 bail_out:
982    return ok;
983 }
984
985 /* 
986  * This routine returns:
987  *    false       if an error occurred
988  *    true        otherwise
989  *    ids.count   number of jobids found (may be zero)
990  */       
991 static bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids) 
992 {
993    bool ok = false;
994    POOL_MEM query(PM_MESSAGE);
995
996    /* Only a copy job is allowed */
997    if (jcr->get_JobType() != JT_COPY) {
998       Jmsg(jcr, M_FATAL, 0,
999            _("Selection Type 'pooluncopiedjobs' only applies to Copy Jobs"));
1000       goto bail_out;
1001    }
1002
1003    Dmsg1(dbglevel, "copy selection pattern=%s\n", jcr->rpool->name());
1004    Mmsg(query, sql_jobids_of_pool_uncopied_jobs, jcr->rpool->name());
1005    Dmsg1(dbglevel, "get uncopied jobs query=%s\n", query.c_str());
1006    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
1007       Jmsg(jcr, M_FATAL, 0,
1008            _("SQL to get uncopied jobs failed. ERR=%s\n"), db_strerror(jcr->db));
1009       goto bail_out;
1010    }
1011    ok = true;
1012
1013 bail_out:
1014    return ok;
1015 }
1016
1017 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
1018                  const char *query2, const char *type) 
1019 {
1020    dlist *item_chain;
1021    uitem *item = NULL;
1022    uitem *last_item = NULL;
1023    regex_t preg;
1024    char prbuf[500];
1025    int rc;
1026    bool ok = false;
1027    POOL_MEM query(PM_MESSAGE);
1028
1029    item_chain = New(dlist(item, &item->link));
1030    if (!jcr->job->selection_pattern) {
1031       Jmsg(jcr, M_FATAL, 0, _("No %s %s selection pattern specified.\n"),
1032          jcr->get_OperationName(), type);
1033       goto bail_out;
1034    }
1035    Dmsg1(dbglevel, "regex-sel-pattern=%s\n", jcr->job->selection_pattern);
1036    /* Basic query for names */
1037    Mmsg(query, query1, jcr->rpool->name());
1038    Dmsg1(dbglevel, "get name query1=%s\n", query.c_str());
1039    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
1040         (void *)item_chain)) {
1041       Jmsg(jcr, M_FATAL, 0,
1042            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
1043       goto bail_out;
1044    }
1045    Dmsg1(dbglevel, "query1 returned %d names\n", item_chain->size());
1046    if (item_chain->size() == 0) {
1047       Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to %s.\n"),
1048            jcr->rpool->name(), jcr->get_ActionName(0));
1049       ok = true;
1050       goto bail_out;               /* skip regex match */
1051    } else {
1052       /* Compile regex expression */
1053       rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
1054       if (rc != 0) {
1055          regerror(rc, &preg, prbuf, sizeof(prbuf));
1056          Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
1057               jcr->job->selection_pattern, prbuf);
1058          goto bail_out;
1059       }
1060       /* Now apply the regex to the names and remove any item not matched */
1061       foreach_dlist(item, item_chain) {
1062          const int nmatch = 30;
1063          regmatch_t pmatch[nmatch];
1064          if (last_item) {
1065             Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
1066             free(last_item->item);
1067             item_chain->remove(last_item);
1068          }
1069          Dmsg1(dbglevel, "get name Item=%s\n", item->item);
1070          rc = regexec(&preg, item->item, nmatch, pmatch,  0);
1071          if (rc == 0) {
1072             last_item = NULL;   /* keep this one */
1073          } else {   
1074             last_item = item;
1075          }
1076       }
1077       if (last_item) {
1078          free(last_item->item);
1079          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
1080          item_chain->remove(last_item);
1081       }
1082       regfree(&preg);
1083    }
1084    if (item_chain->size() == 0) {
1085       Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to %s.\n"), jcr->get_ActionName(0));
1086       ok = true;
1087       goto bail_out;               /* skip regex match */
1088    }
1089
1090    /* 
1091     * At this point, we have a list of items in item_chain
1092     *  that have been matched by the regex, so now we need
1093     *  to look up their jobids.
1094     */
1095    ids->count = 0;
1096    foreach_dlist(item, item_chain) {
1097       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
1098       Mmsg(query, query2, item->item, jcr->rpool->name());
1099       Dmsg1(dbglevel, "get id from name query2=%s\n", query.c_str());
1100       if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
1101          Jmsg(jcr, M_FATAL, 0,
1102               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
1103          goto bail_out;
1104       }
1105    }
1106    if (ids->count == 0) {
1107       Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0));
1108    }
1109    ok = true;
1110
1111 bail_out:
1112    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
1113    foreach_dlist(item, item_chain) {
1114       free(item->item);
1115    }
1116    delete item_chain;
1117    return ok;
1118 }
1119
1120 /*
1121  * Release resources allocated during backup.
1122  */
1123 void migration_cleanup(JCR *jcr, int TermCode)
1124 {
1125    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
1126    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
1127    char ec6[50], ec7[50], ec8[50];
1128    char term_code[100], sd_term_msg[100];
1129    const char *term_msg;
1130    int msg_type = M_INFO;
1131    MEDIA_DBR mr;
1132    double kbps;
1133    utime_t RunTime;
1134    JCR *mig_jcr = jcr->mig_jcr;
1135    POOL_MEM query(PM_MESSAGE);
1136
1137    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
1138    update_job_end(jcr, TermCode);
1139    memset(&mr, 0, sizeof(mr));
1140
1141    /* 
1142     * Check if we actually did something.  
1143     *  mig_jcr is jcr of the newly migrated job.
1144     */
1145    if (mig_jcr) {
1146       char old_jobid[50], new_jobid[50];
1147
1148       edit_uint64(jcr->previous_jr.JobId, old_jobid);
1149       edit_uint64(mig_jcr->jr.JobId, new_jobid);
1150
1151       mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
1152       mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
1153       mig_jcr->VolSessionId = jcr->VolSessionId;
1154       mig_jcr->VolSessionTime = jcr->VolSessionTime;
1155       mig_jcr->jr.RealEndTime = 0; 
1156       mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
1157
1158       update_job_end(mig_jcr, TermCode);
1159      
1160       /* Update final items to set them to the previous job's values */
1161       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
1162                   "JobTDate=%s WHERE JobId=%s", 
1163          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
1164          edit_uint64(jcr->previous_jr.JobTDate, ec1),
1165          new_jobid);
1166       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1167
1168       /*
1169        * If we terminated a migration normally:
1170        *   - mark the previous job as migrated
1171        *   - move any Log records to the new JobId
1172        *   - Purge the File records from the previous job
1173        */
1174       if (jcr->get_JobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) {
1175          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1176               (char)JT_MIGRATED_JOB, old_jobid);
1177          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1178          UAContext *ua = new_ua_context(jcr);
1179          /* Move JobLog to new JobId */
1180          Mmsg(query, "UPDATE Log SET JobId=%s WHERE JobId=%s",
1181            new_jobid, old_jobid);
1182          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1183          /* Purge all old file records, but leave Job record */
1184          purge_files_from_jobs(ua, old_jobid);
1185          free_ua_context(ua);
1186       } 
1187
1188       /*
1189        * If we terminated a copy normally:
1190        *   - copy any Log records to the new JobId
1191        *   - set type="Job Copy" for the new job
1192        */
1193       if (jcr->get_JobType() == JT_COPY && jcr->JobStatus == JS_Terminated) {
1194          /* Copy JobLog to new JobId */
1195          Mmsg(query, "INSERT INTO Log (JobId, Time, LogText ) " 
1196                       "SELECT %s, Time, LogText FROM Log WHERE JobId=%s",
1197               new_jobid, old_jobid);
1198          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1199          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1200               (char)JT_JOB_COPY, new_jobid);
1201          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1202       } 
1203
1204       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
1205          Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
1206             db_strerror(jcr->db));
1207          set_jcr_job_status(jcr, JS_ErrorTerminated);
1208       }
1209
1210       update_bootstrap_file(mig_jcr);
1211
1212       if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
1213          /*
1214           * Note, if the job has failed, most likely it did not write any
1215           *  tape, so suppress this "error" message since in that case
1216           *  it is normal.  Or look at it the other way, only for a
1217           *  normal exit should we complain about this error.
1218           */
1219          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
1220             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
1221          }
1222          mig_jcr->VolumeName[0] = 0;         /* none */
1223       }
1224
1225       if (mig_jcr->VolumeName[0]) {
1226          /* Find last volume name. Multiple vols are separated by | */
1227          char *p = strrchr(mig_jcr->VolumeName, '|');
1228          if (p) {
1229             p++;                         /* skip | */
1230          } else {
1231             p = mig_jcr->VolumeName;     /* no |, take full name */
1232          }
1233          bstrncpy(mr.VolumeName, p, sizeof(mr.VolumeName));
1234          if (!db_get_media_record(jcr, jcr->db, &mr)) {
1235             Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
1236                mr.VolumeName, db_strerror(jcr->db));
1237          }
1238       }
1239
1240       switch (jcr->JobStatus) {
1241       case JS_Terminated:
1242          if (jcr->JobErrors || jcr->SDErrors) {
1243             term_msg = _("%s OK -- with warnings");
1244          } else {
1245             term_msg = _("%s OK");
1246          }
1247          break;
1248       case JS_FatalError:
1249       case JS_ErrorTerminated:
1250          term_msg = _("*** %s Error ***");
1251          msg_type = M_ERROR;          /* Generate error message */
1252          if (jcr->store_bsock) {
1253             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1254             if (jcr->SD_msg_chan) {
1255                pthread_cancel(jcr->SD_msg_chan);
1256             }
1257          }
1258          break;
1259       case JS_Canceled:
1260          term_msg = _("%s Canceled");
1261          if (jcr->store_bsock) {
1262             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1263             if (jcr->SD_msg_chan) {
1264                pthread_cancel(jcr->SD_msg_chan);
1265             }
1266          }
1267          break;
1268       default:
1269          term_msg = _("Inappropriate %s term code");
1270          break;
1271       }
1272    } else {
1273       if (jcr->get_JobType() == JT_MIGRATE && jcr->previous_jr.JobId != 0) {
1274          /* Mark previous job as migrated */
1275          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1276               (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
1277          db_sql_query(jcr->db, query.c_str(), NULL, NULL);
1278       }
1279       term_msg = _("%s -- no files to %s");
1280    }
1281
1282    bsnprintf(term_code, sizeof(term_code), term_msg, jcr->get_OperationName(), jcr->get_ActionName(0));
1283    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
1284    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
1285    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
1286    if (RunTime <= 0) {
1287       kbps = 0;
1288    } else {
1289       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
1290    }
1291
1292    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1293
1294    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
1295 "  Build OS:               %s %s %s\n"
1296 "  Prev Backup JobId:      %s\n"
1297 "  Prev Backup Job:        %s\n"
1298 "  New Backup JobId:       %s\n"
1299 "  Current JobId:          %s\n"
1300 "  Current Job:            %s\n"
1301 "  Backup Level:           %s%s\n"
1302 "  Client:                 %s\n"
1303 "  FileSet:                \"%s\" %s\n"
1304 "  Read Pool:              \"%s\" (From %s)\n"
1305 "  Read Storage:           \"%s\" (From %s)\n"
1306 "  Write Pool:             \"%s\" (From %s)\n"
1307 "  Write Storage:          \"%s\" (From %s)\n"
1308 "  Catalog:                \"%s\" (From %s)\n"
1309 "  Start time:             %s\n"
1310 "  End time:               %s\n"
1311 "  Elapsed time:           %s\n"
1312 "  Priority:               %d\n"
1313 "  SD Files Written:       %s\n"
1314 "  SD Bytes Written:       %s (%sB)\n"
1315 "  Rate:                   %.1f KB/s\n"
1316 "  Volume name(s):         %s\n"
1317 "  Volume Session Id:      %d\n"
1318 "  Volume Session Time:    %d\n"
1319 "  Last Volume Bytes:      %s (%sB)\n"
1320 "  SD Errors:              %d\n"
1321 "  SD termination status:  %s\n"
1322 "  Termination:            %s\n\n"),
1323         BACULA, my_name, VERSION, LSMDATE, edt,
1324         HOST_OS, DISTNAME, DISTVER,
1325         edit_uint64(jcr->previous_jr.JobId, ec6),
1326         jcr->previous_jr.Job,
1327         mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
1328         edit_uint64(jcr->jr.JobId, ec8),
1329         jcr->jr.Job,
1330         level_to_str(jcr->get_JobLevel()), jcr->since,
1331         jcr->client->name(),
1332         jcr->fileset->name(), jcr->FSCreateTime,
1333         jcr->rpool->name(), jcr->rpool_source,
1334         jcr->rstore?jcr->rstore->name():"*None*", 
1335         NPRT(jcr->rstore_source), 
1336         jcr->pool->name(), jcr->pool_source,
1337         jcr->wstore?jcr->wstore->name():"*None*", 
1338         NPRT(jcr->wstore_source),
1339         jcr->catalog->name(), jcr->catalog_source,
1340         sdt,
1341         edt,
1342         edit_utime(RunTime, elapsed, sizeof(elapsed)),
1343         jcr->JobPriority,
1344         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1345         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1346         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1347         (float)kbps,
1348         mig_jcr ? mig_jcr->VolumeName : "",
1349         jcr->VolSessionId,
1350         jcr->VolSessionTime,
1351         edit_uint64_with_commas(mr.VolBytes, ec4),
1352         edit_uint64_with_suffix(mr.VolBytes, ec5),
1353         jcr->SDErrors,
1354         sd_term_msg,
1355         term_code);
1356
1357    Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
1358    if (jcr->mig_jcr) {
1359       free_jcr(jcr->mig_jcr);
1360       jcr->mig_jcr = NULL;
1361    }
1362    Dmsg0(100, "Leave migrate_cleanup()\n");
1363 }
1364
1365 /* 
1366  * Return next DBId from comma separated list   
1367  *
1368  * Returns:
1369  *   1 if next DBId returned
1370  *   0 if no more DBIds are in list
1371  *  -1 there is an error
1372  */
1373 static int get_next_dbid_from_list(char **p, DBId_t *DBId)
1374 {
1375    const int maxlen = 30;
1376    char id[maxlen+1];
1377    char *q = *p;
1378
1379    id[0] = 0;
1380    for (int i=0; i<maxlen; i++) {
1381       if (*q == 0) {
1382          break;
1383       } else if (*q == ',') {
1384          q++;
1385          break;
1386       }
1387       id[i] = *q++;
1388       id[i+1] = 0;
1389    }
1390    if (id[0] == 0) {
1391       return 0;
1392    } else if (!is_a_number(id)) {
1393       return -1;                      /* error */
1394    }
1395    *p = q;
1396    *DBId = str_to_int64(id);
1397    return 1;
1398 }
1399
1400 bool set_migration_wstorage(JCR *jcr, POOL *pool)
1401 {
1402    POOL *wpool = pool->NextPool;
1403
1404    if (!wpool) {
1405       Jmsg(jcr, M_FATAL, 0, _("No Next Pool specification found in Pool \"%s\".\n"),
1406          pool->hdr.name);
1407       return false;
1408    }
1409
1410    if (!wpool->storage || wpool->storage->size() == 0) {
1411       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Next Pool \"%s\".\n"),
1412          wpool->name());
1413       return false;
1414    }
1415
1416    /* If pool storage specified, use it instead of job storage for backup */
1417    copy_wstorage(jcr, wpool->storage, _("Storage from Pool's NextPool resource"));
1418    return true;
1419 }