diff --git a/src/console/console.c b/src/console/console.c index a8d5db59002..fcb08dcf170 100644 --- a/src/console/console.c +++ b/src/console/console.c @@ -586,24 +586,24 @@ struct cpl_keywords_t { }; static struct cpl_keywords_t cpl_keywords[] = { - {"pool=", ".pool" }, - {"fileset=", ".fileset" }, - {"client=", ".client" }, - {"job=", ".jobs" }, - {"restore_job=",".jobs type=R" }, - {"level=", ".level" }, - {"storage=", ".storage" }, - {"schedule=", ".schedule" }, - {"volume=", ".media" }, - {"oldvolume=", ".media" }, - {"volstatus=", ".volstatus" }, - {"ls", ".ls" }, - {"cd", ".lsdir" }, - {"mark", ".ls" }, - {"m", ".ls" }, - {"unmark", ".lsmark" }, - {"catalog=", ".catalogs" }, - {"actiononpurge=", ".actiononpurge" } + { "pool=", ".pool" }, + { "fileset=", ".fileset" }, + { "client=", ".client" }, + { "job=", ".jobs" }, + { "restore_job=",".jobs type=R" }, + { "level=", ".level" }, + { "storage=", ".storage" }, + { "schedule=", ".schedule" }, + { "volume=", ".media" }, + { "oldvolume=", ".media" }, + { "volstatus=", ".volstatus" }, + { "ls", ".ls" }, + { "cd", ".lsdir" }, + { "mark", ".ls" }, + { "m", ".ls" }, + { "unmark", ".lsmark" }, + { "catalog=", ".catalogs" }, + { "actiononpurge=", ".actiononpurge" } }; #define key_size ((int)(sizeof(cpl_keywords)/sizeof(struct cpl_keywords_t))) diff --git a/src/dird/protos.h b/src/dird/protos.h index 13f2bd0a3e8..2a9d76903d1 100644 --- a/src/dird/protos.h +++ b/src/dird/protos.h @@ -216,6 +216,7 @@ bool do_autochanger_volume_operation(UAContext *ua, STORERES *store, /* scheduler.c */ JCR *wait_for_next_job(char *one_shot_job_to_run); +bool is_doy_in_last_week(int year, int doy); void term_scheduler(); /* ua_acl.c */ diff --git a/src/dird/scheduler.c b/src/dird/scheduler.c index dad10f6b19a..81234c7a977 100644 --- a/src/dird/scheduler.c +++ b/src/dird/scheduler.c @@ -255,7 +255,7 @@ void term_scheduler() * depending if the year is leap year or not, the doy of the last day of the month * is varying one day. */ -static bool is_doy_in_last_week(int year, int doy) +bool is_doy_in_last_week(int year, int doy) { int i; int *last_dom; diff --git a/src/dird/ua_cmds.c b/src/dird/ua_cmds.c index 12f1e580261..74b1f758d6c 100644 --- a/src/dird/ua_cmds.c +++ b/src/dird/ua_cmds.c @@ -187,7 +187,8 @@ static struct cmdstruct commands[] = { "\twhen=\n" "\tcomment= yes"), false }, { NT_("status"), status_cmd, _("Report status"), - NT_("all | dir= | director | client= | storage= slots | days=nnn"), true }, + NT_("all | dir= | director | client= | storage= slots |\n" + "\tdays=nnn | scheduler | schedule="), true }, { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"), NT_("limit= client= jobid= job= ujobid="), true }, { NT_("setdebug"), setdebug_cmd, _("Sets debug level"), diff --git a/src/dird/ua_dotcmds.c b/src/dird/ua_dotcmds.c index 67a30742abe..a9df7633bd3 100644 --- a/src/dird/ua_dotcmds.c +++ b/src/dird/ua_dotcmds.c @@ -67,6 +67,7 @@ static bool locationscmd(UAContext *ua, const char *cmd); static bool mediacmd(UAContext *ua, const char *cmd); static bool aopcmd(UAContext *ua, const char *cmd); static bool catalogscmd(UAContext *ua, const char *cmd); +static bool schedulecmd(UAContext *ua, const char *cmd); static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd); static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd); @@ -107,6 +108,7 @@ static struct cmdstruct commands[] = { { NT_(".pools"), poolscmd, NULL, true }, { NT_(".quit"), dot_quit_cmd, NULL, false }, { NT_(".sql"), sql_cmd, NULL, false }, + { NT_(".schedule"), schedulecmd, NULL, false }, { NT_(".status"), dot_status_cmd, NULL, false }, { NT_(".storage"), storagecmd, NULL, true }, { NT_(".volstatus"), volstatuscmd, NULL, true }, @@ -1052,6 +1054,19 @@ static bool mediacmd(UAContext *ua, const char *cmd) return true; } +static bool schedulecmd(UAContext *ua, const char *cmd) +{ + SCHEDRES *sched; + + LockRes(); + foreach_res(sched, R_SCHEDULE) { + ua->send_msg("%s\n", sched->hdr.name); + } + UnlockRes(); + return true; +} + + static bool locationscmd(UAContext *ua, const char *cmd) { if (!open_client_db(ua)) { diff --git a/src/dird/ua_status.c b/src/dird/ua_status.c index 8617654ebbc..25433690260 100644 --- a/src/dird/ua_status.c +++ b/src/dird/ua_status.c @@ -35,6 +35,7 @@ static void list_scheduled_jobs(UAContext *ua); static void list_running_jobs(UAContext *ua); static void list_terminated_jobs(UAContext *ua); static void do_director_status(UAContext *ua); +static void do_scheduler_status(UAContext *ua); static void do_all_status(UAContext *ua); static void status_slots(UAContext *ua, STORERES *store); static void status_content(UAContext *ua, STORERES *store); @@ -148,12 +149,11 @@ int status_cmd(UAContext *ua, const char *cmd) Dmsg1(20, "status:%s:\n", cmd); - for (i=1; iargc; i++) { + for (i = 1; i < ua->argc; i++) { if (bstrcasecmp(ua->argk[i], NT_("all"))) { do_all_status(ua); return 1; - } else if (bstrcasecmp(ua->argk[i], NT_("dir")) || - bstrcasecmp(ua->argk[i], NT_("director"))) { + } else if (bstrncasecmp(ua->argk[i], NT_("dir"), 3)) { do_director_status(ua); return 1; } else if (bstrcasecmp(ua->argk[i], NT_("client"))) { @@ -171,6 +171,9 @@ int status_cmd(UAContext *ua, const char *cmd) } } return 1; + } else if (bstrncasecmp(ua->argk[i], NT_("sched"), 5)) { + do_scheduler_status(ua); + return 1; } else { store = get_storage_resource(ua, false/*no default*/); if (store) { @@ -202,7 +205,7 @@ int status_cmd(UAContext *ua, const char *cmd) } /* If no args, ask for status type */ if (ua->argc == 1) { - char prmt[MAX_NAME_LENGTH]; + char prmt[MAX_NAME_LENGTH]; start_prompt(ua, _("Status available for:\n")); add_prompt(ua, NT_("Director")); @@ -382,6 +385,151 @@ void list_dir_status_header(UAContext *ua) } } +static void do_scheduler_status(UAContext *ua) +{ + int i, hour, mday, wday, month, wom, woy, yday; + int days = 14; /* Default days for preview */ + bool is_last_week = false, /* Are we in the last week of a month? */ + schedulegiven = false; + char dt[MAX_TIME_LENGTH]; + time_t time_to_check, now, runtime; + char level[15]; + char schedulename[MAX_NAME_LENGTH]; + const int seconds_per_day = 86400; /* Number of seconds in one day */ + struct tm tm; + SCHEDRES *sched; + JOBRES *job; + RUNRES *run; + + now = time(NULL); /* Initialize to now */ + time_to_check = now; + + i = find_arg_with_value(ua, NT_("days")); + if (i >= 0) { + days = atoi(ua->argv[i]); + if (((days < 0) || (days > 366)) && !ua->api) { + ua->send_msg(_("Ignoring invalid value for days. Max is 366.\n")); + days = 14; + } + } + + /* + * Schedule given? + */ + i = find_arg_with_value(ua, NT_("schedule")); + if (i >= 0) { + bstrncpy(schedulename, ua->argv[i], sizeof(schedulename)); + schedulegiven = true; + } + + ua->send_msg("Scheduler Jobs:\n\n"); + ua->send_msg("Schedule Jobs Triggered\n"); + ua->send_msg("===========================================================\n"); + + LockRes(); + foreach_res(sched, R_SCHEDULE) { + if (schedulegiven) { + if (!bstrcmp(sched->hdr.name, schedulename)) { + continue; + } + } + ua->send_msg("%s\n", sched->hdr.name); + foreach_res(job, R_JOB) { + if (job->schedule && bstrcmp(sched->hdr.name, job->schedule->hdr.name)) { + ua->send_msg(" %s\n", job->name()); + } + } + ua->send_msg("\n"); + } + UnlockRes(); + + ua->send_msg("====\n\n"); + ua->send_msg("Scheduler Preview for %d days:\n\n", days); + ua->send_msg("Date Schedule Overrides\n"); + ua->send_msg("==============================================================\n"); + + while (time_to_check < (now + (days * seconds_per_day))) { + (void)localtime_r(&time_to_check, &tm); + hour = tm.tm_hour; + mday = tm.tm_mday - 1; + wday = tm.tm_wday; + month = tm.tm_mon; + wom = mday / 7; + woy = tm_woy(time_to_check); /* Get week of year */ + yday = tm.tm_yday; /* Get day of year */ + + is_last_week = is_doy_in_last_week(tm.tm_year + 1900 , yday); + + LockRes(); + foreach_res(sched, R_SCHEDULE) { + if (schedulegiven) { + if (!bstrcmp(sched->hdr.name, schedulename)) { + continue; + } + } + + for (run = sched->run; run; run = run->next) { + bool run_now; + + run_now = bit_is_set(hour, run->hour) && + bit_is_set(mday, run->mday) && + bit_is_set(wday, run->wday) && + bit_is_set(month, run->month) && + (bit_is_set(wom, run->wom) || + (run->last_set && is_last_week)) && + bit_is_set(woy, run->woy); + + if (run_now) { + /* + * Find time (time_t) job is to be run + */ + (void)localtime_r(&time_to_check, &tm); /* Reset tm structure */ + tm.tm_min = run->minute; /* Set run minute */ + tm.tm_sec = 0; /* Zero secs */ + runtime = mktime(&tm); + bstrftime_wd(dt, sizeof(dt), runtime); + + ua->send_msg(dt); + ua->send_msg(" %-22.22s ", sched->hdr.name); + + bstrncpy(level, level_to_str(run->level), sizeof(level)); + ua->send_msg("Level=%s ", level); + + if (run->Priority) { + ua->send_msg("Priority=%d ", run->Priority); + } + + if (run->spool_data_set) { + ua->send_msg("Spool Data=%d ", run->spool_data); + } + + if (run->accurate_set) { + ua->send_msg("Accurate=%d ", run->accurate); + } + + if (run->pool) { + ua->send_msg("Pool=%s ", run->pool->name()); + } + + if (run->storage) { + ua->send_msg("Storage=%s ", run->storage->name()); + } + + if (run->msgs) { + ua->send_msg("Messages=%s ", run->msgs->name()); + } + + ua->send_msg("\n"); + } + } + } + UnlockRes(); + + time_to_check += 3600; /* next hour */ + } + ua->send_msg("====\n"); +} + static void do_director_status(UAContext *ua) { list_dir_status_header(ua); diff --git a/src/lib/btime.c b/src/lib/btime.c index 132100da259..5d2875017ae 100644 --- a/src/lib/btime.c +++ b/src/lib/btime.c @@ -79,6 +79,17 @@ char *bstrftime_ny(char *dt, int maxlen, utime_t utime) return dt; } +/* Formatted time for user display with weekday: weekday dd-Mon hh:mm */ +char *bstrftime_wd(char *dt, int maxlen, utime_t utime) +{ + time_t time = (time_t)utime; + struct tm tm; + + /* ***FIXME**** the format and localtime_r() should be user configurable */ + (void)localtime_r(&time, &tm); + strftime(dt, maxlen, "%a %d-%b %H:%M", &tm); + return dt; +} /* Formatted time for user display: dd-Mon-yy hh:mm (no century) */ char *bstrftime_nc(char *dt, int maxlen, utime_t utime) diff --git a/src/lib/btime.h b/src/lib/btime.h index fd9010680f7..f502c2b7b70 100644 --- a/src/lib/btime.h +++ b/src/lib/btime.h @@ -35,6 +35,7 @@ char *bstrftime(char *dt, int maxlen, utime_t tim); char *bstrftimes(char *dt, int maxlen, utime_t tim); char *bstrftime_ny(char *dt, int maxlen, utime_t tim); char *bstrftime_nc(char *dt, int maxlen, utime_t tim); +char *bstrftime_wd(char *dt, int maxlen, utime_t tim); utime_t str_to_utime(const char *str);