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