2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 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 two of the GNU 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 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
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);
122 /* Wait until we have something in the
126 while (jobs_to_run->empty()) {
128 if (!jobs_to_run->empty()) {
131 bmicrosleep(next_check_secs, 0); /* recheck once per minute */
136 foreach_dlist(je, jobs_to_run) {
137 dump_job(je, _("Walk queue"));
141 * Pull the first job to run (already sorted by runtime and
142 * Priority, then wait around until it is time to run it.
144 next_job = (job_item *)jobs_to_run->first();
145 jobs_to_run->remove(next_job);
147 dump_job(next_job, _("Dequeued job"));
149 if (!next_job) { /* we really should have something now */
150 Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
153 /* Now wait for the time to run the job */
156 /** discard scheduled queue and rebuild with new schedule objects. **/
158 if (schedules_invalidated) {
159 dump_job(next_job, "Invalidated job");
161 while (!jobs_to_run->empty()) {
162 next_job = (job_item *)jobs_to_run->first();
163 jobs_to_run->remove(next_job);
164 dump_job(next_job, "Invalidated job");
167 schedules_invalidated = false;
172 prev = now = time(NULL);
173 twait = next_job->runtime - now;
174 if (twait <= 0) { /* time to run it */
177 /* Recheck at least once per minute */
178 bmicrosleep((next_check_secs < twait)?next_check_secs:twait, 0);
179 /* Attempt to handle clock shift (but not daylight savings time changes)
180 * we allow a skew of 10 seconds before invalidating everything.
183 if (now < prev-10 || now > (prev+next_check_secs+10)) {
184 schedules_invalidated = true;
187 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
188 run = next_job->run; /* pick up needed values */
191 dump_job(next_job, _("Run job"));
196 goto again; /* ignore this job */
198 run->last_run = now; /* mark as run now */
201 set_jcr_defaults(jcr, job);
203 jcr->set_JobLevel(run->level); /* override run level */
206 jcr->pool = run->pool; /* override pool */
207 jcr->run_pool_override = true;
209 if (run->full_pool) {
210 jcr->full_pool = run->full_pool; /* override full pool */
211 jcr->run_full_pool_override = true;
214 jcr->inc_pool = run->inc_pool; /* override inc pool */
215 jcr->run_inc_pool_override = true;
217 if (run->diff_pool) {
218 jcr->diff_pool = run->diff_pool; /* override dif pool */
219 jcr->run_diff_pool_override = true;
223 store.store = run->storage;
224 pm_strcpy(store.store_source, _("run override"));
225 set_rwstorage(jcr, &store); /* override storage */
228 jcr->messages = run->msgs; /* override messages */
231 jcr->JobPriority = run->Priority;
233 if (run->spool_data_set) {
234 jcr->spool_data = run->spool_data;
236 if (run->write_part_after_job_set) {
237 jcr->write_part_after_job = run->write_part_after_job;
239 Dmsg0(dbglvl, "Leave wait_for_next_job()\n");
245 * Shutdown the scheduler
247 void term_scheduler()
255 * Find all jobs to be run this hour and the next hour.
257 static void find_runs()
259 time_t now, next_hour, runtime;
265 int hour, mday, wday, month, wom, woy;
266 /* Items corresponding to above at the next hour */
267 int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
269 Dmsg0(dbglvl, "enter find_runs()\n");
272 /* compute values for time now */
274 (void)localtime_r(&now, &tm);
277 mday = tm.tm_mday - 1;
281 woy = tm_woy(now); /* get week of year */
283 Dmsg7(dbglvl, "now = %x: h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
284 now, hour, month, mday, wday, wom, woy);
287 * Compute values for next hour from now.
288 * We do this to be sure we don't miss a job while
291 next_hour = now + 3600;
292 (void)localtime_r(&next_hour, &tm);
293 nh_hour = tm.tm_hour;
294 nh_mday = tm.tm_mday - 1;
295 nh_wday = tm.tm_wday;
296 nh_month = tm.tm_mon;
297 nh_year = tm.tm_year;
298 nh_wom = nh_mday / 7;
299 nh_woy = tm_woy(now); /* get week of year */
301 Dmsg7(dbglvl, "nh = %x: h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
302 next_hour, nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
304 /* Loop through all jobs */
306 foreach_res(job, R_JOB) {
307 sched = job->schedule;
308 if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
309 continue; /* no, skip this job */
311 Dmsg1(dbglvl, "Got job: %s\n", job->hdr.name);
312 for (run=sched->run; run; run=run->next) {
313 bool run_now, run_nh;
315 * Find runs scheduled between now and the next hour.
319 Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
320 hour, month, mday, wday, wom, woy);
321 Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
322 bit_is_set(hour, run->hour),
323 bit_is_set(month, run->month),
324 bit_is_set(mday, run->mday),
325 bit_is_set(wday, run->wday),
326 bit_is_set(wom, run->wom),
327 bit_is_set(woy, run->woy));
329 Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
330 nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
331 Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
332 bit_is_set(nh_hour, run->hour),
333 bit_is_set(nh_month, run->month),
334 bit_is_set(nh_mday, run->mday),
335 bit_is_set(nh_wday, run->wday),
336 bit_is_set(nh_wom, run->wom),
337 bit_is_set(nh_woy, run->woy));
340 run_now = bit_is_set(hour, run->hour) &&
341 bit_is_set(mday, run->mday) &&
342 bit_is_set(wday, run->wday) &&
343 bit_is_set(month, run->month) &&
344 bit_is_set(wom, run->wom) &&
345 bit_is_set(woy, run->woy);
347 run_nh = bit_is_set(nh_hour, run->hour) &&
348 bit_is_set(nh_mday, run->mday) &&
349 bit_is_set(nh_wday, run->wday) &&
350 bit_is_set(nh_month, run->month) &&
351 bit_is_set(nh_wom, run->wom) &&
352 bit_is_set(nh_woy, run->woy);
354 Dmsg3(dbglvl, "run@%p: run_now=%d run_nh=%d\n", run, run_now, run_nh);
356 if (run_now || run_nh) {
357 /* find time (time_t) job is to be run */
358 (void)localtime_r(&now, &tm); /* reset tm structure */
359 tm.tm_min = run->minute; /* set run minute */
360 tm.tm_sec = 0; /* zero secs */
361 runtime = mktime(&tm);
363 add_job(job, run, now, runtime);
365 /* If job is to be run in the next hour schedule it */
367 add_job(job, run, now, runtime + 3600);
373 Dmsg0(dbglvl, "Leave find_runs()\n");
376 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
379 bool inserted = false;
381 * Don't run any job that ran less than a minute ago, but
382 * do run any job scheduled less than a minute ago.
384 if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
386 Dmsg4(000, "Drop: Job=\"%s\" run=%lld. last_run=%lld. now=%lld\n", job->hdr.name,
387 (utime_t)runtime, (utime_t)run->last_run, (utime_t)now);
393 Dmsg4(000, "Add: Job=\"%s\" run=%lld last_run=%lld now=%lld\n", job->hdr.name,
394 (utime_t)runtime, (utime_t)run->last_run, (utime_t)now);
396 /* accept to run this job */
397 job_item *je = (job_item *)malloc(sizeof(job_item));
400 je->runtime = runtime;
402 je->Priority = run->Priority;
404 je->Priority = job->Priority;
407 /* Add this job to the wait queue in runtime, priority sorted order */
408 foreach_dlist(ji, jobs_to_run) {
409 if (ji->runtime > je->runtime ||
410 (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
411 jobs_to_run->insert_before(je, ji);
412 dump_job(je, _("Inserted job"));
417 /* If place not found in queue, append it */
419 jobs_to_run->append(je);
420 dump_job(je, _("Appended job"));
423 foreach_dlist(ji, jobs_to_run) {
424 dump_job(ji, _("Run queue"));
426 Dmsg0(000, "End run queue\n");
430 static void dump_job(job_item *ji, const char *msg)
433 char dt[MAX_TIME_LENGTH];
434 int save_debug = debug_level;
435 if (debug_level < dbglvl) {
438 bstrftime_nc(dt, sizeof(dt), ji->runtime);
439 Dmsg4(dbglvl, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name,
442 debug_level = save_debug;