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