4 * It looks at what jobs are to be run and when
5 * and waits around until it is time to
8 * Kern Sibbald, May MM, major revision December MMIII
13 Copyright (C) 2000-2006 Kern Sibbald
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License
17 version 2 as amended with additional clauses defined in the
18 file LICENSE in the main source directory.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 the file LICENSE for additional details.
38 const int dbglvl = DBGLVL;
46 dlink link; /* link for list */
49 /* List of jobs to be run. They were scheduled in this hour or the next */
50 static dlist *jobs_to_run; /* list of jobs to be run */
52 /* Time interval in secs to sleep if nothing to be run */
53 static int const next_check_secs = 60;
55 /* Forward referenced subroutines */
56 static void find_runs();
57 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
58 static void dump_job(job_item *ji, const char *msg);
60 /* Imported subroutines */
62 /* Imported variables */
65 * called by reload_config to tell us that the schedules
66 * we may have based our next jobs to run queues have been
67 * invalidated. In fact the schedules may not have changed
68 * but the run object that we have recorded the last_run time
69 * on are new and no longer have a valid last_run time which
70 * causes us to double run schedules that get put into the list
73 static bool schedules_invalidated = false;
74 void invalidate_schedules(void) {
75 schedules_invalidated = true;
78 /*********************************************************************
80 * Main Bacula Scheduler
83 JCR *wait_for_next_job(char *one_shot_job_to_run)
89 static bool first = true;
90 job_item *next_job = NULL;
92 Dmsg0(dbglvl, "Enter wait_for_next_job\n");
95 /* Create scheduled jobs list */
96 jobs_to_run = New(dlist(next_job, &next_job->link));
97 if (one_shot_job_to_run) { /* one shot */
98 job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
100 Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
102 Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
103 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
104 set_jcr_defaults(jcr, job);
108 /* Wait until we have something in the
112 while (jobs_to_run->empty()) {
114 if (!jobs_to_run->empty()) {
117 bmicrosleep(next_check_secs, 0); /* recheck once per minute */
122 foreach_dlist(je, jobs_to_run) {
123 dump_job(je, _("Walk queue"));
127 * Pull the first job to run (already sorted by runtime and
128 * Priority, then wait around until it is time to run it.
130 next_job = (job_item *)jobs_to_run->first();
131 jobs_to_run->remove(next_job);
133 dump_job(next_job, _("Dequeued job"));
135 if (!next_job) { /* we really should have something now */
136 Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
139 /* Now wait for the time to run the job */
142 /** discard scheduled queue and rebuild with new schedule objects. **/
144 if (schedules_invalidated) {
145 dump_job(next_job, "Invalidated job");
147 while (!jobs_to_run->empty()) {
148 next_job = (job_item *)jobs_to_run->first();
149 jobs_to_run->remove(next_job);
150 dump_job(next_job, "Invalidated job");
153 schedules_invalidated = false;
157 prev = now = time(NULL);
158 twait = next_job->runtime - now;
159 if (twait <= 0) { /* time to run it */
162 /* Recheck at least once per minute */
163 bmicrosleep((next_check_secs < twait)?next_check_secs:twait, 0);
164 /* Attempt to handle clock shift from/to daylight savings time
165 * we allow a skew of 10 seconds before invalidating everything.
168 if (now < prev+10 || now > (prev+next_check_secs+10)) {
169 schedules_invalidated = true;
172 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
173 run = next_job->run; /* pick up needed values */
176 dump_job(next_job, _("Run job"));
181 goto again; /* ignore this job */
183 run->last_run = now; /* mark as run now */
186 set_jcr_defaults(jcr, job);
188 jcr->JobLevel = run->level; /* override run level */
191 jcr->pool = run->pool; /* override pool */
193 if (run->full_pool) {
194 jcr->full_pool = run->full_pool; /* override full pool */
197 jcr->inc_pool = run->inc_pool; /* override inc pool */
200 jcr->dif_pool = run->dif_pool; /* override dif pool */
203 set_storage(jcr, run->storage); /* override storage */
206 jcr->messages = run->msgs; /* override messages */
209 jcr->JobPriority = run->Priority;
211 if (run->spool_data_set) {
212 jcr->spool_data = run->spool_data;
214 if (run->write_part_after_job_set) {
215 jcr->write_part_after_job = run->write_part_after_job;
217 Dmsg0(dbglvl, "Leave wait_for_next_job()\n");
223 * Shutdown the scheduler
225 void term_scheduler()
229 /* Release all queued job entries to be run */
230 foreach_dlist(je, jobs_to_run) {
238 * Find all jobs to be run this hour and the next hour.
240 static void find_runs()
242 time_t now, next_hour, runtime;
248 int hour, mday, wday, month, wom, woy;
249 /* Items corresponding to above at the next hour */
250 int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
252 Dmsg0(dbglvl, "enter find_runs()\n");
255 /* compute values for time now */
257 localtime_r(&now, &tm);
260 mday = tm.tm_mday - 1;
264 woy = tm_woy(now); /* get week of year */
267 * Compute values for next hour from now.
268 * We do this to be sure we don't miss a job while
271 next_hour = now + 3600;
272 localtime_r(&next_hour, &tm);
273 nh_hour = tm.tm_hour;
274 nh_mday = tm.tm_mday - 1;
275 nh_wday = tm.tm_wday;
276 nh_month = tm.tm_mon;
277 nh_year = tm.tm_year;
278 nh_wom = nh_mday / 7;
279 nh_woy = tm_woy(now); /* get week of year */
281 /* Loop through all jobs */
283 foreach_res(job, R_JOB) {
284 sched = job->schedule;
285 if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
286 continue; /* no, skip this job */
288 Dmsg1(dbglvl, "Got job: %s\n", job->hdr.name);
289 for (run=sched->run; run; run=run->next) {
290 bool run_now, run_nh;
292 * Find runs scheduled between now and the next hour.
296 Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
297 hour, month, mday, wday, wom, woy);
298 Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
299 bit_is_set(hour, run->hour),
300 bit_is_set(month, run->month),
301 bit_is_set(mday, run->mday),
302 bit_is_set(wday, run->wday),
303 bit_is_set(wom, run->wom),
304 bit_is_set(woy, run->woy));
306 Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
307 nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
308 Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
309 bit_is_set(nh_hour, run->hour),
310 bit_is_set(nh_month, run->month),
311 bit_is_set(nh_mday, run->mday),
312 bit_is_set(nh_wday, run->wday),
313 bit_is_set(nh_wom, run->wom),
314 bit_is_set(nh_woy, run->woy));
317 run_now = bit_is_set(hour, run->hour) &&
318 bit_is_set(mday, run->mday) &&
319 bit_is_set(wday, run->wday) &&
320 bit_is_set(month, run->month) &&
321 bit_is_set(wom, run->wom) &&
322 bit_is_set(woy, run->woy);
324 run_nh = bit_is_set(nh_hour, run->hour) &&
325 bit_is_set(nh_mday, run->mday) &&
326 bit_is_set(nh_wday, run->wday) &&
327 bit_is_set(nh_month, run->month) &&
328 bit_is_set(nh_wom, run->wom) &&
329 bit_is_set(nh_woy, run->woy);
331 Dmsg3(dbglvl, "run@%p: run_now=%d run_nh=%d\n", run, run_now, run_nh);
333 /* find time (time_t) job is to be run */
334 localtime_r(&now, &tm); /* reset tm structure */
335 tm.tm_min = run->minute; /* set run minute */
336 tm.tm_sec = 0; /* zero secs */
338 runtime = mktime(&tm);
339 add_job(job, run, now, runtime);
341 /* If job is to be run in the next hour schedule it */
343 /* Set correct values */
344 tm.tm_hour = nh_hour;
345 tm.tm_mday = nh_mday + 1; /* fixup because we biased for tests above */
346 tm.tm_mon = nh_month;
347 tm.tm_year = nh_year;
348 runtime = mktime(&tm);
349 add_job(job, run, now, runtime);
354 Dmsg0(dbglvl, "Leave find_runs()\n");
357 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
360 bool inserted = false;
362 * Don't run any job that ran less than a minute ago, but
363 * do run any job scheduled less than a minute ago.
365 if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
367 char dt[50], dt1[50], dt2[50];
368 bstrftime_nc(dt, sizeof(dt), runtime);
369 bstrftime_nc(dt1, sizeof(dt1), run->last_run);
370 bstrftime_nc(dt2, sizeof(dt2), now);
371 Dmsg7(000, "Drop: Job=\"%s\" run=%s(%x). last_run=%s(%x). now=%s(%x)\n", job->hdr.name,
372 dt, runtime, dt1, run->last_run, dt2, now);
378 Dmsg4(000, "Add: Job=\"%s\" run=%x last_run=%x now=%x\n", job->hdr.name,
379 runtime, run->last_run, now);
381 /* accept to run this job */
382 job_item *je = (job_item *)malloc(sizeof(job_item));
385 je->runtime = runtime;
387 je->Priority = run->Priority;
389 je->Priority = job->Priority;
392 /* Add this job to the wait queue in runtime, priority sorted order */
393 foreach_dlist(ji, jobs_to_run) {
394 if (ji->runtime > je->runtime ||
395 (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
396 jobs_to_run->insert_before(je, ji);
397 dump_job(je, _("Inserted job"));
402 /* If place not found in queue, append it */
404 jobs_to_run->append(je);
405 dump_job(je, _("Appended job"));
408 foreach_dlist(ji, jobs_to_run) {
409 dump_job(ji, _("Run queue"));
411 Dmsg0(000, "End run queue\n");
415 static void dump_job(job_item *ji, const char *msg)
418 char dt[MAX_TIME_LENGTH];
419 int save_debug = debug_level;
420 if (debug_level < dbglvl) {
423 bstrftime_nc(dt, sizeof(dt), ji->runtime);
424 Dmsg4(dbglvl, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name,
427 debug_level = save_debug;