2 Bacula® - The Network Backup Solution
4 Copyright (C) 2004-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
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.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
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.
30 * Bacula Director -- migrate.c -- responsible for doing
31 * migration and copy jobs.
33 * Also handles Copy jobs (March MMVIII)
35 * Kern Sibbald, September MMIV
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
42 * When the Storage daemon finishes the job, update the DB.
51 #include "lib/bregex.h"
56 static const int dbglevel = 10;
58 static char OKbootstrap[] = "3000 OK bootstrap\n";
59 static int get_job_to_migrate(JCR *jcr);
61 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
62 const char *query2, const char *type);
63 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
65 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type);
66 static bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids);
67 static void start_migration_job(JCR *jcr);
68 static int get_next_dbid_from_list(char **p, DBId_t *DBId);
71 * Called here before the job is run to do the job
72 * specific setup. Note, one of the important things to
73 * complete in this init code is to make the definitive
74 * choice of input and output storage devices. This is
75 * because immediately after the init, the job is queued
76 * in the jobq.c code, and it checks that all the resources
77 * (storage resources in particular) are available, so these
78 * must all be properly defined.
80 * previous_jr refers to the job DB record of the Job that is
81 * going to be migrated.
82 * prev_job refers to the job resource of the Job that is
83 * going to be migrated.
84 * jcr is the jcr for the current "migration" job. It is a
85 * control job that is put in the DB as a migration job, which
86 * means that this job migrated a previous job to a new job.
87 * No Volume or File data is associated with this control
89 * mig_jcr refers to the newly migrated job that is run by
90 * the current jcr. It is a backup job that moves (migrates) the
91 * data written for the previous_jr into the new pool. This
92 * job (mig_jcr) becomes the new backup job that replaces
93 * the original backup job. Note, this jcr is not really run. It
94 * is simply attached to the current jcr. It will show up in
95 * the Director's status output, but not in the SD or FD, both of
96 * which deal only with the current migration job (i.e. jcr).
98 bool do_migration_init(JCR *jcr)
104 JCR *mig_jcr; /* newly migrated job */
108 apply_pool_overrides(jcr);
110 if (!allow_duplicate_job(jcr)) {
114 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->pool->name());
115 if (jcr->jr.PoolId == 0) {
116 Dmsg1(dbglevel, "JobId=%d no PoolId\n", (int)jcr->JobId);
117 Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n"));
121 * Note, at this point, pool is the pool for this job. We
122 * transfer it to rpool (read pool), and a bit later,
123 * pool will be changed to point to the write pool,
124 * which comes from pool->NextPool.
126 jcr->rpool = jcr->pool; /* save read pool */
127 pm_strcpy(jcr->rpool_source, jcr->pool_source);
130 Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source);
132 if (!get_or_create_fileset_record(jcr)) {
133 Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId);
134 Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
138 /* If we find a job or jobs to migrate it is previous_jr.JobId */
139 count = get_job_to_migrate(jcr);
147 Dmsg1(dbglevel, "Back from get_job_to_migrate JobId=%d\n", (int)jcr->JobId);
149 if (jcr->previous_jr.JobId == 0) {
150 Dmsg1(dbglevel, "JobId=%d no previous JobId\n", (int)jcr->JobId);
151 Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName(0));
152 return true; /* no work */
155 create_restore_bootstrap_file(jcr);
157 if (jcr->previous_jr.JobId == 0 || jcr->ExpectedFiles == 0) {
158 set_jcr_job_status(jcr, JS_Terminated);
159 Dmsg1(dbglevel, "JobId=%d expected files == 0\n", (int)jcr->JobId);
160 if (jcr->previous_jr.JobId == 0) {
161 Jmsg(jcr, M_INFO, 0, _("No previous Job found to %s.\n"), jcr->get_ActionName(0));
163 Jmsg(jcr, M_INFO, 0, _("Previous Job has no data to %s.\n"), jcr->get_ActionName(0));
165 return true; /* no work */
169 Dmsg5(dbglevel, "JobId=%d: Current: Name=%s JobId=%d Type=%c Level=%c\n",
171 jcr->jr.Name, (int)jcr->jr.JobId,
172 jcr->jr.JobType, jcr->jr.JobLevel);
175 job = (JOB *)GetResWithName(R_JOB, jcr->jr.Name);
176 prev_job = (JOB *)GetResWithName(R_JOB, jcr->previous_jr.Name);
179 Jmsg(jcr, M_FATAL, 0, _("Job resource not found for \"%s\".\n"), jcr->jr.Name);
183 Jmsg(jcr, M_FATAL, 0, _("Previous Job resource not found for \"%s\".\n"),
184 jcr->previous_jr.Name);
188 jcr->spool_data = job->spool_data; /* turn on spooling if requested in job */
190 /* Create a migration jcr */
191 mig_jcr = jcr->mig_jcr = new_jcr(sizeof(JCR), dird_free_jcr);
192 memcpy(&mig_jcr->previous_jr, &jcr->previous_jr, sizeof(mig_jcr->previous_jr));
195 * Turn the mig_jcr into a "real" job that takes on the aspects of
196 * the previous backup job "prev_job".
198 set_jcr_defaults(mig_jcr, prev_job);
199 if (!setup_job(mig_jcr)) {
200 Jmsg(jcr, M_FATAL, 0, _("setup job failed.\n"));
204 /* Now reset the job record from the previous job */
205 memcpy(&mig_jcr->jr, &jcr->previous_jr, sizeof(mig_jcr->jr));
206 /* Update the jr to reflect the new values of PoolId and JobId. */
207 mig_jcr->jr.PoolId = jcr->jr.PoolId;
208 mig_jcr->jr.JobId = mig_jcr->JobId;
210 Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
211 mig_jcr->jr.Name, (int)mig_jcr->jr.JobId,
212 mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
215 * Get the PoolId used with the original job. Then
216 * find the pool name from the database record.
218 memset(&pr, 0, sizeof(pr));
219 pr.PoolId = mig_jcr->previous_jr.PoolId;
220 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
221 Jmsg(jcr, M_FATAL, 0, _("Pool for JobId %s not in database. ERR=%s\n"),
222 edit_int64(pr.PoolId, ed1), db_strerror(jcr->db));
225 /* Get the pool resource corresponding to the original job */
226 pool = (POOL *)GetResWithName(R_POOL, pr.Name);
228 Jmsg(jcr, M_FATAL, 0, _("Pool resource \"%s\" not found.\n"), pr.Name);
232 /* If pool storage specified, use it for restore */
233 copy_rstorage(mig_jcr, pool->storage, _("Pool resource"));
234 copy_rstorage(jcr, pool->storage, _("Pool resource"));
237 * If the original backup pool has a NextPool, make sure a
238 * record exists in the database. Note, in this case, we
239 * will be migrating from pool to pool->NextPool.
241 if (pool->NextPool) {
242 jcr->jr.PoolId = get_or_create_pool_record(jcr, pool->NextPool->name());
243 if (jcr->jr.PoolId == 0) {
247 if (!set_migration_wstorage(jcr, pool)) {
250 mig_jcr->pool = jcr->pool = pool->NextPool;
251 pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource"));
252 mig_jcr->jr.PoolId = jcr->jr.PoolId;
254 Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->pool->name(), jcr->rpool->name());
259 * Do a Migration of a previous job
261 * Returns: false on failure
264 bool do_migration(JCR *jcr)
268 JCR *mig_jcr = jcr->mig_jcr; /* newly migrated job */
271 * If mig_jcr is NULL, there is nothing to do for this job,
272 * so set a normal status, cleanup and return OK.
275 set_jcr_job_status(jcr, JS_Terminated);
276 migration_cleanup(jcr, jcr->JobStatus);
280 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
281 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to %s. ERR=%s"),
282 edit_int64(jcr->previous_jr.JobId, ed1),
283 jcr->get_ActionName(0),
284 db_strerror(jcr->db));
285 set_jcr_job_status(jcr, JS_Terminated);
286 migration_cleanup(jcr, jcr->JobStatus);
289 /* Make sure this job was not already migrated */
290 if (jcr->previous_jr.JobType != JT_BACKUP) {
291 Jmsg(jcr, M_INFO, 0, _("JobId %s already %s probably by another Job. %s stopped.\n"),
292 edit_int64(jcr->previous_jr.JobId, ed1),
293 jcr->get_ActionName(1),
294 jcr->get_OperationName());
295 set_jcr_job_status(jcr, JS_Terminated);
296 migration_cleanup(jcr, jcr->JobStatus);
300 /* Print Job Start message */
301 Jmsg(jcr, M_INFO, 0, _("Start %s JobId %s, Job=%s\n"),
302 jcr->get_OperationName(), edit_uint64(jcr->JobId, ed1), jcr->Job);
305 * Open a message channel connection with the Storage
306 * daemon. This is to let him know that our client
307 * will be contacting him for a backup session.
310 Dmsg0(110, "Open connection with storage daemon\n");
311 set_jcr_job_status(jcr, JS_WaitSD);
312 set_jcr_job_status(mig_jcr, JS_WaitSD);
314 * Start conversation with Storage daemon
316 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
319 sd = jcr->store_bsock;
321 * Now start a job with the Storage daemon
323 Dmsg2(dbglevel, "Read store=%s, write store=%s\n",
324 ((STORE *)jcr->rstorage->first())->name(),
325 ((STORE *)jcr->wstorage->first())->name());
326 if (((STORE *)jcr->rstorage->first())->name() == ((STORE *)jcr->wstorage->first())->name()) {
327 Jmsg(jcr, M_FATAL, 0, _("Read storage \"%s\" same as write storage.\n"),
328 ((STORE *)jcr->rstorage->first())->name());
331 if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage)) {
334 Dmsg0(150, "Storage daemon connection OK\n");
336 if (!send_bootstrap_file(jcr, sd) ||
337 !response(jcr, sd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
342 * We re-update the job start record so that the start
343 * time is set after the run before job. This avoids
344 * that any files created by the run before job will
345 * be saved twice. They will be backed up in the current
346 * job, but not in the next one unless they are changed.
347 * Without this, they will be backed up in this job and
348 * in the next job run because in that case, their date
349 * is after the start of this run.
351 jcr->start_time = time(NULL);
352 jcr->jr.StartTime = jcr->start_time;
353 jcr->jr.JobTDate = jcr->start_time;
354 set_jcr_job_status(jcr, JS_Running);
356 /* Update job start record for this migration control job */
357 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
358 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
363 mig_jcr->start_time = time(NULL);
364 mig_jcr->jr.StartTime = mig_jcr->start_time;
365 mig_jcr->jr.JobTDate = mig_jcr->start_time;
366 set_jcr_job_status(mig_jcr, JS_Running);
368 /* Update job start record for the real migration backup job */
369 if (!db_update_job_start_record(mig_jcr, mig_jcr->db, &mig_jcr->jr)) {
370 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(mig_jcr->db));
374 Dmsg4(dbglevel, "mig_jcr: Name=%s JobId=%d Type=%c Level=%c\n",
375 mig_jcr->jr.Name, (int)mig_jcr->jr.JobId,
376 mig_jcr->jr.JobType, mig_jcr->jr.JobLevel);
380 * Start the job prior to starting the message thread below
381 * to avoid two threads from using the BSOCK structure at
384 if (!sd->fsend("run")) {
389 * Now start a Storage daemon message thread
391 if (!start_storage_daemon_message_thread(jcr)) {
396 set_jcr_job_status(jcr, JS_Running);
397 set_jcr_job_status(mig_jcr, JS_Running);
399 /* Pickup Job termination data */
400 /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */
401 wait_for_storage_daemon_termination(jcr);
402 set_jcr_job_status(jcr, jcr->SDJobStatus);
403 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
404 if (jcr->JobStatus != JS_Terminated) {
408 migration_cleanup(jcr, jcr->JobStatus);
418 /* Add an item to the list if it is unique */
419 static void add_unique_id(idpkt *ids, char *item)
421 const int maxlen = 30;
425 /* Walk through current list to see if each item is the same as item */
428 for (int i=0; i<maxlen; i++) {
431 } else if (*q == ',') {
438 if (strcmp(item, id) == 0) {
442 /* Did not find item, so add it to list */
443 if (ids->count == 0) {
446 pm_strcat(ids->list, ",");
448 pm_strcat(ids->list, item);
450 // Dmsg3(0, "add_uniq count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
455 * Callback handler make list of DB Ids
457 static int unique_dbid_handler(void *ctx, int num_fields, char **row)
459 idpkt *ids = (idpkt *)ctx;
461 add_unique_id(ids, row[0]);
462 Dmsg3(dbglevel, "dbid_hdlr count=%d Ids=%p %s\n", ids->count, ids->list, ids->list);
472 static int item_compare(void *item1, void *item2)
474 uitem *i1 = (uitem *)item1;
475 uitem *i2 = (uitem *)item2;
476 return strcmp(i1->item, i2->item);
479 static int unique_name_handler(void *ctx, int num_fields, char **row)
481 dlist *list = (dlist *)ctx;
483 uitem *new_item = (uitem *)malloc(sizeof(uitem));
486 memset(new_item, 0, sizeof(uitem));
487 new_item->item = bstrdup(row[0]);
488 Dmsg1(dbglevel, "Unique_name_hdlr Item=%s\n", row[0]);
489 item = (uitem *)list->binary_insert((void *)new_item, item_compare);
490 if (item != new_item) { /* already in list */
491 free(new_item->item);
492 free((char *)new_item);
498 /* Get Job names in Pool */
499 const char *sql_job =
500 "SELECT DISTINCT Job.Name from Job,Pool"
501 " WHERE Pool.Name='%s' AND Job.PoolId=Pool.PoolId";
503 /* Get JobIds from regex'ed Job names */
504 const char *sql_jobids_from_job =
505 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
506 " WHERE Job.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
507 " ORDER by Job.StartTime";
509 /* Get Client names in Pool */
510 const char *sql_client =
511 "SELECT DISTINCT Client.Name from Client,Pool,Job"
512 " WHERE Pool.Name='%s' AND Job.ClientId=Client.ClientId AND"
513 " Job.PoolId=Pool.PoolId";
515 /* Get JobIds from regex'ed Client names */
516 const char *sql_jobids_from_client =
517 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool,Client"
518 " WHERE Client.Name='%s' AND Pool.Name='%s' AND Job.PoolId=Pool.PoolId"
519 " AND Job.ClientId=Client.ClientId AND Job.Type='B'"
520 " AND Job.JobStatus = 'T'"
521 " ORDER by Job.StartTime";
523 /* Get Volume names in Pool */
524 const char *sql_vol =
525 "SELECT DISTINCT VolumeName FROM Media,Pool WHERE"
526 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
527 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'";
529 /* Get JobIds from regex'ed Volume names */
530 const char *sql_jobids_from_vol =
531 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Media,JobMedia,Job"
532 " WHERE Media.VolumeName='%s' AND Media.MediaId=JobMedia.MediaId"
533 " AND JobMedia.JobId=Job.JobId AND Job.Type='B'"
534 " AND Job.JobStatus = 'T' AND Media.Enabled=1"
535 " ORDER by Job.StartTime";
537 const char *sql_smallest_vol =
538 "SELECT Media.MediaId FROM Media,Pool,JobMedia WHERE"
539 " Media.MediaId in (SELECT DISTINCT MediaId from JobMedia) AND"
540 " Media.VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
541 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
542 " ORDER BY VolBytes ASC LIMIT 1";
544 const char *sql_oldest_vol =
545 "SELECT Media.MediaId FROM Media,Pool,JobMedia WHERE"
546 " Media.MediaId in (SELECT DISTINCT MediaId from JobMedia) AND"
547 " Media.VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
548 " Media.PoolId=Pool.PoolId AND Pool.Name='%s'"
549 " ORDER BY LastWritten ASC LIMIT 1";
551 /* Get JobIds when we have selected MediaId */
552 const char *sql_jobids_from_mediaid =
553 "SELECT DISTINCT Job.JobId,Job.StartTime FROM JobMedia,Job"
554 " WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId IN (%s)"
555 " AND Job.Type='B' AND Job.JobStatus = 'T'"
556 " ORDER by Job.StartTime";
558 /* Get the number of bytes in the pool */
559 const char *sql_pool_bytes =
560 "SELECT SUM(JobBytes) FROM Job WHERE JobId IN"
561 " (SELECT DISTINCT Job.JobId from Pool,Job,Media,JobMedia WHERE"
562 " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
563 " VolStatus in ('Full','Used','Error','Append') AND Media.Enabled=1 AND"
564 " Job.Type='B' AND Job.JobStatus = 'T' AND"
565 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId)";
567 /* Get the number of bytes in the Jobs */
568 const char *sql_job_bytes =
569 "SELECT SUM(JobBytes) FROM Job WHERE JobId IN (%s)";
571 /* Get Media Ids in Pool */
572 const char *sql_mediaids =
573 "SELECT MediaId FROM Media,Pool WHERE"
574 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
575 " Media.PoolId=Pool.PoolId AND Pool.Name='%s' ORDER BY LastWritten ASC";
577 /* Get JobIds in Pool longer than specified time */
578 const char *sql_pool_time =
579 "SELECT DISTINCT Job.JobId FROM Pool,Job,Media,JobMedia WHERE"
580 " Pool.Name='%s' AND Media.PoolId=Pool.PoolId AND"
581 " VolStatus in ('Full','Used','Error') AND Media.Enabled=1 AND"
582 " Job.Type='B' AND Job.JobStatus = 'T' AND"
583 " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId"
584 " AND Job.RealEndTime<='%s'";
586 /* Get JobIds from successfully completed backup jobs which have not been copied before */
587 const char *sql_jobids_of_pool_uncopied_jobs =
588 "SELECT DISTINCT Job.JobId,Job.StartTime FROM Job,Pool"
589 " WHERE Pool.Name = '%s' AND Pool.PoolId = Job.PoolId"
590 " AND Job.Type = 'B' AND Job.JobStatus = 'T'"
591 " AND Job.jobBytes > 0"
592 " AND Job.JobId NOT IN"
593 " (SELECT PriorJobId FROM Job WHERE"
594 " Type IN ('B','C') AND Job.JobStatus = 'T'"
595 " AND PriorJobId != 0)"
596 " ORDER by Job.StartTime";
599 * const char *sql_ujobid =
600 * "SELECT DISTINCT Job.Job from Client,Pool,Media,Job,JobMedia "
601 * " WHERE Media.PoolId=Pool.PoolId AND Pool.Name='%s' AND"
602 * " JobMedia.JobId=Job.JobId AND Job.PoolId=Media.PoolId";
607 * This is the central piece of code that finds a job or jobs
608 * actually JobIds to migrate. It first looks to see if one
609 * has been "manually" specified in jcr->MigrateJobId, and if
610 * so, it returns that JobId to be run. Otherwise, it
611 * examines the Selection Type to see what kind of migration
612 * we are doing (Volume, Job, Client, ...) and applies any
613 * Selection Pattern if appropriate to obtain a list of JobIds.
614 * Finally, it will loop over all the JobIds found, except the last
615 * one starting a new job with MigrationJobId set to that JobId, and
616 * finally, it returns the last JobId to the caller.
618 * Returns: -1 on error
619 * 0 if no jobs to migrate
620 * 1 if OK and jcr->previous_jr filled in
622 static int get_job_to_migrate(JCR *jcr)
624 char ed1[30], ed2[30];
625 POOL_MEM query(PM_MESSAGE);
630 idpkt ids, mid, jids;
635 char dt[MAX_TIME_LENGTH];
638 ids.list = get_pool_memory(PM_MESSAGE);
641 mid.list = get_pool_memory(PM_MESSAGE);
644 jids.list = get_pool_memory(PM_MESSAGE);
650 * If MigrateJobId is set, then we migrate only that Job,
651 * otherwise, we go through the full selection of jobs to
654 if (jcr->MigrateJobId != 0) {
655 Dmsg1(dbglevel, "At Job start previous jobid=%u\n", jcr->MigrateJobId);
656 edit_uint64(jcr->MigrateJobId, ids.list);
659 switch (jcr->job->selection_type) {
661 if (!regex_find_jobids(jcr, &ids, sql_job, sql_jobids_from_job, "Job")) {
666 if (!regex_find_jobids(jcr, &ids, sql_client, sql_jobids_from_client, "Client")) {
671 if (!regex_find_jobids(jcr, &ids, sql_vol, sql_jobids_from_vol, "Volume")) {
676 if (!jcr->job->selection_pattern) {
677 Jmsg(jcr, M_FATAL, 0, _("No %s SQL selection pattern specified.\n"), jcr->get_OperationName());
680 Dmsg1(dbglevel, "SQL=%s\n", jcr->job->selection_pattern);
681 if (!db_sql_query(jcr->db, jcr->job->selection_pattern,
682 unique_dbid_handler, (void *)&ids)) {
683 Jmsg(jcr, M_FATAL, 0,
684 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
688 case MT_SMALLEST_VOL:
689 if (!find_mediaid_then_jobids(jcr, &ids, sql_smallest_vol, "Smallest Volume")) {
694 if (!find_mediaid_then_jobids(jcr, &ids, sql_oldest_vol, "Oldest Volume")) {
698 case MT_POOL_OCCUPANCY:
700 /* Find count of bytes in pool */
701 Mmsg(query, sql_pool_bytes, jcr->rpool->name());
702 if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
703 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
706 if (ctx.count == 0) {
707 Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
710 pool_bytes = ctx.value;
711 Dmsg2(dbglevel, "highbytes=%lld pool=%lld\n", jcr->rpool->MigrationHighBytes,
713 if (pool_bytes < (int64_t)jcr->rpool->MigrationHighBytes) {
714 Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
717 Dmsg0(dbglevel, "We should do Occupation migration.\n");
720 /* Find a list of MediaIds that could be migrated */
721 Mmsg(query, sql_mediaids, jcr->rpool->name());
722 Dmsg1(dbglevel, "query=%s\n", query.c_str());
723 if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
724 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
727 if (ids.count == 0) {
728 Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
731 Dmsg2(dbglevel, "Pool Occupancy ids=%d MediaIds=%s\n", ids.count, ids.list);
733 if (!find_jobids_from_mediaid_list(jcr, &ids, "Volume")) {
736 /* ids == list of jobs */
738 for (int i=0; i < (int)ids.count; i++) {
739 stat = get_next_dbid_from_list(&p, &DBId);
740 Dmsg2(dbglevel, "get_next_dbid stat=%d JobId=%u\n", stat, (uint32_t)DBId);
742 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
744 } else if (stat == 0) {
749 Mmsg(mid.list, "%s", edit_int64(DBId, ed1));
750 if (jids.count > 0) {
751 pm_strcat(jids.list, ",");
753 pm_strcat(jids.list, mid.list);
754 jids.count += mid.count;
756 /* Find count of bytes from Jobs */
757 Mmsg(query, sql_job_bytes, mid.list);
758 Dmsg1(dbglevel, "Jobbytes query: %s\n", query.c_str());
759 if (!db_sql_query(jcr->db, query.c_str(), db_int64_handler, (void *)&ctx)) {
760 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
763 pool_bytes -= ctx.value;
764 Dmsg2(dbglevel, "Total %s Job bytes=%s\n", jcr->get_ActionName(0), edit_int64_with_commas(ctx.value, ed1));
765 Dmsg2(dbglevel, "lowbytes=%s poolafter=%s\n",
766 edit_int64_with_commas(jcr->rpool->MigrationLowBytes, ed1),
767 edit_int64_with_commas(pool_bytes, ed2));
768 if (pool_bytes <= (int64_t)jcr->rpool->MigrationLowBytes) {
769 Dmsg0(dbglevel, "We should be done.\n");
773 /* Transfer jids to ids, where the jobs list is expected */
774 ids.count = jids.count;
775 pm_strcpy(ids.list, jids.list);
776 Dmsg2(dbglevel, "Pool Occupancy ids=%d JobIds=%s\n", ids.count, ids.list);
779 ttime = time(NULL) - (time_t)jcr->rpool->MigrationTime;
780 (void)localtime_r(&ttime, &tm);
781 strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
784 Mmsg(query, sql_pool_time, jcr->rpool->name(), dt);
785 Dmsg1(dbglevel, "query=%s\n", query.c_str());
786 if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)&ids)) {
787 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
790 if (ids.count == 0) {
791 Jmsg(jcr, M_INFO, 0, _("No Volumes found to %s.\n"), jcr->get_ActionName(0));
794 Dmsg2(dbglevel, "PoolTime ids=%d JobIds=%s\n", ids.count, ids.list);
796 case MT_POOL_UNCOPIED_JOBS:
797 if (!find_jobids_of_pool_uncopied_jobs(jcr, &ids)) {
802 Jmsg(jcr, M_FATAL, 0, _("Unknown %s Selection Type.\n"), jcr->get_OperationName());
808 * Loop over all jobids except the last one, sending
809 * them to start_migration_job(), which will start a job
810 * for each of them. For the last JobId, we handle it below.
813 if (ids.count == 0) {
814 Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
818 Jmsg(jcr, M_INFO, 0, _("The following %u JobId%s chosen to be %s: %s\n"),
819 ids.count, (ids.count < 2) ? _(" was") : _("s were"),
820 jcr->get_ActionName(1), ids.list);
822 Dmsg2(dbglevel, "Before loop count=%d ids=%s\n", ids.count, ids.list);
823 for (int i=1; i < (int)ids.count; i++) {
825 stat = get_next_jobid_from_list(&p, &JobId);
826 Dmsg3(dbglevel, "get_jobid_no=%d stat=%d JobId=%u\n", i, stat, JobId);
827 jcr->MigrateJobId = JobId;
828 start_migration_job(jcr);
829 Dmsg0(dbglevel, "Back from start_migration_job\n");
831 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
833 } else if (stat == 0) {
834 Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
839 /* Now get the last JobId and handle it in the current job */
841 stat = get_next_jobid_from_list(&p, &JobId);
842 Dmsg2(dbglevel, "Last get_next_jobid stat=%d JobId=%u\n", stat, (int)JobId);
844 Jmsg(jcr, M_FATAL, 0, _("Invalid JobId found.\n"));
846 } else if (stat == 0) {
847 Jmsg(jcr, M_INFO, 0, _("No JobIds found to %s.\n"), jcr->get_ActionName(0));
851 jcr->previous_jr.JobId = JobId;
852 Dmsg1(dbglevel, "Previous jobid=%d\n", (int)jcr->previous_jr.JobId);
854 if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) {
855 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for JobId %s to %s. ERR=%s"),
856 edit_int64(jcr->previous_jr.JobId, ed1),
857 jcr->get_ActionName(0),
858 db_strerror(jcr->db));
861 Jmsg(jcr, M_INFO, 0, _("%s using JobId=%s Job=%s\n"),
862 jcr->get_OperationName(),
863 edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
864 Dmsg4(dbglevel, "%s JobId=%d using JobId=%s Job=%s\n",
865 jcr->get_OperationName(),
867 edit_int64(jcr->previous_jr.JobId, ed1), jcr->previous_jr.Job);
877 free_pool_memory(ids.list);
878 free_pool_memory(mid.list);
879 free_pool_memory(jids.list);
883 static void start_migration_job(JCR *jcr)
885 UAContext *ua = new_ua_context(jcr);
888 Mmsg(ua->cmd, "run %s jobid=%s", jcr->job->hdr.name,
889 edit_uint64(jcr->MigrateJobId, ed1));
890 Dmsg2(dbglevel, "=============== %s cmd=%s\n", jcr->get_OperationName(), ua->cmd);
891 parse_ua_args(ua); /* parse command */
892 JobId_t jobid = run_cmd(ua, ua->cmd);
894 Jmsg(jcr, M_ERROR, 0, _("Could not start migration job.\n"));
896 Jmsg(jcr, M_INFO, 0, _("%s JobId %d started.\n"), jcr->get_OperationName(), (int)jobid);
901 static bool find_mediaid_then_jobids(JCR *jcr, idpkt *ids, const char *query1,
905 POOL_MEM query(PM_MESSAGE);
908 /* Basic query for MediaId */
909 Mmsg(query, query1, jcr->rpool->name());
910 if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
911 Jmsg(jcr, M_FATAL, 0, _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
914 if (ids->count == 0) {
915 Jmsg(jcr, M_INFO, 0, _("No %s found to %s.\n"), type, jcr->get_ActionName(0));
916 ok = true; /* Not an error */
918 } else if (ids->count != 1) {
919 Jmsg(jcr, M_FATAL, 0, _("SQL error. Expected 1 MediaId got %d\n"), ids->count);
922 Dmsg2(dbglevel, "%s MediaIds=%s\n", type, ids->list);
924 ok = find_jobids_from_mediaid_list(jcr, ids, type);
931 * This routine returns:
932 * false if an error occurred
934 * ids.count number of jobids found (may be zero)
936 static bool find_jobids_from_mediaid_list(JCR *jcr, idpkt *ids, const char *type)
939 POOL_MEM query(PM_MESSAGE);
941 Mmsg(query, sql_jobids_from_mediaid, ids->list);
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));
947 if (ids->count == 0) {
948 Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0));
957 * This routine returns:
958 * false if an error occurred
960 * ids.count number of jobids found (may be zero)
962 static bool find_jobids_of_pool_uncopied_jobs(JCR *jcr, idpkt *ids)
965 POOL_MEM query(PM_MESSAGE);
967 /* Only a copy job is allowed */
968 if (jcr->get_JobType() != JT_COPY) {
969 Jmsg(jcr, M_FATAL, 0,
970 _("Selection Type 'pooluncopiedjobs' only applies to Copy Jobs"));
974 Dmsg1(dbglevel, "copy selection pattern=%s\n", jcr->rpool->name());
975 Mmsg(query, sql_jobids_of_pool_uncopied_jobs, jcr->rpool->name());
976 Dmsg1(dbglevel, "get uncopied jobs query=%s\n", query.c_str());
977 if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
978 Jmsg(jcr, M_FATAL, 0,
979 _("SQL to get uncopied jobs failed. ERR=%s\n"), db_strerror(jcr->db));
988 static bool regex_find_jobids(JCR *jcr, idpkt *ids, const char *query1,
989 const char *query2, const char *type)
993 uitem *last_item = NULL;
998 POOL_MEM query(PM_MESSAGE);
1000 item_chain = New(dlist(item, &item->link));
1001 if (!jcr->job->selection_pattern) {
1002 Jmsg(jcr, M_FATAL, 0, _("No %s %s selection pattern specified.\n"),
1003 jcr->get_OperationName(), type);
1006 Dmsg1(dbglevel, "regex-sel-pattern=%s\n", jcr->job->selection_pattern);
1007 /* Basic query for names */
1008 Mmsg(query, query1, jcr->rpool->name());
1009 Dmsg1(dbglevel, "get name query1=%s\n", query.c_str());
1010 if (!db_sql_query(jcr->db, query.c_str(), unique_name_handler,
1011 (void *)item_chain)) {
1012 Jmsg(jcr, M_FATAL, 0,
1013 _("SQL to get %s failed. ERR=%s\n"), type, db_strerror(jcr->db));
1016 Dmsg1(dbglevel, "query1 returned %d names\n", item_chain->size());
1017 if (item_chain->size() == 0) {
1018 Jmsg(jcr, M_INFO, 0, _("Query of Pool \"%s\" returned no Jobs to %s.\n"),
1019 jcr->rpool->name(), jcr->get_ActionName(0));
1021 goto bail_out; /* skip regex match */
1023 /* Compile regex expression */
1024 rc = regcomp(&preg, jcr->job->selection_pattern, REG_EXTENDED);
1026 regerror(rc, &preg, prbuf, sizeof(prbuf));
1027 Jmsg(jcr, M_FATAL, 0, _("Could not compile regex pattern \"%s\" ERR=%s\n"),
1028 jcr->job->selection_pattern, prbuf);
1031 /* Now apply the regex to the names and remove any item not matched */
1032 foreach_dlist(item, item_chain) {
1033 const int nmatch = 30;
1034 regmatch_t pmatch[nmatch];
1036 Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
1037 free(last_item->item);
1038 item_chain->remove(last_item);
1040 Dmsg1(dbglevel, "get name Item=%s\n", item->item);
1041 rc = regexec(&preg, item->item, nmatch, pmatch, 0);
1043 last_item = NULL; /* keep this one */
1049 free(last_item->item);
1050 Dmsg1(dbglevel, "Remove item %s\n", last_item->item);
1051 item_chain->remove(last_item);
1055 if (item_chain->size() == 0) {
1056 Jmsg(jcr, M_INFO, 0, _("Regex pattern matched no Jobs to %s.\n"), jcr->get_ActionName(0));
1058 goto bail_out; /* skip regex match */
1062 * At this point, we have a list of items in item_chain
1063 * that have been matched by the regex, so now we need
1064 * to look up their jobids.
1067 foreach_dlist(item, item_chain) {
1068 Dmsg2(dbglevel, "Got %s: %s\n", type, item->item);
1069 Mmsg(query, query2, item->item, jcr->rpool->name());
1070 Dmsg1(dbglevel, "get id from name query2=%s\n", query.c_str());
1071 if (!db_sql_query(jcr->db, query.c_str(), unique_dbid_handler, (void *)ids)) {
1072 Jmsg(jcr, M_FATAL, 0,
1073 _("SQL failed. ERR=%s\n"), db_strerror(jcr->db));
1077 if (ids->count == 0) {
1078 Jmsg(jcr, M_INFO, 0, _("No %ss found to %s.\n"), type, jcr->get_ActionName(0));
1083 Dmsg2(dbglevel, "Count=%d Jobids=%s\n", ids->count, ids->list);
1084 foreach_dlist(item, item_chain) {
1092 * Release resources allocated during backup.
1094 void migration_cleanup(JCR *jcr, int TermCode)
1096 char sdt[MAX_TIME_LENGTH], edt[MAX_TIME_LENGTH];
1097 char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], elapsed[50];
1098 char ec6[50], ec7[50], ec8[50];
1099 char term_code[100], sd_term_msg[100];
1100 const char *term_msg;
1101 int msg_type = M_INFO;
1105 JCR *mig_jcr = jcr->mig_jcr;
1106 POOL_MEM query(PM_MESSAGE);
1108 Dmsg2(100, "Enter migrate_cleanup %d %c\n", TermCode, TermCode);
1109 update_job_end(jcr, TermCode);
1110 memset(&mr, 0, sizeof(mr));
1113 * Check if we actually did something.
1114 * mig_jcr is jcr of the newly migrated job.
1117 char old_jobid[50], new_jobid[50];
1119 edit_uint64(jcr->previous_jr.JobId, old_jobid);
1120 edit_uint64(mig_jcr->jr.JobId, new_jobid);
1122 mig_jcr->JobFiles = jcr->JobFiles = jcr->SDJobFiles;
1123 mig_jcr->JobBytes = jcr->JobBytes = jcr->SDJobBytes;
1124 mig_jcr->VolSessionId = jcr->VolSessionId;
1125 mig_jcr->VolSessionTime = jcr->VolSessionTime;
1126 mig_jcr->jr.RealEndTime = 0;
1127 mig_jcr->jr.PriorJobId = jcr->previous_jr.JobId;
1129 update_job_end(mig_jcr, TermCode);
1131 /* Update final items to set them to the previous job's values */
1132 Mmsg(query, "UPDATE Job SET StartTime='%s',EndTime='%s',"
1133 "JobTDate=%s WHERE JobId=%s",
1134 jcr->previous_jr.cStartTime, jcr->previous_jr.cEndTime,
1135 edit_uint64(jcr->previous_jr.JobTDate, ec1),
1137 db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1140 * If we terminated a migration normally:
1141 * - mark the previous job as migrated
1142 * - move any Log records to the new JobId
1143 * - Purge the File records from the previous job
1145 if (jcr->get_JobType() == JT_MIGRATE && jcr->JobStatus == JS_Terminated) {
1146 Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1147 (char)JT_MIGRATED_JOB, old_jobid);
1148 db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1149 UAContext *ua = new_ua_context(jcr);
1150 /* Move JobLog to new JobId */
1151 Mmsg(query, "UPDATE Log SET JobId=%s WHERE JobId=%s",
1152 new_jobid, old_jobid);
1153 db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1154 /* Purge all old file records, but leave Job record */
1155 purge_files_from_jobs(ua, old_jobid);
1156 free_ua_context(ua);
1160 * If we terminated a copy normally:
1161 * - copy any Log records to the new JobId
1162 * - set type="Job Copy" for the new job
1164 if (jcr->get_JobType() == JT_COPY && jcr->JobStatus == JS_Terminated) {
1165 /* Copy JobLog to new JobId */
1166 Mmsg(query, "INSERT INTO Log (JobId, Time, LogText ) "
1167 "SELECT %s, Time, LogText FROM Log WHERE JobId=%s",
1168 new_jobid, old_jobid);
1169 db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1170 Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1171 (char)JT_JOB_COPY, new_jobid);
1172 db_sql_query(mig_jcr->db, query.c_str(), NULL, NULL);
1175 if (!db_get_job_record(jcr, jcr->db, &jcr->jr)) {
1176 Jmsg(jcr, M_WARNING, 0, _("Error getting Job record for Job report: ERR=%s"),
1177 db_strerror(jcr->db));
1178 set_jcr_job_status(jcr, JS_ErrorTerminated);
1181 update_bootstrap_file(mig_jcr);
1183 if (!db_get_job_volume_names(mig_jcr, mig_jcr->db, mig_jcr->jr.JobId, &mig_jcr->VolumeName)) {
1185 * Note, if the job has failed, most likely it did not write any
1186 * tape, so suppress this "error" message since in that case
1187 * it is normal. Or look at it the other way, only for a
1188 * normal exit should we complain about this error.
1190 if (jcr->JobStatus == JS_Terminated && jcr->jr.JobBytes) {
1191 Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(mig_jcr->db));
1193 mig_jcr->VolumeName[0] = 0; /* none */
1196 if (mig_jcr->VolumeName[0]) {
1197 /* Find last volume name. Multiple vols are separated by | */
1198 char *p = strrchr(mig_jcr->VolumeName, '|');
1202 p = mig_jcr->VolumeName; /* no |, take full name */
1204 bstrncpy(mr.VolumeName, p, sizeof(mr.VolumeName));
1205 if (!db_get_media_record(jcr, jcr->db, &mr)) {
1206 Jmsg(jcr, M_WARNING, 0, _("Error getting Media record for Volume \"%s\": ERR=%s"),
1207 mr.VolumeName, db_strerror(jcr->db));
1211 switch (jcr->JobStatus) {
1213 if (jcr->JobErrors || jcr->SDErrors) {
1214 term_msg = _("%s OK -- with warnings");
1216 term_msg = _("%s OK");
1220 case JS_ErrorTerminated:
1221 term_msg = _("*** %s Error ***");
1222 msg_type = M_ERROR; /* Generate error message */
1223 if (jcr->store_bsock) {
1224 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1225 if (jcr->SD_msg_chan) {
1226 pthread_cancel(jcr->SD_msg_chan);
1231 term_msg = _("%s Canceled");
1232 if (jcr->store_bsock) {
1233 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
1234 if (jcr->SD_msg_chan) {
1235 pthread_cancel(jcr->SD_msg_chan);
1240 term_msg = _("Inappropriate %s term code");
1244 if (jcr->get_JobType() == JT_MIGRATE && jcr->previous_jr.JobId != 0) {
1245 /* Mark previous job as migrated */
1246 Mmsg(query, "UPDATE Job SET Type='%c' WHERE JobId=%s",
1247 (char)JT_MIGRATED_JOB, edit_uint64(jcr->previous_jr.JobId, ec1));
1248 db_sql_query(jcr->db, query.c_str(), NULL, NULL);
1250 term_msg = _("%s -- no files to %s");
1253 bsnprintf(term_code, sizeof(term_code), term_msg, jcr->get_OperationName(), jcr->get_ActionName(0));
1254 bstrftimes(sdt, sizeof(sdt), jcr->jr.StartTime);
1255 bstrftimes(edt, sizeof(edt), jcr->jr.EndTime);
1256 RunTime = jcr->jr.EndTime - jcr->jr.StartTime;
1260 kbps = (double)jcr->SDJobBytes / (1000 * RunTime);
1263 jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg));
1265 Jmsg(jcr, msg_type, 0, _("%s %s %s (%s): %s\n"
1266 " Build OS: %s %s %s\n"
1267 " Prev Backup JobId: %s\n"
1268 " New Backup JobId: %s\n"
1269 " Current JobId: %s\n"
1270 " Current Job: %s\n"
1271 " Backup Level: %s%s\n"
1273 " FileSet: \"%s\" %s\n"
1274 " Read Pool: \"%s\" (From %s)\n"
1275 " Read Storage: \"%s\" (From %s)\n"
1276 " Write Pool: \"%s\" (From %s)\n"
1277 " Write Storage: \"%s\" (From %s)\n"
1278 " Catalog: \"%s\" (From %s)\n"
1281 " Elapsed time: %s\n"
1283 " SD Files Written: %s\n"
1284 " SD Bytes Written: %s (%sB)\n"
1285 " Rate: %.1f KB/s\n"
1286 " Volume name(s): %s\n"
1287 " Volume Session Id: %d\n"
1288 " Volume Session Time: %d\n"
1289 " Last Volume Bytes: %s (%sB)\n"
1291 " SD termination status: %s\n"
1292 " Termination: %s\n\n"),
1293 BACULA, my_name, VERSION, LSMDATE, edt,
1294 HOST_OS, DISTNAME, DISTVER,
1295 edit_uint64(jcr->previous_jr.JobId, ec6),
1296 mig_jcr ? edit_uint64(mig_jcr->jr.JobId, ec7) : "0",
1297 edit_uint64(jcr->jr.JobId, ec8),
1299 level_to_str(jcr->get_JobLevel()), jcr->since,
1300 jcr->client->name(),
1301 jcr->fileset->name(), jcr->FSCreateTime,
1302 jcr->rpool->name(), jcr->rpool_source,
1303 jcr->rstore?jcr->rstore->name():"*None*",
1304 NPRT(jcr->rstore_source),
1305 jcr->pool->name(), jcr->pool_source,
1306 jcr->wstore?jcr->wstore->name():"*None*",
1307 NPRT(jcr->wstore_source),
1308 jcr->catalog->name(), jcr->catalog_source,
1311 edit_utime(RunTime, elapsed, sizeof(elapsed)),
1313 edit_uint64_with_commas(jcr->SDJobFiles, ec1),
1314 edit_uint64_with_commas(jcr->SDJobBytes, ec2),
1315 edit_uint64_with_suffix(jcr->SDJobBytes, ec3),
1317 mig_jcr ? mig_jcr->VolumeName : "",
1319 jcr->VolSessionTime,
1320 edit_uint64_with_commas(mr.VolBytes, ec4),
1321 edit_uint64_with_suffix(mr.VolBytes, ec5),
1326 Dmsg1(100, "migrate_cleanup() mig_jcr=0x%x\n", jcr->mig_jcr);
1328 free_jcr(jcr->mig_jcr);
1329 jcr->mig_jcr = NULL;
1331 Dmsg0(100, "Leave migrate_cleanup()\n");
1335 * Return next DBId from comma separated list
1338 * 1 if next DBId returned
1339 * 0 if no more DBIds are in list
1340 * -1 there is an error
1342 static int get_next_dbid_from_list(char **p, DBId_t *DBId)
1344 const int maxlen = 30;
1349 for (int i=0; i<maxlen; i++) {
1352 } else if (*q == ',') {
1361 } else if (!is_a_number(id)) {
1362 return -1; /* error */
1365 *DBId = str_to_int64(id);
1369 bool set_migration_wstorage(JCR *jcr, POOL *pool)
1371 POOL *wpool = pool->NextPool;
1374 Jmsg(jcr, M_FATAL, 0, _("No Next Pool specification found in Pool \"%s\".\n"),
1379 if (!wpool->storage || wpool->storage->size() == 0) {
1380 Jmsg(jcr, M_FATAL, 0, _("No Storage specification found in Next Pool \"%s\".\n"),
1385 /* If pool storage specified, use it instead of job storage for backup */
1386 copy_wstorage(jcr, wpool->storage, _("Storage from Pool's NextPool resource"));