2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
31 * It looks at what jobs are to be run and when
32 * and waits around until it is time to
35 * Kern Sibbald, May MM, major revision December MMIII
50 const int dbglvl = DBGLVL;
58 dlink link; /* link for list */
61 /* List of jobs to be run. They were scheduled in this hour or the next */
62 static dlist *jobs_to_run; /* list of jobs to be run */
64 /* Time interval in secs to sleep if nothing to be run */
65 static int const next_check_secs = 60;
67 /* Forward referenced subroutines */
68 static void find_runs();
69 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
70 static void dump_job(job_item *ji, const char *msg);
72 /* Imported subroutines */
74 /* Imported variables */
77 * called by reload_config to tell us that the schedules
78 * we may have based our next jobs to run queues have been
79 * invalidated. In fact the schedules may not have changed
80 * but the run object that we have recorded the last_run time
81 * on are new and no longer have a valid last_run time which
82 * causes us to double run schedules that get put into the list
85 static bool schedules_invalidated = false;
86 void invalidate_schedules(void) {
87 schedules_invalidated = true;
90 /*********************************************************************
92 * Main Bacula Scheduler
95 JCR *wait_for_next_job(char *one_shot_job_to_run)
101 static bool first = true;
102 job_item *next_job = NULL;
104 Dmsg0(dbglvl, "Enter wait_for_next_job\n");
107 /* Create scheduled jobs list */
108 jobs_to_run = New(dlist(next_job, &next_job->link));
109 if (one_shot_job_to_run) { /* one shot */
110 job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
112 Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
114 Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
115 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
116 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 (but not daylight savings time changes)
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->setJobLevel(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;
222 store.store = run->storage;
223 pm_strcpy(store.store_source, _("run override"));
224 set_rwstorage(jcr, &store); /* override storage */
227 jcr->messages = run->msgs; /* override messages */
230 jcr->JobPriority = run->Priority;
232 if (run->spool_data_set) {
233 jcr->spool_data = run->spool_data;
235 if (run->accurate_set) { /* overwrite accurate mode */
236 jcr->accurate = run->accurate;
238 if (run->write_part_after_job_set) {
239 jcr->write_part_after_job = run->write_part_after_job;
241 if (run->MaxRunSchedTime_set) {
242 jcr->MaxRunSchedTime = run->MaxRunSchedTime;
244 Dmsg0(dbglvl, "Leave wait_for_next_job()\n");
250 * Shutdown the scheduler
252 void term_scheduler()
260 * Find all jobs to be run this hour and the next hour.
262 static void find_runs()
264 time_t now, next_hour, runtime;
269 int hour, mday, wday, month, wom, woy;
270 /* Items corresponding to above at the next hour */
271 int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy;
273 Dmsg0(dbglvl, "enter find_runs()\n");
275 /* compute values for time now */
277 (void)localtime_r(&now, &tm);
279 mday = tm.tm_mday - 1;
283 woy = tm_woy(now); /* get week of year */
285 Dmsg7(dbglvl, "now = %x: h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
286 now, hour, month, mday, wday, wom, woy);
289 * Compute values for next hour from now.
290 * We do this to be sure we don't miss a job while
293 next_hour = now + 3600;
294 (void)localtime_r(&next_hour, &tm);
295 nh_hour = tm.tm_hour;
296 nh_mday = tm.tm_mday - 1;
297 nh_wday = tm.tm_wday;
298 nh_month = tm.tm_mon;
299 nh_wom = nh_mday / 7;
300 nh_woy = tm_woy(next_hour); /* get week of year */
302 Dmsg7(dbglvl, "nh = %x: h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
303 next_hour, nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
305 /* Loop through all jobs */
307 foreach_res(job, R_JOB) {
308 sched = job->schedule;
309 if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
310 continue; /* no, skip this job */
312 Dmsg1(dbglvl, "Got job: %s\n", job->hdr.name);
313 for (run=sched->run; run; run=run->next) {
314 bool run_now, run_nh;
316 * Find runs scheduled between now and the next hour.
320 Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
321 hour, month, mday, wday, wom, woy);
322 Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
323 bit_is_set(hour, run->hour),
324 bit_is_set(month, run->month),
325 bit_is_set(mday, run->mday),
326 bit_is_set(wday, run->wday),
327 bit_is_set(wom, run->wom),
328 bit_is_set(woy, run->woy));
330 Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
331 nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
332 Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
333 bit_is_set(nh_hour, run->hour),
334 bit_is_set(nh_month, run->month),
335 bit_is_set(nh_mday, run->mday),
336 bit_is_set(nh_wday, run->wday),
337 bit_is_set(nh_wom, run->wom),
338 bit_is_set(nh_woy, run->woy));
341 run_now = bit_is_set(hour, run->hour) &&
342 bit_is_set(mday, run->mday) &&
343 bit_is_set(wday, run->wday) &&
344 bit_is_set(month, run->month) &&
345 bit_is_set(wom, run->wom) &&
346 bit_is_set(woy, run->woy);
348 run_nh = bit_is_set(nh_hour, run->hour) &&
349 bit_is_set(nh_mday, run->mday) &&
350 bit_is_set(nh_wday, run->wday) &&
351 bit_is_set(nh_month, run->month) &&
352 bit_is_set(nh_wom, run->wom) &&
353 bit_is_set(nh_woy, run->woy);
355 Dmsg3(dbglvl, "run@%p: run_now=%d run_nh=%d\n", run, run_now, run_nh);
357 if (run_now || run_nh) {
358 /* find time (time_t) job is to be run */
359 (void)localtime_r(&now, &tm); /* reset tm structure */
360 tm.tm_min = run->minute; /* set run minute */
361 tm.tm_sec = 0; /* zero secs */
362 runtime = mktime(&tm);
364 add_job(job, run, now, runtime);
366 /* If job is to be run in the next hour schedule it */
368 add_job(job, run, now, runtime + 3600);
374 Dmsg0(dbglvl, "Leave find_runs()\n");
377 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
380 bool inserted = false;
382 * Don't run any job that ran less than a minute ago, but
383 * do run any job scheduled less than a minute ago.
385 if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
387 Dmsg4(000, "Drop: Job=\"%s\" run=%lld. last_run=%lld. now=%lld\n", job->hdr.name,
388 (utime_t)runtime, (utime_t)run->last_run, (utime_t)now);
394 Dmsg4(000, "Add: Job=\"%s\" run=%lld last_run=%lld now=%lld\n", job->hdr.name,
395 (utime_t)runtime, (utime_t)run->last_run, (utime_t)now);
397 /* accept to run this job */
398 job_item *je = (job_item *)malloc(sizeof(job_item));
401 je->runtime = runtime;
403 je->Priority = run->Priority;
405 je->Priority = job->Priority;
408 /* Add this job to the wait queue in runtime, priority sorted order */
409 foreach_dlist(ji, jobs_to_run) {
410 if (ji->runtime > je->runtime ||
411 (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
412 jobs_to_run->insert_before(je, ji);
413 dump_job(je, _("Inserted job"));
418 /* If place not found in queue, append it */
420 jobs_to_run->append(je);
421 dump_job(je, _("Appended job"));
424 foreach_dlist(ji, jobs_to_run) {
425 dump_job(ji, _("Run queue"));
427 Dmsg0(000, "End run queue\n");
431 static void dump_job(job_item *ji, const char *msg)
434 char dt[MAX_TIME_LENGTH];
435 int save_debug = debug_level;
436 if (debug_level < dbglvl) {
439 bstrftime_nc(dt, sizeof(dt), ji->runtime);
440 Dmsg4(dbglvl, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name,
443 debug_level = save_debug;