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