int db_delete_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
/* sql_find.c */
+bool db_find_last_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime, int JobLevel);
bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime);
bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr);
int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr);
return false;
}
+
+/*
+ * Find the last job start time for the specified JobLevel
+ *
+ * StartTime is returned in stime
+ *
+ * Returns: false on failure
+ * true on success, jr is unchanged, but stime is set
+ */
+bool
+db_find_last_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime, int JobLevel)
+{
+ SQL_ROW row;
+ char ed1[50], ed2[50];
+
+ db_lock(mdb);
+
+ pm_strcpy(stime, "0000-00-00 00:00:00"); /* default */
+
+ Mmsg(mdb->cmd,
+"SELECT StartTime FROM Job WHERE JobStatus='T' AND Type='%c' AND "
+"Level='%c' AND Name='%s' AND ClientId=%s AND FileSetId=%s "
+"ORDER BY StartTime DESC LIMIT 1",
+ jr->JobType, JobLevel, jr->Name,
+ edit_int64(jr->ClientId, ed1), edit_int64(jr->FileSetId, ed2));
+ if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
+ Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"),
+ sql_strerror(mdb), mdb->cmd);
+ goto bail_out;
+ }
+ if ((row = sql_fetch_row(mdb)) == NULL) {
+ sql_free_result(mdb);
+ Mmsg(mdb->errmsg, _("No prior Full backup Job record found.\n"));
+ goto bail_out;
+ }
+ Dmsg1(100, "Got start time: %s\n", row[0]);
+ pm_strcpy(stime, row[0]);
+ sql_free_result(mdb);
+ db_unlock(mdb);
+ return true;
+
+bail_out:
+ db_unlock(mdb);
+ return false;
+}
+
/*
* Find last failed job since given start-time
* it must be either Full or Diff.
{"allowhigherduplicates", store_bool, ITEM(res_job.AllowHigherDuplicates), 0, ITEM_DEFAULT, true},
{"cancelqueuedduplicates", store_bool, ITEM(res_job.CancelQueuedDuplicates), 0, ITEM_DEFAULT, true},
{"cancelrunningduplicates", store_bool, ITEM(res_job.CancelRunningDuplicates), 0, ITEM_DEFAULT, false},
+ {"pluginoptions", store_str, ITEM(res_job.PluginOptions), 0, 0, 0},
{NULL, NULL, {0}, 0, 0, 0}
};
if (res->res_job.WriteBootstrap) {
sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
}
+ if (res->res_job.PluginOptions) {
+ sendit(sock, _(" --> PluginOptions=%s\n"), NPRT(res->res_job.PluginOptions));
+ }
if (res->res_job.storage) {
STORE *store;
foreach_alist(store, res->res_job.storage) {
if (res->res_job.WriteBootstrap) {
free(res->res_job.WriteBootstrap);
}
+ if (res->res_job.PluginOptions) {
+ free(res->res_job.PluginOptions);
+ }
if (res->res_job.selection_pattern) {
free(res->res_job.selection_pattern);
}
int JobLevel; /* default backup/verify level */
int Priority; /* Job priority */
int RestoreJobId; /* What -- JobId to restore */
+ int RescheduleTimes; /* Number of times to reschedule job */
+ int replace; /* How (overwrite, ..) */
+ int selection_type;
+
char *RestoreWhere; /* Where on disk to restore -- directory */
char *RegexWhere; /* RegexWhere option */
char *strip_prefix; /* remove prefix from filename */
char *add_prefix; /* add prefix to filename */
char *add_suffix; /* add suffix to filename -- .old */
- bool where_use_regexp; /* true if RestoreWhere is a BREGEXP */
char *RestoreBootstrap; /* Bootstrap file */
- alist *RunScripts; /* Run {client} program {after|before} Job */
+ char *PluginOptions; /* Options to pass to plugin */
union {
char *WriteBootstrap; /* Where to write bootstrap Job updates */
char *WriteVerifyList; /* List of changed files */
};
- int replace; /* How (overwrite, ..) */
utime_t MaxRunTime; /* max run time in seconds */
utime_t MaxWaitTime; /* max blocking time in seconds */
utime_t FullMaxWaitTime; /* Max Full job wait time */
utime_t MaxFullInterval; /* Maximum time interval between Fulls */
utime_t MaxDiffInterval; /* Maximum time interval between Diffs */
utime_t DuplicateJobProximity; /* Permitted time between duplicicates */
- uint32_t MaxConcurrentJobs; /* Maximum concurrent jobs */
int64_t spool_size; /* Size of spool file for this job */
- int RescheduleTimes; /* Number of times to reschedule job */
+ uint32_t MaxConcurrentJobs; /* Maximum concurrent jobs */
+ uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
+
+ MSGS *messages; /* How and where to send messages */
+ SCHED *schedule; /* When -- Automatic schedule */
+ CLIENT *client; /* Who to backup */
+ FILESET *fileset; /* What to backup -- Fileset */
+ alist *storage; /* Where is device -- list of Storage to be used */
+ POOL *pool; /* Where is media -- Media Pool */
+ POOL *full_pool; /* Pool for Full backups */
+ POOL *inc_pool; /* Pool for Incremental backups */
+ POOL *diff_pool; /* Pool for Differental backups */
+ char *selection_pattern;
+ union {
+ JOB *verify_job; /* Job name to verify */
+ };
+ JOB *jobdefs; /* Job defaults */
+ alist *run_cmds; /* Run commands */
+ alist *RunScripts; /* Run {client} program {after|before} Job */
+
+ bool where_use_regexp; /* true if RestoreWhere is a BREGEXP */
bool RescheduleOnError; /* Set to reschedule on error */
bool PrefixLinks; /* prefix soft links with Where path */
bool PruneJobs; /* Force pruning of Jobs */
bool CancelQueuedDuplicates; /* Cancel queued jobs */
bool CancelRunningDuplicates; /* Cancel Running jobs */
- MSGS *messages; /* How and where to send messages */
- SCHED *schedule; /* When -- Automatic schedule */
- CLIENT *client; /* Who to backup */
- FILESET *fileset; /* What to backup -- Fileset */
- alist *storage; /* Where is device -- list of Storage to be used */
- POOL *pool; /* Where is media -- Media Pool */
- POOL *full_pool; /* Pool for Full backups */
- POOL *inc_pool; /* Pool for Incremental backups */
- POOL *diff_pool; /* Pool for Differental backups */
- char *selection_pattern;
- int selection_type;
- union {
- JOB *verify_job; /* Job name to verify */
- };
- JOB *jobdefs; /* Job defaults */
- alist *run_cmds; /* Run commands */
- uint32_t NumConcurrentJobs; /* number of concurrent jobs running */
/* Methods */
char *name() const;
bool do_diff = false;
time_t now;
utime_t full_time;
-// utime_t diff_time;
+ utime_t diff_time;
since[0] = 0;
/* If job cloned and a since time already given, use it */
switch (jcr->JobLevel) {
case L_DIFFERENTIAL:
case L_INCREMENTAL:
+ POOLMEM *stime = get_pool_memory(PM_MESSAGE);
/* Look up start time of last Full job */
now = time(NULL);
- jcr->jr.JobId = 0; /* flag for db_find_job_start time */
+ jcr->jr.JobId = 0; /* flag to return since time */
have_full = db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime);
-#ifdef xxx
/* If there was a successful job, make sure it is recent enough */
if (jcr->JobLevel == L_INCREMENTAL && have_full && jcr->job->MaxDiffInterval > 0) {
/* Lookup last diff job */
- jcr->jr.JobId = 0;
- /* ***FIXME*** must find diff start time and not destroy jcr->stime */
- if (db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime)) {
- diff_time = str_to_utime(jcr->stime);
+ if (db_find_last_job_start_time(jcr, jcr->db, &jcr->jr, &stime, L_DIFFERENTIAL)) {
+ diff_time = str_to_utime(stime);
do_diff = ((now - diff_time) <= jcr->job->MaxDiffInterval);
}
}
-#endif
- if (have_full && jcr->job->MaxFullInterval > 0) {
- full_time = str_to_utime(jcr->stime);
+ if (have_full && jcr->job->MaxFullInterval > 0 &&
+ db_find_last_job_start_time(jcr, jcr->db, &jcr->jr, &stime, L_FULL)) {
+ full_time = str_to_utime(stime);
do_full = ((now - full_time) <= jcr->job->MaxFullInterval);
}
+ free_pool_memory(stime);
+
if (!have_full || do_full) {
/* No recent Full job found, so upgrade this one to Full */
Jmsg(jcr, M_INFO, 0, "%s", db_strerror(jcr->db));
char *when, *verify_job_name, *catalog_name;
char *previous_job_name;
char *since;
+ char *plugin_options;
const char *verify_list;
JOB *job;
JOB *verify_job;
char *client_name);
static void select_where_regexp(UAContext *ua, JCR *jcr);
static bool scan_command_line_arguments(UAContext *ua, run_ctx &rc);
+static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc);
+static int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc);
/* Imported variables */
extern struct s_kw ReplaceOptions[];
{
JCR *jcr = NULL;
run_ctx rc;
- int i, opt;
+ int status;
if (!open_client_db(ua)) {
return 1;
ua->quit = true;
}
-try_again:
/*
* Create JCR to run job. NOTE!!! after this point, free_jcr()
* before returning.
ua->jcr->unlink_bsr = false;
}
- jcr->verify_job = rc.verify_job;
- jcr->previous_job = rc.previous_job;
- jcr->pool = rc.pool;
- if (jcr->pool != jcr->job->pool) {
- pm_strcpy(jcr->pool_source, _("User input"));
- }
- set_rwstorage(jcr, rc.store);
- jcr->client = rc.client;
- pm_strcpy(jcr->client_name, rc.client->name());
- jcr->fileset = rc.fileset;
- jcr->ExpectedFiles = rc.files;
- if (rc.catalog) {
- jcr->catalog = rc.catalog;
- pm_strcpy(jcr->catalog_source, _("User input"));
- }
- if (rc.where) {
- if (jcr->where) {
- free(jcr->where);
- }
- jcr->where = bstrdup(rc.where);
- rc.where = NULL;
- }
-
- if (rc.regexwhere) {
- if (jcr->RegexWhere) {
- free(jcr->RegexWhere);
- }
- jcr->RegexWhere = bstrdup(rc.regexwhere);
- rc.regexwhere = NULL;
- }
-
- if (rc.when) {
- jcr->sched_time = str_to_utime(rc.when);
- if (jcr->sched_time == 0) {
- ua->send_msg(_("Invalid time, using current time.\n"));
- jcr->sched_time = time(NULL);
- }
- rc.when = NULL;
- }
-
- if (rc.bootstrap) {
- if (jcr->RestoreBootstrap) {
- free(jcr->RestoreBootstrap);
- }
- jcr->RestoreBootstrap = bstrdup(rc.bootstrap);
- rc.bootstrap = NULL;
- }
-
- if (rc.replace) {
- jcr->replace = 0;
- for (i=0; ReplaceOptions[i].name; i++) {
- if (strcasecmp(rc.replace, ReplaceOptions[i].name) == 0) {
- jcr->replace = ReplaceOptions[i].token;
- }
- }
- if (!jcr->replace) {
- ua->send_msg(_("Invalid replace option: %s\n"), rc.replace);
- goto bail_out;
- }
- } else if (rc.job->replace) {
- jcr->replace = rc.job->replace;
- } else {
- jcr->replace = REPLACE_ALWAYS;
- }
- rc.replace = NULL;
-
- if (rc.Priority) {
- jcr->JobPriority = rc.Priority;
- rc.Priority = 0;
- }
-
- if (rc.since) {
- if (!jcr->stime) {
- jcr->stime = get_pool_memory(PM_MESSAGE);
- }
- pm_strcpy(jcr->stime, rc.since);
- rc.since = NULL;
- }
-
- if (rc.cloned) {
- jcr->cloned = rc.cloned;
- rc.cloned = false;
+try_again:
+ if (!reset_restore_context(ua, jcr, rc)) {
+ goto bail_out;
}
- /* If pool changed, update migration write storage */
- if (jcr->JobType == JT_MIGRATE || jcr->JobType == JT_COPY) {
- if (!set_migration_wstorage(jcr, rc.pool)) {
- goto bail_out;
- }
- }
- rc.replace = ReplaceOptions[0].name;
- for (i=0; ReplaceOptions[i].name; i++) {
- if (ReplaceOptions[i].token == jcr->replace) {
- rc.replace = ReplaceOptions[i].name;
- }
- }
- if (rc.level_name) {
- if (!get_level_from_name(jcr, rc.level_name)) {
- ua->send_msg(_("Level %s not valid.\n"), rc.level_name);
- goto bail_out;
- }
- rc.level_name = NULL;
- }
- if (rc.jid) {
- /* Note, this is also MigrateJobId */
- jcr->RestoreJobId = str_to_int64(rc.jid);
- rc.jid = 0;
- }
-
/* Run without prompting? */
if (ua->batch || find_arg(ua, NT_("yes")) > 0) {
goto start_job;
goto try_again;
}
+ /* Allow the user to modify the settings */
+ status = modify_job_parameters(ua, jcr, rc);
+ switch (status) {
+ case 0:
+ goto try_again;
+ case 1:
+ break;
+ case -1:
+ goto bail_out;
+ }
+
+
+ if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
+ JobId_t JobId;
+ Dmsg1(800, "Calling run_job job=%x\n", jcr->job);
+
+start_job:
+ Dmsg3(100, "JobId=%u using pool %s priority=%d\n", (int)jcr->JobId,
+ jcr->pool->name(), jcr->JobPriority);
+ JobId = run_job(jcr);
+ Dmsg4(100, "JobId=%u NewJobId=%d using pool %s priority=%d\n", (int)jcr->JobId,
+ JobId, jcr->pool->name(), jcr->JobPriority);
+ free_jcr(jcr); /* release jcr */
+ if (JobId == 0) {
+ ua->error_msg(_("Job failed.\n"));
+ } else {
+ char ed1[50];
+ ua->send_msg(_("Job queued. JobId=%s\n"), edit_int64(JobId, ed1));
+ }
+ return JobId;
+ }
+
+bail_out:
+ ua->send_msg(_("Job not run.\n"));
+ free_jcr(jcr);
+ return 0; /* do not run */
+}
+
+int modify_job_parameters(UAContext *ua, JCR *jcr, run_ctx &rc)
+{
+ int i, opt;
/*
* At user request modify parameters of job to be run.
*/
}
goto bail_out;
}
+ return 1;
- if (ua->cmd[0] == 0 || strncasecmp(ua->cmd, _("yes"), strlen(ua->cmd)) == 0) {
- JobId_t JobId;
- Dmsg1(800, "Calling run_job job=%x\n", jcr->job);
+bail_out:
+ return -1;
-start_job:
- Dmsg3(100, "JobId=%u using pool %s priority=%d\n", (int)jcr->JobId,
- jcr->pool->name(), jcr->JobPriority);
- JobId = run_job(jcr);
- Dmsg4(100, "JobId=%u NewJobId=%d using pool %s priority=%d\n", (int)jcr->JobId,
- JobId, jcr->pool->name(), jcr->JobPriority);
- free_jcr(jcr); /* release jcr */
- if (JobId == 0) {
- ua->error_msg(_("Job failed.\n"));
- } else {
- char ed1[50];
- ua->send_msg(_("Job queued. JobId=%s\n"), edit_int64(JobId, ed1));
+try_again:
+ return 0;
+}
+
+/*
+ * Reset the restore context.
+ * This subroutine can be called multiple times, so it
+ * must keep any prior settings.
+ */
+static bool reset_restore_context(UAContext *ua, JCR *jcr, run_ctx &rc)
+{
+ int i;
+
+ jcr->verify_job = rc.verify_job;
+ jcr->previous_job = rc.previous_job;
+ jcr->pool = rc.pool;
+ if (jcr->pool != jcr->job->pool) {
+ pm_strcpy(jcr->pool_source, _("User input"));
+ }
+ set_rwstorage(jcr, rc.store);
+ jcr->client = rc.client;
+ pm_strcpy(jcr->client_name, rc.client->name());
+ jcr->fileset = rc.fileset;
+ jcr->ExpectedFiles = rc.files;
+ if (rc.catalog) {
+ jcr->catalog = rc.catalog;
+ pm_strcpy(jcr->catalog_source, _("User input"));
+ }
+ if (rc.where) {
+ if (jcr->where) {
+ free(jcr->where);
}
- return JobId;
+ jcr->where = bstrdup(rc.where);
+ rc.where = NULL;
}
-bail_out:
- ua->send_msg(_("Job not run.\n"));
- free_jcr(jcr);
- return 0; /* do not run */
+
+ if (rc.regexwhere) {
+ if (jcr->RegexWhere) {
+ free(jcr->RegexWhere);
+ }
+ jcr->RegexWhere = bstrdup(rc.regexwhere);
+ rc.regexwhere = NULL;
+ }
+
+ if (rc.when) {
+ jcr->sched_time = str_to_utime(rc.when);
+ if (jcr->sched_time == 0) {
+ ua->send_msg(_("Invalid time, using current time.\n"));
+ jcr->sched_time = time(NULL);
+ }
+ rc.when = NULL;
+ }
+
+ if (rc.bootstrap) {
+ if (jcr->RestoreBootstrap) {
+ free(jcr->RestoreBootstrap);
+ }
+ jcr->RestoreBootstrap = bstrdup(rc.bootstrap);
+ rc.bootstrap = NULL;
+ }
+
+ if (rc.plugin_options) {
+ if (jcr->plugin_options) {
+ free(jcr->plugin_options);
+ }
+ jcr->plugin_options = bstrdup(rc.plugin_options);
+ rc.plugin_options = NULL;
+ }
+
+
+ if (rc.replace) {
+ jcr->replace = 0;
+ for (i=0; ReplaceOptions[i].name; i++) {
+ if (strcasecmp(rc.replace, ReplaceOptions[i].name) == 0) {
+ jcr->replace = ReplaceOptions[i].token;
+ }
+ }
+ if (!jcr->replace) {
+ ua->send_msg(_("Invalid replace option: %s\n"), rc.replace);
+ return false;
+ }
+ } else if (rc.job->replace) {
+ jcr->replace = rc.job->replace;
+ } else {
+ jcr->replace = REPLACE_ALWAYS;
+ }
+ rc.replace = NULL;
+
+ if (rc.Priority) {
+ jcr->JobPriority = rc.Priority;
+ rc.Priority = 0;
+ }
+
+ if (rc.since) {
+ if (!jcr->stime) {
+ jcr->stime = get_pool_memory(PM_MESSAGE);
+ }
+ pm_strcpy(jcr->stime, rc.since);
+ rc.since = NULL;
+ }
+
+ if (rc.cloned) {
+ jcr->cloned = rc.cloned;
+ rc.cloned = false;
+ }
+
+
+ /* If pool changed, update migration write storage */
+ if (jcr->JobType == JT_MIGRATE || jcr->JobType == JT_COPY) {
+ if (!set_migration_wstorage(jcr, rc.pool)) {
+ return false;
+ }
+ }
+ rc.replace = ReplaceOptions[0].name;
+ for (i=0; ReplaceOptions[i].name; i++) {
+ if (ReplaceOptions[i].token == jcr->replace) {
+ rc.replace = ReplaceOptions[i].name;
+ }
+ }
+ if (rc.level_name) {
+ if (!get_level_from_name(jcr, rc.level_name)) {
+ ua->send_msg(_("Level %s not valid.\n"), rc.level_name);
+ return false;
+ }
+ rc.level_name = NULL;
+ }
+ if (rc.jid) {
+ /* Note, this is also MigrateJobId */
+ jcr->RestoreJobId = str_to_int64(rc.jid);
+ rc.jid = 0;
+ }
+ return true;
}
static void select_where_regexp(UAContext *ua, JCR *jcr)
void *plugin_ctx; /* current plugin context */
Plugin *plugin; /* plugin instance */
save_pkt *plugin_sp; /* plugin save packet */
+ char *plugin_options; /* user set options for plugin */
/* Daemon specific part of JCR */
/* This should be empty in the library */
BSOCK *bs; /* used on Unix machines */
void *context; /* Win32 */
void (*callback)(const char *msg, int len, void *context); /* Win32 */
+
+ /* Methods */
+ STATUS_PKT() { memset(this, 0, sizeof(STATUS_PKT)); };
+ ~STATUS_PKT() { };
};
extern void output_status(STATUS_PKT *sp);
General:
05Mar08
+kes Fix bugs in MaxFullInterval and Implement MaxDiffInterval.
+kes Start PluginOptions string, and refactor a bit of ua_run.c
ebl Apply Allan patch that permit to reset recyclepool.
04Mar08
kes Test patch -- possible fix or improvement for bug #1053