]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/migrate.c
update 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", jcr->job->name(),
921         edit_uint64(jcr->MigrateJobId, ed1));
922    Dmsg2(dbglevel, "=============== %s cmd=%s\n", jcr->get_OperationName(), ua->cmd);
923    parse_ua_args(ua);                 /* parse command */
924    JobId_t jobid = run_cmd(ua, ua->cmd);
925    if (jobid == 0) {
926       Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
927    } else {
928       Jmsg(jcr, M_INFO, 0, _("%s JobId %d started.\n"), jcr->get_OperationName(), (int)jobid);
929    }
930    free_ua_context(ua);
931 }
932
933 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
934                  const char *type) 
935 {
936    bool ok = false;
937    POOL_MEM query(PM_MESSAGE);
938
939    ids->count = 0;
940    /* Basic query for MediaId */
941    Mmsg(query, query1, jcr->rpool->name());
942    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
943       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
944       goto bail_out;
945    }
946    if (ids->count == 0) {
947       Jmsg(jcr, M_INFO, 0, _("No %s found to %s.\n"), type, jcr->get_ActionName(0));
948       ok = true;         /* Not an error */
949       goto bail_out;
950    } else if (ids->count != 1) {
951       Jmsg(jcr, M_FATAL, 0, _("SQL error. Expected 1 MediaId got %d\n"), ids->count);
952       goto bail_out;
953    }
954    Dmsg2(dbglevel, "%s MediaIds=%s\n", type, ids->list);
955
956    ok = find_jobids_from_mediaid_list(jcr, ids, type);
957
958 bail_out:
959    return ok;
960 }
961
962 /* 
963  * This routine returns:
964  *    false       if an error occurred
965  *    true        otherwise
966  *    ids.count   number of jobids found (may be zero)
967  */       
968 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type) 
969 {
970    bool ok = false;
971    POOL_MEM query(PM_MESSAGE);
972
973    Mmsg(query, sql_jobids_from_mediaid, ids->list);
974    ids->count = 0;
975    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
976       Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
977       goto bail_out;
978    }
979    if (ids->count == 0) {
980       Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0));
981    }
982    ok = true;
983
984 bail_out:
985    return ok;
986 }
987
988 /* 
989  * This routine returns:
990  *    false       if an error occurred
991  *    true        otherwise
992  *    ids.count   number of jobids found (may be zero)
993  */       
994 static bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids) 
995 {
996    bool ok = false;
997    POOL_MEM query(PM_MESSAGE);
998
999    /* Only a copy job is allowed */
1000    if (jcr->getJobType() != JT_COPY) {
1001       Jmsg(jcr, M_FATAL, 0,
1002            _("Selection Type 'pooluncopiedjobs' only applies to Copy Jobs"));
1003       goto bail_out;
1004    }
1005
1006    Dmsg1(dbglevel, "copy selection pattern=%s\n", jcr->rpool->name());
1007    Mmsg(query, sql_jobids_of_pool_uncopied_jobs, jcr->rpool->name());
1008    Dmsg1(dbglevel, "get uncopied jobs query=%s\n", query.c_str());
1009    if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
1010       Jmsg(jcr, M_FATAL, 0,
1011            _("SQL to get uncopied jobs failed. ERR=%s\n"), db_strerror(jcr->db));
1012       goto bail_out;
1013    }
1014    ok = true;
1015
1016 bail_out:
1017    return ok;
1018 }
1019
1020 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
1021                  const char *query2, const char *type) 
1022 {
1023    dlist *item_chain;
1024    uitem *item = NULL;
1025    uitem *last_item = NULL;
1026    regex_t preg;
1027    char prbuf[500];
1028    int rc;
1029    bool ok = false;
1030    POOL_MEM query(PM_MESSAGE);
1031
1032    item_chain = New(dlist(item, &item->link));
1033    if (!jcr->job->selection_pattern) {
1034       Jmsg(jcr, M_FATAL, 0, _("No %s %s selection pattern specified.\n"),
1035          jcr->get_OperationName(), type);
1036       goto bail_out;
1037    }
1038    Dmsg1(dbglevel, "regex-sel-pattern=%s\n", jcr->job->selection_pattern);
1039    /* Basic query for names */
1040    Mmsg(query, query1, jcr->rpool->name());
1041    Dmsg1(dbglevel, "get name query1=%s\n", query.c_str());
1042    if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler, 
1043         (void *)item_chain)) {
1044       Jmsg(jcr, M_FATAL, 0,
1045            _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
1046       goto bail_out;
1047    }
1048    Dmsg1(dbglevel, "query1 returned %d names\n", item_chain->size());
1049    if (item_chain->size() == 0) {
1050       Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to %s.\n"),
1051            jcr->rpool->name(), jcr->get_ActionName(0));
1052       ok = true;
1053       goto bail_out;               /* skip regex match */
1054    } else {
1055       /* Compile regex expression */
1056       rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
1057       if (rc != 0) {
1058          regerror(rc, &preg, prbuf, sizeof(prbuf));
1059          Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
1060               jcr->job->selection_pattern, prbuf);
1061          goto bail_out;
1062       }
1063       /* Now apply the regex to the names and remove any item not matched */
1064       foreach_dlist(item, item_chain) {
1065          const int nmatch = 30;
1066          regmatch_t pmatch[nmatch];
1067          if (last_item) {
1068             Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
1069             free(last_item->item);
1070             item_chain->remove(last_item);
1071          }
1072          Dmsg1(dbglevel, "get name Item=%s\n", item->item);
1073          rc = regexec(&preg, item->item, nmatch, pmatch,  0);
1074          if (rc == 0) {
1075             last_item = NULL;   /* keep this one */
1076          } else {   
1077             last_item = item;
1078          }
1079       }
1080       if (last_item) {
1081          free(last_item->item);
1082          Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
1083          item_chain->remove(last_item);
1084       }
1085       regfree(&preg);
1086    }
1087    if (item_chain->size() == 0) {
1088       Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to %s.\n"), jcr->get_ActionName(0));
1089       ok = true;
1090       goto bail_out;               /* skip regex match */
1091    }
1092
1093    /* 
1094     * At this point, we have a list of items in item_chain
1095     *  that have been matched by the regex, so now we need
1096     *  to look up their jobids.
1097     */
1098    ids->count = 0;
1099    foreach_dlist(item, item_chain) {
1100       Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
1101       Mmsg(query, query2, item->item, jcr->rpool->name());
1102       Dmsg1(dbglevel, "get id from name query2=%s\n", query.c_str());
1103       if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
1104          Jmsg(jcr, M_FATAL, 0,
1105               _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
1106          goto bail_out;
1107       }
1108    }
1109    if (ids->count == 0) {
1110       Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0));
1111    }
1112    ok = true;
1113
1114 bail_out:
1115    Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
1116    foreach_dlist(item, item_chain) {
1117       free(item->item);
1118    }
1119    delete item_chain;
1120    return ok;
1121 }
1122
1123 /*
1124  * Release resources allocated during backup.
1125  */
1126 void migration_cleanup(JCR *jcr, int TermCode)
1127 {
1128    char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
1129    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
1130    char ec6[50], ec7[50], ec8[50];
1131    char term_code[100], sd_term_msg[100];
1132    const char *term_msg;
1133    int msg_type = M_INFO;
1134    MEDIA_DBR mr;
1135    double kbps;
1136    utime_t RunTime;
1137    JCR *mig_jcr = jcr->mig_jcr;
1138    POOL_MEM query(PM_MESSAGE);
1139
1140    Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
1141    update_job_end(jcr, TermCode);
1142    memset(&mr, 0, sizeof(mr));
1143
1144    /* 
1145     * Check if we actually did something.  
1146     *  mig_jcr is jcr of the newly migrated job.
1147     */
1148    if (mig_jcr) {
1149       char old_jobid[50], new_jobid[50];
1150
1151       edit_uint64(jcr->previous_jr.JobId, old_jobid);
1152       edit_uint64(mig_jcr->jr.JobId, new_jobid);
1153
1154       mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
1155       mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
1156       mig_jcr->VolSessionId = jcr->VolSessionId;
1157       mig_jcr->VolSessionTime = jcr->VolSessionTime;
1158       mig_jcr->jr.RealEndTime = 0; 
1159       mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
1160
1161       update_job_end(mig_jcr, TermCode);
1162      
1163       /* Update final items to set them to the previous job's values */
1164       Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
1165                   "JobTDate=%s WHERE JobId=%s", 
1166          jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime, 
1167          edit_uint64(jcr->previous_jr.JobTDate, ec1),
1168          new_jobid);
1169       db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1170
1171       /*
1172        * If we terminated a migration normally:
1173        *   - mark the previous job as migrated
1174        *   - move any Log records to the new JobId
1175        *   - Purge the File records from the previous job
1176        */
1177       if (jcr->getJobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) {
1178          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1179               (char)JT_MIGRATED_JOB, old_jobid);
1180          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1181          UAContext *ua = new_ua_context(jcr);
1182          /* Move JobLog to new JobId */
1183          Mmsg(query, "UPDATE Log SET JobId=%s WHERE JobId=%s",
1184            new_jobid, old_jobid);
1185          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1186
1187          if (jcr->job->PurgeMigrateJob) {
1188             /* Purge old Job record */
1189             purge_jobs_from_catalog(ua, old_jobid);
1190          } else {
1191             /* Purge all old file records, but leave Job record */
1192             purge_files_from_jobs(ua, old_jobid);
1193          }
1194
1195          free_ua_context(ua);
1196       } 
1197
1198       /*
1199        * If we terminated a Copy (rather than a Migration) normally:
1200        *   - copy any Log records to the new JobId
1201        *   - set type="Job Copy" for the new job
1202        */
1203       if (jcr->getJobType() == JT_COPY && jcr->JobStatus == JS_Terminated) {
1204          /* Copy JobLog to new JobId */
1205          Mmsg(query, "INSERT INTO Log (JobId, Time, LogText ) " 
1206                       "SELECT %s, Time, LogText FROM Log WHERE JobId=%s",
1207               new_jobid, old_jobid);
1208          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1209          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1210               (char)JT_JOB_COPY, new_jobid);
1211          db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1212       } 
1213
1214       if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
1215          Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
1216             db_strerror(jcr->db));
1217          jcr->setJobStatus(JS_ErrorTerminated);
1218       }
1219
1220       update_bootstrap_file(mig_jcr);
1221
1222       if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
1223          /*
1224           * Note, if the job has failed, most likely it did not write any
1225           *  tape, so suppress this "error" message since in that case
1226           *  it is normal.  Or look at it the other way, only for a
1227           *  normal exit should we complain about this error.
1228           */
1229          if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
1230             Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
1231          }
1232          mig_jcr->VolumeName[0] = 0;         /* none */
1233       }
1234
1235       if (mig_jcr->VolumeName[0]) {
1236          /* Find last volume name. Multiple vols are separated by | */
1237          char *p = strrchr(mig_jcr->VolumeName, '|');
1238          if (p) {
1239             p++;                         /* skip | */
1240          } else {
1241             p = mig_jcr->VolumeName;     /* no |, take full name */
1242          }
1243          bstrncpy(mr.VolumeName, p, sizeof(mr.VolumeName));
1244          if (!db_get_media_record(jcr, jcr->db, &mr)) {
1245             Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
1246                mr.VolumeName, db_strerror(jcr->db));
1247          }
1248       }
1249
1250       switch (jcr->JobStatus) {
1251       case JS_Terminated:
1252          if (jcr->JobErrors || jcr->SDErrors) {
1253             term_msg = _("%s OK -- with warnings");
1254          } else {
1255             term_msg = _("%s OK");
1256          }
1257          break;
1258       case JS_FatalError:
1259       case JS_ErrorTerminated:
1260          term_msg = _("*** %s Error ***");
1261          msg_type = M_ERROR;          /* Generate error message */
1262          if (jcr->store_bsock) {
1263             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1264             if (jcr->SD_msg_chan) {
1265                pthread_cancel(jcr->SD_msg_chan);
1266             }
1267          }
1268          break;
1269       case JS_Canceled:
1270          term_msg = _("%s Canceled");
1271          if (jcr->store_bsock) {
1272             bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1273             if (jcr->SD_msg_chan) {
1274                pthread_cancel(jcr->SD_msg_chan);
1275             }
1276          }
1277          break;
1278       default:
1279          term_msg = _("Inappropriate %s term code");
1280          break;
1281       }
1282    } else {
1283       if (jcr->getJobType() == JT_MIGRATE && jcr->previous_jr.JobId != 0) {
1284          /* Mark previous job as migrated */
1285          Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1286               (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
1287          db_sql_query(jcr->db, query.c_str(), NULL, NULL);
1288       }
1289       term_msg = _("%s -- no files to %s");
1290    }
1291
1292    bsnprintf(term_code, sizeof(term_code), term_msg, jcr->get_OperationName(), jcr->get_ActionName(0));
1293    bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
1294    bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
1295    RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
1296    if (RunTime <= 0) {
1297       kbps = 0;
1298    } else {
1299       kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
1300    }
1301
1302    jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1303
1304    Jmsg(jcr, msg_type, 0, _("%s %s %s (%s):\n"
1305 "  Build OS:               %s %s %s\n"
1306 "  Prev Backup JobId:      %s\n"
1307 "  Prev Backup Job:        %s\n"
1308 "  New Backup JobId:       %s\n"
1309 "  Current JobId:          %s\n"
1310 "  Current Job:            %s\n"
1311 "  Backup Level:           %s%s\n"
1312 "  Client:                 %s\n"
1313 "  FileSet:                \"%s\" %s\n"
1314 "  Read Pool:              \"%s\" (From %s)\n"
1315 "  Read Storage:           \"%s\" (From %s)\n"
1316 "  Write Pool:             \"%s\" (From %s)\n"
1317 "  Write Storage:          \"%s\" (From %s)\n"
1318 "  Catalog:                \"%s\" (From %s)\n"
1319 "  Start time:             %s\n"
1320 "  End time:               %s\n"
1321 "  Elapsed time:           %s\n"
1322 "  Priority:               %d\n"
1323 "  SD Files Written:       %s\n"
1324 "  SD Bytes Written:       %s (%sB)\n"
1325 "  Rate:                   %.1f KB/s\n"
1326 "  Volume name(s):         %s\n"
1327 "  Volume Session Id:      %d\n"
1328 "  Volume Session Time:    %d\n"
1329 "  Last Volume Bytes:      %s (%sB)\n"
1330 "  SD Errors:              %d\n"
1331 "  SD termination status:  %s\n"
1332 "  Termination:            %s\n\n"),
1333         BACULA, my_name, VERSION, LSMDATE,
1334         HOST_OS, DISTNAME, DISTVER,
1335         edit_uint64(jcr->previous_jr.JobId, ec6),
1336         jcr->previous_jr.Job,
1337         mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
1338         edit_uint64(jcr->jr.JobId, ec8),
1339         jcr->jr.Job,
1340         level_to_str(jcr->getJobLevel()), jcr->since,
1341         jcr->client->name(),
1342         jcr->fileset->name(), jcr->FSCreateTime,
1343         jcr->rpool->name(), jcr->rpool_source,
1344         jcr->rstore?jcr->rstore->name():"*None*", 
1345         NPRT(jcr->rstore_source), 
1346         jcr->pool->name(), jcr->pool_source,
1347         jcr->wstore?jcr->wstore->name():"*None*", 
1348         NPRT(jcr->wstore_source),
1349         jcr->catalog->name(), jcr->catalog_source,
1350         sdt,
1351         edt,
1352         edit_utime(RunTime, elapsed, sizeof(elapsed)),
1353         jcr->JobPriority,
1354         edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1355         edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1356         edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1357         (float)kbps,
1358         mig_jcr ? mig_jcr->VolumeName : "",
1359         jcr->VolSessionId,
1360         jcr->VolSessionTime,
1361         edit_uint64_with_commas(mr.VolBytes, ec4),
1362         edit_uint64_with_suffix(mr.VolBytes, ec5),
1363         jcr->SDErrors,
1364         sd_term_msg,
1365         term_code);
1366
1367    Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
1368    if (jcr->mig_jcr) {
1369       free_jcr(jcr->mig_jcr);
1370       jcr->mig_jcr = NULL;
1371    }
1372    Dmsg0(100, "Leave migrate_cleanup()\n");
1373 }
1374
1375 /* 
1376  * Return next DBId from comma separated list   
1377  *
1378  * Returns:
1379  *   1 if next DBId returned
1380  *   0 if no more DBIds are in list
1381  *  -1 there is an error
1382  */
1383 static int get_next_dbid_from_list(char **p, DBId_t *DBId)
1384 {
1385    const int maxlen = 30;
1386    char id[maxlen+1];
1387    char *q = *p;
1388
1389    id[0] = 0;
1390    for (int i=0; i<maxlen; i++) {
1391       if (*q == 0) {
1392          break;
1393       } else if (*q == ',') {
1394          q++;
1395          break;
1396       }
1397       id[i] = *q++;
1398       id[i+1] = 0;
1399    }
1400    if (id[0] == 0) {
1401       return 0;
1402    } else if (!is_a_number(id)) {
1403       return -1;                      /* error */
1404    }
1405    *p = q;
1406    *DBId = str_to_int64(id);
1407    return 1;
1408 }
1409
1410 bool set_migration_wstorage(JCR *jcr, POOL *pool)
1411 {
1412    POOL *wpool = pool->NextPool;
1413
1414    if (!wpool) {
1415       Jmsg(jcr, M_FATAL, 0, _("No Next Pool specification found in Pool \"%s\".\n"),
1416          pool->hdr.name);
1417       return false;
1418    }
1419
1420    if (!wpool->storage || wpool->storage->size() == 0) {
1421       Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Next Pool \"%s\".\n"),
1422          wpool->name());
1423       return false;
1424    }
1425
1426    /* If pool storage specified, use it instead of job storage for backup */
1427    copy_wstorage(jcr, wpool->storage, _("Storage from Pool's NextPool resource"));
1428    return true;
1429 }