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.
30 /* #define SCHED_DEBUG */
39 dlink link; /* link for list */
42 /* List of jobs to be run. They were scheduled in this hour or the next */
43 static dlist *jobs_to_run; /* list of jobs to be run */
45 /* Time interval in secs to sleep if nothing to be run */
46 static int const NEXT_CHECK_SECS = 60;
48 /* Forward referenced subroutines */
49 static void find_runs();
50 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
51 static void dump_job(job_item *ji, const char *msg);
53 /* Imported subroutines */
55 /* Imported variables */
58 /*********************************************************************
60 * Main Bacula Scheduler
63 JCR *wait_for_next_job(char *one_shot_job_to_run)
69 static bool first = true;
70 job_item *next_job = NULL;
72 Dmsg0(200, "Enter wait_for_next_job\n");
75 /* Create scheduled jobs list */
76 jobs_to_run = New(dlist(next_job, &next_job->link));
77 if (one_shot_job_to_run) { /* one shot */
78 job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
80 Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
82 Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
83 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
84 set_jcr_defaults(jcr, job);
88 /* Wait until we have something in the
92 while (jobs_to_run->empty()) {
94 if (!jobs_to_run->empty()) {
97 bmicrosleep(NEXT_CHECK_SECS, 0); /* recheck once per minute */
102 foreach_dlist(je, jobs_to_run) {
103 dump_job(je, _("Walk queue"));
107 * Pull the first job to run (already sorted by runtime and
108 * Priority, then wait around until it is time to run it.
110 next_job = (job_item *)jobs_to_run->first();
111 jobs_to_run->remove(next_job);
113 dump_job(next_job, _("Dequeued job"));
115 if (!next_job) { /* we really should have something now */
116 Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
119 /* Now wait for the time to run the job */
123 twait = next_job->runtime - now;
124 if (twait <= 0) { /* time to run it */
127 bmicrosleep(twait, 0);
129 run = next_job->run; /* pick up needed values */
132 dump_job(next_job, _("Run job"));
136 goto again; /* ignore this job */
138 run->last_run = now; /* mark as run now */
140 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
142 set_jcr_defaults(jcr, job);
144 jcr->JobLevel = run->level; /* override run level */
147 jcr->pool = run->pool; /* override pool */
149 if (run->full_pool) {
150 jcr->full_pool = run->full_pool; /* override full pool */
153 jcr->inc_pool = run->inc_pool; /* override inc pool */
156 jcr->dif_pool = run->dif_pool; /* override dif pool */
159 set_storage(jcr, run->storage); /* override storage */
162 jcr->messages = run->msgs; /* override messages */
165 jcr->JobPriority = run->Priority;
167 if (run->spool_data_set) {
168 jcr->spool_data = run->spool_data;
170 if (run->write_part_after_job_set) {
171 jcr->write_part_after_job = run->write_part_after_job;
173 Dmsg0(200, "Leave wait_for_next_job()\n");
179 * Shutdown the scheduler
181 void term_scheduler()
185 /* Release all queued job entries to be run */
186 foreach_dlist(je, jobs_to_run) {
194 * Find all jobs to be run this hour and the next hour.
196 static void find_runs()
198 time_t now, next_hour, runtime;
204 int hour, mday, wday, month, wom, woy;
205 /* Items corresponding to above at the next hour */
206 int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
208 Dmsg0(1200, "enter find_runs()\n");
211 /* compute values for time now */
213 localtime_r(&now, &tm);
216 mday = tm.tm_mday - 1;
220 woy = tm_woy(now); /* get week of year */
223 * Compute values for next hour from now.
224 * We do this to be sure we don't miss a job while
227 next_hour = now + 3600;
228 localtime_r(&next_hour, &tm);
229 nh_hour = tm.tm_hour;
230 nh_mday = tm.tm_mday - 1;
231 nh_wday = tm.tm_wday;
232 nh_month = tm.tm_mon;
233 nh_year = tm.tm_year;
234 nh_wom = nh_mday / 7;
235 nh_woy = tm_woy(now); /* get week of year */
237 /* Loop through all jobs */
239 foreach_res(job, R_JOB) {
240 sched = job->schedule;
241 if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
242 continue; /* no, skip this job */
244 Dmsg1(1200, "Got job: %s\n", job->hdr.name);
245 for (run=sched->run; run; run=run->next) {
246 bool run_now, run_nh;
248 * Find runs scheduled between now and the next hour.
252 Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
253 hour, month, mday, wday, wom, woy);
254 Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
255 bit_is_set(hour, run->hour),
256 bit_is_set(month, run->month),
257 bit_is_set(mday, run->mday),
258 bit_is_set(wday, run->wday),
259 bit_is_set(wom, run->wom),
260 bit_is_set(woy, run->woy));
262 Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
263 nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
264 Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
265 bit_is_set(nh_hour, run->hour),
266 bit_is_set(nh_month, run->month),
267 bit_is_set(nh_mday, run->mday),
268 bit_is_set(nh_wday, run->wday),
269 bit_is_set(nh_wom, run->wom),
270 bit_is_set(nh_woy, run->woy));
273 run_now = bit_is_set(hour, run->hour) &&
274 bit_is_set(mday, run->mday) &&
275 bit_is_set(wday, run->wday) &&
276 bit_is_set(month, run->month) &&
277 bit_is_set(wom, run->wom) &&
278 bit_is_set(woy, run->woy);
280 run_nh = bit_is_set(nh_hour, run->hour) &&
281 bit_is_set(nh_mday, run->mday) &&
282 bit_is_set(nh_wday, run->wday) &&
283 bit_is_set(nh_month, run->month) &&
284 bit_is_set(nh_wom, run->wom) &&
285 bit_is_set(nh_woy, run->woy);
287 Dmsg2(1200, "run_now=%d run_nh=%d\n", run_now, run_nh);
289 /* find time (time_t) job is to be run */
290 localtime_r(&now, &tm); /* reset tm structure */
291 tm.tm_min = run->minute; /* set run minute */
292 tm.tm_sec = 0; /* zero secs */
294 runtime = mktime(&tm);
295 add_job(job, run, now, runtime);
297 /* If job is to be run in the next hour schedule it */
299 /* Set correct values */
300 tm.tm_hour = nh_hour;
301 tm.tm_mday = nh_mday + 1; /* fixup because we biased for tests above */
302 tm.tm_mon = nh_month;
303 tm.tm_year = nh_year;
304 runtime = mktime(&tm);
305 add_job(job, run, now, runtime);
310 Dmsg0(1200, "Leave find_runs()\n");
313 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
316 bool inserted = false;
318 * Don't run any job that ran less than a minute ago, but
319 * do run any job scheduled less than a minute ago.
321 if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
323 char dt[50], dt1[50], dt2[50];
324 bstrftime_nc(dt, sizeof(dt), runtime);
325 bstrftime_nc(dt1, sizeof(dt1), run->last_run);
326 bstrftime_nc(dt2, sizeof(dt2), now);
327 Dmsg4(000, "Drop: Job=\"%s\" run=%s. last_run=%s. now=%s\n", job->hdr.name,
333 /* accept to run this job */
334 job_item *je = (job_item *)malloc(sizeof(job_item));
337 je->runtime = runtime;
339 je->Priority = run->Priority;
341 je->Priority = job->Priority;
344 /* Add this job to the wait queue in runtime, priority sorted order */
345 foreach_dlist(ji, jobs_to_run) {
346 if (ji->runtime > je->runtime ||
347 (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
348 jobs_to_run->insert_before(je, ji);
349 dump_job(je, _("Inserted job"));
354 /* If place not found in queue, append it */
356 jobs_to_run->append(je);
357 dump_job(je, _("Appended job"));
360 foreach_dlist(ji, jobs_to_run) {
361 dump_job(ji, _("Run queue"));
363 Dmsg0(000, "End run queue\n");
367 static void dump_job(job_item *ji, const char *msg)
370 char dt[MAX_TIME_LENGTH];
371 int save_debug = debug_level;
373 if (debug_level < 200) {
376 bstrftime_nc(dt, sizeof(dt), ji->runtime);
377 Dmsg4(200, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name,
380 debug_level = save_debug;