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 Bacula® - The Network Backup Solution
15 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
17 The main author of Bacula is Kern Sibbald, with contributions from
18 many others, a complete list can be found in the file AUTHORS.
19 This program is Free Software; you can redistribute it and/or
20 modify it under the terms of version two of the GNU General Public
21 License as published by the Free Software Foundation plus additions
22 that are listed in the file LICENSE.
24 This program is distributed in the hope that it will be useful, but
25 WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to the Free Software
31 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
34 Bacula® is a registered trademark of John Walker.
35 The licensor of Bacula is the Free Software Foundation Europe
36 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
37 Switzerland, email:ftf@fsfeurope.org.
51 const int dbglvl = DBGLVL;
59 dlink link; /* link for list */
62 /* List of jobs to be run. They were scheduled in this hour or the next */
63 static dlist *jobs_to_run; /* list of jobs to be run */
65 /* Time interval in secs to sleep if nothing to be run */
66 static int const next_check_secs = 60;
68 /* Forward referenced subroutines */
69 static void find_runs();
70 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
71 static void dump_job(job_item *ji, const char *msg);
73 /* Imported subroutines */
75 /* Imported variables */
78 * called by reload_config to tell us that the schedules
79 * we may have based our next jobs to run queues have been
80 * invalidated. In fact the schedules may not have changed
81 * but the run object that we have recorded the last_run time
82 * on are new and no longer have a valid last_run time which
83 * causes us to double run schedules that get put into the list
86 static bool schedules_invalidated = false;
87 void invalidate_schedules(void) {
88 schedules_invalidated = true;
91 /*********************************************************************
93 * Main Bacula Scheduler
96 JCR *wait_for_next_job(char *one_shot_job_to_run)
102 static bool first = true;
103 job_item *next_job = NULL;
105 Dmsg0(dbglvl, "Enter wait_for_next_job\n");
108 /* Create scheduled jobs list */
109 jobs_to_run = New(dlist(next_job, &next_job->link));
110 if (one_shot_job_to_run) { /* one shot */
111 job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
113 Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
115 Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
116 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
117 set_jcr_defaults(jcr, job);
121 /* Wait until we have something in the
125 while (jobs_to_run->empty()) {
127 if (!jobs_to_run->empty()) {
130 bmicrosleep(next_check_secs, 0); /* recheck once per minute */
135 foreach_dlist(je, jobs_to_run) {
136 dump_job(je, _("Walk queue"));
140 * Pull the first job to run (already sorted by runtime and
141 * Priority, then wait around until it is time to run it.
143 next_job = (job_item *)jobs_to_run->first();
144 jobs_to_run->remove(next_job);
146 dump_job(next_job, _("Dequeued job"));
148 if (!next_job) { /* we really should have something now */
149 Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
152 /* Now wait for the time to run the job */
155 /** discard scheduled queue and rebuild with new schedule objects. **/
157 if (schedules_invalidated) {
158 dump_job(next_job, "Invalidated job");
160 while (!jobs_to_run->empty()) {
161 next_job = (job_item *)jobs_to_run->first();
162 jobs_to_run->remove(next_job);
163 dump_job(next_job, "Invalidated job");
166 schedules_invalidated = false;
171 prev = now = time(NULL);
172 twait = next_job->runtime - now;
173 if (twait <= 0) { /* time to run it */
176 /* Recheck at least once per minute */
177 bmicrosleep((next_check_secs < twait)?next_check_secs:twait, 0);
178 /* Attempt to handle clock shift from/to daylight savings time
179 * we allow a skew of 10 seconds before invalidating everything.
182 if (now < prev+10 || now > (prev+next_check_secs+10)) {
183 schedules_invalidated = true;
186 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
187 run = next_job->run; /* pick up needed values */
190 dump_job(next_job, _("Run job"));
195 goto again; /* ignore this job */
197 run->last_run = now; /* mark as run now */
200 set_jcr_defaults(jcr, job);
202 jcr->JobLevel = run->level; /* override run level */
205 jcr->pool = run->pool; /* override pool */
206 jcr->run_pool_override = true;
208 if (run->full_pool) {
209 jcr->full_pool = run->full_pool; /* override full pool */
210 jcr->run_full_pool_override = true;
213 jcr->inc_pool = run->inc_pool; /* override inc pool */
214 jcr->run_inc_pool_override = true;
216 if (run->diff_pool) {
217 jcr->diff_pool = run->diff_pool; /* override dif pool */
218 jcr->run_diff_pool_override = true;
221 set_rwstorage(jcr, run->storage); /* override storage */
224 jcr->messages = run->msgs; /* override messages */
227 jcr->JobPriority = run->Priority;
229 if (run->spool_data_set) {
230 jcr->spool_data = run->spool_data;
232 if (run->write_part_after_job_set) {
233 jcr->write_part_after_job = run->write_part_after_job;
235 Dmsg0(dbglvl, "Leave wait_for_next_job()\n");
241 * Shutdown the scheduler
243 void term_scheduler()
247 /* Release all queued job entries to be run */
248 foreach_dlist(je, jobs_to_run) {
256 * Find all jobs to be run this hour and the next hour.
258 static void find_runs()
260 time_t now, next_hour, runtime;
266 int hour, mday, wday, month, wom, woy;
267 /* Items corresponding to above at the next hour */
268 int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
270 Dmsg0(dbglvl, "enter find_runs()\n");
273 /* compute values for time now */
275 (void)localtime_r(&now, &tm);
278 mday = tm.tm_mday - 1;
282 woy = tm_woy(now); /* get week of year */
285 * Compute values for next hour from now.
286 * We do this to be sure we don't miss a job while
289 next_hour = now + 3600;
290 (void)localtime_r(&next_hour, &tm);
291 nh_hour = tm.tm_hour;
292 nh_mday = tm.tm_mday - 1;
293 nh_wday = tm.tm_wday;
294 nh_month = tm.tm_mon;
295 nh_year = tm.tm_year;
296 nh_wom = nh_mday / 7;
297 nh_woy = tm_woy(now); /* get week of year */
299 /* Loop through all jobs */
301 foreach_res(job, R_JOB) {
302 sched = job->schedule;
303 if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
304 continue; /* no, skip this job */
306 Dmsg1(dbglvl, "Got job: %s\n", job->hdr.name);
307 for (run=sched->run; run; run=run->next) {
308 bool run_now, run_nh;
310 * Find runs scheduled between now and the next hour.
314 Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
315 hour, month, mday, wday, wom, woy);
316 Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
317 bit_is_set(hour, run->hour),
318 bit_is_set(month, run->month),
319 bit_is_set(mday, run->mday),
320 bit_is_set(wday, run->wday),
321 bit_is_set(wom, run->wom),
322 bit_is_set(woy, run->woy));
324 Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
325 nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
326 Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
327 bit_is_set(nh_hour, run->hour),
328 bit_is_set(nh_month, run->month),
329 bit_is_set(nh_mday, run->mday),
330 bit_is_set(nh_wday, run->wday),
331 bit_is_set(nh_wom, run->wom),
332 bit_is_set(nh_woy, run->woy));
335 run_now = bit_is_set(hour, run->hour) &&
336 bit_is_set(mday, run->mday) &&
337 bit_is_set(wday, run->wday) &&
338 bit_is_set(month, run->month) &&
339 bit_is_set(wom, run->wom) &&
340 bit_is_set(woy, run->woy);
342 run_nh = bit_is_set(nh_hour, run->hour) &&
343 bit_is_set(nh_mday, run->mday) &&
344 bit_is_set(nh_wday, run->wday) &&
345 bit_is_set(nh_month, run->month) &&
346 bit_is_set(nh_wom, run->wom) &&
347 bit_is_set(nh_woy, run->woy);
349 Dmsg3(dbglvl, "run@%p: run_now=%d run_nh=%d\n", run, run_now, run_nh);
351 /* find time (time_t) job is to be run */
352 (void)localtime_r(&now, &tm); /* reset tm structure */
353 tm.tm_min = run->minute; /* set run minute */
354 tm.tm_sec = 0; /* zero secs */
356 runtime = mktime(&tm);
357 add_job(job, run, now, runtime);
359 /* If job is to be run in the next hour schedule it */
361 /* Set correct values */
362 tm.tm_hour = nh_hour;
363 tm.tm_mday = nh_mday + 1; /* fixup because we biased for tests above */
364 tm.tm_mon = nh_month;
365 tm.tm_year = nh_year;
366 runtime = mktime(&tm);
367 add_job(job, run, now, runtime);
372 Dmsg0(dbglvl, "Leave find_runs()\n");
375 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
378 bool inserted = false;
380 * Don't run any job that ran less than a minute ago, but
381 * do run any job scheduled less than a minute ago.
383 if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
385 char dt[50], dt1[50], dt2[50];
386 bstrftime_nc(dt, sizeof(dt), runtime);
387 bstrftime_nc(dt1, sizeof(dt1), run->last_run);
388 bstrftime_nc(dt2, sizeof(dt2), now);
389 Dmsg7(000, "Drop: Job=\"%s\" run=%s(%x). last_run=%s(%x). now=%s(%x)\n", job->hdr.name,
390 dt, runtime, dt1, run->last_run, dt2, now);
396 Dmsg4(000, "Add: Job=\"%s\" run=%x last_run=%x now=%x\n", job->hdr.name,
397 runtime, run->last_run, now);
399 /* accept to run this job */
400 job_item *je = (job_item *)malloc(sizeof(job_item));
403 je->runtime = runtime;
405 je->Priority = run->Priority;
407 je->Priority = job->Priority;
410 /* Add this job to the wait queue in runtime, priority sorted order */
411 foreach_dlist(ji, jobs_to_run) {
412 if (ji->runtime > je->runtime ||
413 (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
414 jobs_to_run->insert_before(je, ji);
415 dump_job(je, _("Inserted job"));
420 /* If place not found in queue, append it */
422 jobs_to_run->append(je);
423 dump_job(je, _("Appended job"));
426 foreach_dlist(ji, jobs_to_run) {
427 dump_job(ji, _("Run queue"));
429 Dmsg0(000, "End run queue\n");
433 static void dump_job(job_item *ji, const char *msg)
436 char dt[MAX_TIME_LENGTH];
437 int save_debug = debug_level;
438 if (debug_level < dbglvl) {
441 bstrftime_nc(dt, sizeof(dt), ji->runtime);
442 Dmsg4(dbglvl, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name,
445 debug_level = save_debug;