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;
158 prev = now = time(NULL);
159 twait = next_job->runtime - now;
160 if (twait <= 0) { /* time to run it */
163 /* Recheck at least once per minute */
164 bmicrosleep((next_check_secs < twait)?next_check_secs:twait, 0);
165 /* Attempt to handle clock shift from/to daylight savings time
166 * we allow a skew of 10 seconds before invalidating everything.
169 if (now < prev+10 || now > (prev+next_check_secs+10)) {
170 schedules_invalidated = true;
173 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
174 run = next_job->run; /* pick up needed values */
177 dump_job(next_job, _("Run job"));
182 goto again; /* ignore this job */
184 run->last_run = now; /* mark as run now */
187 set_jcr_defaults(jcr, job);
189 jcr->JobLevel = run->level; /* override run level */
192 jcr->pool = run->pool; /* override pool */
194 if (run->full_pool) {
195 jcr->full_pool = run->full_pool; /* override full pool */
198 jcr->inc_pool = run->inc_pool; /* override inc pool */
201 jcr->dif_pool = run->dif_pool; /* override dif pool */
204 set_storage(jcr, run->storage); /* override storage */
207 jcr->messages = run->msgs; /* override messages */
210 jcr->JobPriority = run->Priority;
212 if (run->spool_data_set) {
213 jcr->spool_data = run->spool_data;
215 if (run->write_part_after_job_set) {
216 jcr->write_part_after_job = run->write_part_after_job;
218 Dmsg0(dbglvl, "Leave wait_for_next_job()\n");
224 * Shutdown the scheduler
226 void term_scheduler()
230 /* Release all queued job entries to be run */
231 foreach_dlist(je, jobs_to_run) {
239 * Find all jobs to be run this hour and the next hour.
241 static void find_runs()
243 time_t now, next_hour, runtime;
249 int hour, mday, wday, month, wom, woy;
250 /* Items corresponding to above at the next hour */
251 int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
253 Dmsg0(dbglvl, "enter find_runs()\n");
256 /* compute values for time now */
258 localtime_r(&now, &tm);
261 mday = tm.tm_mday - 1;
265 woy = tm_woy(now); /* get week of year */
268 * Compute values for next hour from now.
269 * We do this to be sure we don't miss a job while
272 next_hour = now + 3600;
273 localtime_r(&next_hour, &tm);
274 nh_hour = tm.tm_hour;
275 nh_mday = tm.tm_mday - 1;
276 nh_wday = tm.tm_wday;
277 nh_month = tm.tm_mon;
278 nh_year = tm.tm_year;
279 nh_wom = nh_mday / 7;
280 nh_woy = tm_woy(now); /* get week of year */
282 /* Loop through all jobs */
284 foreach_res(job, R_JOB) {
285 sched = job->schedule;
286 if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
287 continue; /* no, skip this job */
289 Dmsg1(dbglvl, "Got job: %s\n", job->hdr.name);
290 for (run=sched->run; run; run=run->next) {
291 bool run_now, run_nh;
293 * Find runs scheduled between now and the next hour.
297 Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
298 hour, month, mday, wday, wom, woy);
299 Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
300 bit_is_set(hour, run->hour),
301 bit_is_set(month, run->month),
302 bit_is_set(mday, run->mday),
303 bit_is_set(wday, run->wday),
304 bit_is_set(wom, run->wom),
305 bit_is_set(woy, run->woy));
307 Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
308 nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
309 Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
310 bit_is_set(nh_hour, run->hour),
311 bit_is_set(nh_month, run->month),
312 bit_is_set(nh_mday, run->mday),
313 bit_is_set(nh_wday, run->wday),
314 bit_is_set(nh_wom, run->wom),
315 bit_is_set(nh_woy, run->woy));
318 run_now = bit_is_set(hour, run->hour) &&
319 bit_is_set(mday, run->mday) &&
320 bit_is_set(wday, run->wday) &&
321 bit_is_set(month, run->month) &&
322 bit_is_set(wom, run->wom) &&
323 bit_is_set(woy, run->woy);
325 run_nh = bit_is_set(nh_hour, run->hour) &&
326 bit_is_set(nh_mday, run->mday) &&
327 bit_is_set(nh_wday, run->wday) &&
328 bit_is_set(nh_month, run->month) &&
329 bit_is_set(nh_wom, run->wom) &&
330 bit_is_set(nh_woy, run->woy);
332 Dmsg3(dbglvl, "run@%p: run_now=%d run_nh=%d\n", run, run_now, run_nh);
334 /* find time (time_t) job is to be run */
335 localtime_r(&now, &tm); /* reset tm structure */
336 tm.tm_min = run->minute; /* set run minute */
337 tm.tm_sec = 0; /* zero secs */
339 runtime = mktime(&tm);
340 add_job(job, run, now, runtime);
342 /* If job is to be run in the next hour schedule it */
344 /* Set correct values */
345 tm.tm_hour = nh_hour;
346 tm.tm_mday = nh_mday + 1; /* fixup because we biased for tests above */
347 tm.tm_mon = nh_month;
348 tm.tm_year = nh_year;
349 runtime = mktime(&tm);
350 add_job(job, run, now, runtime);
355 Dmsg0(dbglvl, "Leave find_runs()\n");
358 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
361 bool inserted = false;
363 * Don't run any job that ran less than a minute ago, but
364 * do run any job scheduled less than a minute ago.
366 if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
368 char dt[50], dt1[50], dt2[50];
369 bstrftime_nc(dt, sizeof(dt), runtime);
370 bstrftime_nc(dt1, sizeof(dt1), run->last_run);
371 bstrftime_nc(dt2, sizeof(dt2), now);
372 Dmsg7(000, "Drop: Job=\"%s\" run=%s(%x). last_run=%s(%x). now=%s(%x)\n", job->hdr.name,
373 dt, runtime, dt1, run->last_run, dt2, now);
379 Dmsg4(000, "Add: Job=\"%s\" run=%x last_run=%x now=%x\n", job->hdr.name,
380 runtime, run->last_run, now);
382 /* accept to run this job */
383 job_item *je = (job_item *)malloc(sizeof(job_item));
386 je->runtime = runtime;
388 je->Priority = run->Priority;
390 je->Priority = job->Priority;
393 /* Add this job to the wait queue in runtime, priority sorted order */
394 foreach_dlist(ji, jobs_to_run) {
395 if (ji->runtime > je->runtime ||
396 (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
397 jobs_to_run->insert_before(je, ji);
398 dump_job(je, _("Inserted job"));
403 /* If place not found in queue, append it */
405 jobs_to_run->append(je);
406 dump_job(je, _("Appended job"));
409 foreach_dlist(ji, jobs_to_run) {
410 dump_job(ji, _("Run queue"));
412 Dmsg0(000, "End run queue\n");
416 static void dump_job(job_item *ji, const char *msg)
419 char dt[MAX_TIME_LENGTH];
420 int save_debug = debug_level;
421 if (debug_level < dbglvl) {
424 bstrftime_nc(dt, sizeof(dt), ji->runtime);
425 Dmsg4(dbglvl, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name,
428 debug_level = save_debug;