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