2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
23 * It looks at what jobs are to be run and when
24 * and waits around until it is time to
27 * Kern Sibbald, May MM, major revision December MMIII
39 #define DBGLVL DT_SCHEDULER|200
42 const int dbglvl = DBGLVL;
50 dlink link; /* link for list */
53 /* List of jobs to be run. They were scheduled in this hour or the next */
54 static dlist *jobs_to_run; /* list of jobs to be run */
56 /* Time interval in secs to sleep if nothing to be run */
57 static int const next_check_secs = 60;
59 /* Forward referenced subroutines */
60 static void find_runs();
61 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
62 static void dump_job(job_item *ji, const char *msg);
64 /* Imported subroutines */
66 /* Imported variables */
69 * called by reload_config to tell us that the schedules
70 * we may have based our next jobs to run queues have been
71 * invalidated. In fact the schedules may not have changed
72 * but the run object that we have recorded the last_run time
73 * on are new and no longer have a valid last_run time which
74 * causes us to double run schedules that get put into the list
77 static bool schedules_invalidated = false;
78 void invalidate_schedules(void) {
79 schedules_invalidated = true;
82 /*********************************************************************
84 * Main Bacula Scheduler
87 JCR *wait_for_next_job(char *one_shot_job_to_run)
93 static bool first = true;
94 job_item *next_job = NULL;
96 Dmsg0(dbglvl, "Enter wait_for_next_job\n");
99 /* Create scheduled jobs list */
100 jobs_to_run = New(dlist(next_job, &next_job->link));
101 if (one_shot_job_to_run) { /* one shot */
102 job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
104 Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
106 Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
107 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
108 set_jcr_defaults(jcr, job);
113 /* Wait until we have something in the
117 while (jobs_to_run->empty()) {
119 if (!jobs_to_run->empty()) {
122 bmicrosleep(next_check_secs, 0); /* recheck once per minute */
127 foreach_dlist(je, jobs_to_run) {
128 dump_job(je, _("Walk queue"));
132 * Pull the first job to run (already sorted by runtime and
133 * Priority, then wait around until it is time to run it.
135 next_job = (job_item *)jobs_to_run->first();
136 jobs_to_run->remove(next_job);
138 dump_job(next_job, _("Dequeued job"));
140 if (!next_job) { /* we really should have something now */
141 Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
144 /* Now wait for the time to run the job */
147 /** discard scheduled queue and rebuild with new schedule objects. **/
149 if (schedules_invalidated) {
150 dump_job(next_job, "Invalidated job");
152 while (!jobs_to_run->empty()) {
153 next_job = (job_item *)jobs_to_run->first();
154 jobs_to_run->remove(next_job);
155 dump_job(next_job, "Invalidated job");
158 schedules_invalidated = false;
163 prev = now = time(NULL);
164 twait = next_job->runtime - now;
165 if (twait <= 0) { /* time to run it */
168 /* Recheck at least once per minute */
169 bmicrosleep((next_check_secs < twait)?next_check_secs:twait, 0);
170 /* Attempt to handle clock shift (but not daylight savings time changes)
171 * we allow a skew of 10 seconds before invalidating everything.
174 if (now < prev-10 || now > (prev+next_check_secs+10)) {
175 schedules_invalidated = true;
178 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
179 run = next_job->run; /* pick up needed values */
181 if (job->enabled && (!job->client || job->client->enabled)) {
182 dump_job(next_job, _("Run job")); /* no client and job enabled */
185 if (!job->enabled || (job->client && !job->client->enabled)) {
187 goto again; /* ignore this job */
189 run->last_run = now; /* mark as run now */
192 set_jcr_defaults(jcr, job);
194 jcr->setJobLevel(run->level); /* override run level */
197 jcr->pool = run->pool; /* override pool */
198 jcr->run_pool_override = true;
200 if (run->next_pool) {
201 jcr->next_pool = run->next_pool; /* override next pool */
202 jcr->run_next_pool_override = true;
204 if (run->full_pool) {
205 jcr->full_pool = run->full_pool; /* override full pool */
206 jcr->run_full_pool_override = true;
209 jcr->inc_pool = run->inc_pool; /* override inc pool */
210 jcr->run_inc_pool_override = true;
212 if (run->diff_pool) {
213 jcr->diff_pool = run->diff_pool; /* override dif pool */
214 jcr->run_diff_pool_override = true;
218 store.store = run->storage;
219 pm_strcpy(store.store_source, _("run override"));
220 set_rwstorage(jcr, &store); /* override storage */
223 jcr->messages = run->msgs; /* override messages */
226 jcr->JobPriority = run->Priority;
228 if (run->spool_data_set) {
229 jcr->spool_data = run->spool_data;
231 if (run->accurate_set) { /* overwrite accurate mode */
232 jcr->accurate = run->accurate;
234 if (run->write_part_after_job_set) {
235 jcr->write_part_after_job = run->write_part_after_job;
237 if (run->MaxRunSchedTime_set) {
238 jcr->MaxRunSchedTime = run->MaxRunSchedTime;
240 Dmsg0(dbglvl, "Leave wait_for_next_job()\n");
246 * Shutdown the scheduler
248 void term_scheduler()
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;
265 int hour, mday, wday, month, wom, woy, ldom;
266 /* Items corresponding to above at the next hour */
267 int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_ldom;
269 Dmsg0(dbglvl, "enter find_runs()\n");
271 /* compute values for time now */
273 (void)localtime_r(&now, &tm);
275 mday = tm.tm_mday - 1;
279 woy = tm_woy(now); /* get week of year */
280 ldom = tm_ldom(month, tm.tm_year + 1900);
282 Dmsg7(dbglvl, "now = %x: h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
283 now, hour, month, mday, wday, wom, woy);
286 * Compute values for next hour from now.
287 * We do this to be sure we don't miss a job while
290 next_hour = now + 3600;
291 (void)localtime_r(&next_hour, &tm);
292 nh_hour = tm.tm_hour;
293 nh_mday = tm.tm_mday - 1;
294 nh_wday = tm.tm_wday;
295 nh_month = tm.tm_mon;
296 nh_wom = nh_mday / 7;
297 nh_woy = tm_woy(next_hour); /* get week of year */
298 nh_ldom = tm_ldom(nh_month, tm.tm_year + 1900);
300 Dmsg7(dbglvl, "nh = %x: h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
301 next_hour, nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
303 /* Loop through all jobs */
305 foreach_res(job, R_JOB) {
306 sched = job->schedule;
307 if (!sched || !job->enabled || (sched && !sched->enabled) ||
308 (job->client && !job->client->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 Dmsg7(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d ldom=%d\n",
320 hour, month, mday, wday, wom, woy, ldom);
321 Dmsg7(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d bsldom=%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),
328 bit_is_set(31, run->mday));
331 Dmsg7(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d ldom=%d\n",
332 nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy, nh_ldom);
333 Dmsg7(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d bsldom=%d\n",
334 bit_is_set(nh_hour, run->hour),
335 bit_is_set(nh_month, run->month),
336 bit_is_set(nh_mday, run->mday),
337 bit_is_set(nh_wday, run->wday),
338 bit_is_set(nh_wom, run->wom),
339 bit_is_set(nh_woy, run->woy),
340 bit_is_set(31, run->mday));
343 run_now = bit_is_set(hour, run->hour) &&
344 ((bit_is_set(mday, run->mday) &&
345 bit_is_set(wday, run->wday) &&
346 bit_is_set(month, run->month) &&
347 bit_is_set(wom, run->wom) &&
348 bit_is_set(woy, run->woy)) ||
349 (bit_is_set(month, run->month) &&
350 bit_is_set(31, run->mday) && mday == ldom));
352 run_nh = bit_is_set(nh_hour, run->hour) &&
353 ((bit_is_set(nh_mday, run->mday) &&
354 bit_is_set(nh_wday, run->wday) &&
355 bit_is_set(nh_month, run->month) &&
356 bit_is_set(nh_wom, run->wom) &&
357 bit_is_set(nh_woy, run->woy)) ||
358 (bit_is_set(nh_month, run->month) &&
359 bit_is_set(31, run->mday) && nh_mday == nh_ldom));
361 Dmsg3(dbglvl, "run@%p: run_now=%d run_nh=%d\n", run, run_now, run_nh);
363 if (run_now || run_nh) {
364 /* find time (time_t) job is to be run */
365 (void)localtime_r(&now, &tm); /* reset tm structure */
366 tm.tm_min = run->minute; /* set run minute */
367 tm.tm_sec = 0; /* zero secs */
368 runtime = mktime(&tm);
370 add_job(job, run, now, runtime);
372 /* If job is to be run in the next hour schedule it */
374 add_job(job, run, now, runtime + 3600);
380 Dmsg0(dbglvl, "Leave find_runs()\n");
383 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
386 bool inserted = false;
388 * Don't run any job that ran less than a minute ago, but
389 * do run any job scheduled less than a minute ago.
391 if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
393 Dmsg4(000, "Drop: 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);
400 Dmsg4(000, "Add: Job=\"%s\" run=%lld last_run=%lld now=%lld\n", job->hdr.name,
401 (utime_t)runtime, (utime_t)run->last_run, (utime_t)now);
403 /* accept to run this job */
404 job_item *je = (job_item *)malloc(sizeof(job_item));
407 je->runtime = runtime;
409 je->Priority = run->Priority;
411 je->Priority = job->Priority;
414 /* Add this job to the wait queue in runtime, priority sorted order */
415 foreach_dlist(ji, jobs_to_run) {
416 if (ji->runtime > je->runtime ||
417 (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
418 jobs_to_run->insert_before(je, ji);
419 dump_job(je, _("Inserted job"));
424 /* If place not found in queue, append it */
426 jobs_to_run->append(je);
427 dump_job(je, _("Appended job"));
430 foreach_dlist(ji, jobs_to_run) {
431 dump_job(ji, _("Run queue"));
433 Dmsg0(000, "End run queue\n");
437 static void dump_job(job_item *ji, const char *msg)
440 char dt[MAX_TIME_LENGTH];
441 int64_t save_debug = debug_level;
443 if (!chk_dbglvl(dbglvl)) {
446 bstrftime_nc(dt, sizeof(dt), ji->runtime);
447 Dmsg4(dbglvl, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name,
450 debug_level = save_debug;