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