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