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, revised December MMIII
13 Copyright (C) 2000-2003 Kern Sibbald and John Walker
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation; either version 2 of
18 the License, or (at your option) any later version.
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 GNU
23 General Public License for more details.
25 You should have received a copy of the GNU General Public
26 License along with this program; if not, write to the Free
27 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
36 /* Forward referenced subroutines */
37 static void find_runs();
38 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
40 /* Imported subroutines */
42 /* Imported variables */
49 dlink link; /* link for list */
52 /* List of jobs to be run. They were scheduled in this hour or the next */
53 static dlist *jobs_to_run; /* list of jobs to be run */
55 /* Time interval in secs to sleep if nothing to be run */
56 #define NEXT_CHECK_SECS 60
58 /*********************************************************************
60 * Main Bacula Scheduler
63 JCR *wait_for_next_job(char *one_shot_job_to_run)
68 time_t now, runtime, nexttime;
69 static bool first = true;
70 char dt[MAX_TIME_LENGTH];
71 job_item *next_job = NULL;
73 Dmsg0(200, "Enter wait_for_next_job\n");
76 /* Create scheduled jobs list */
77 jobs_to_run = new dlist(next_job, &next_job->link);
78 if (one_shot_job_to_run) { /* one shot */
79 job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
81 Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
83 Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
84 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
85 set_jcr_defaults(jcr, job);
89 /* Wait until we have something in the
92 while (jobs_to_run->size() == 0) {
94 if (jobs_to_run->size() > 0) {
97 bmicrosleep(NEXT_CHECK_SECS, 0); /* recheck once per minute */
101 * Sort through what is to be run in the next
102 * two hours to find the first job to be run,
103 * then wait around until it is time.
107 nexttime = now + 60 * 60 * 24; /* a much later time */
108 bstrftime(dt, sizeof(dt), now);
109 Dmsg2(400, "jobs=%d. Now is %s\n", jobs_to_run->size(), dt);
111 for (job_item *je=NULL; (je=(job_item *)jobs_to_run->next(je)); ) {
112 runtime = je->runtime;
113 if (runtime > 0 && runtime < nexttime) { /* find minimum time job */
121 bstrftime(dt, sizeof(dt), next_job->runtime);
122 Dmsg2(100, " %s run %s\n", dt, next_job->job->hdr.name);
126 if (!next_job) { /* we really should have something now */
127 Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
130 /* Now wait for the time to run the job */
134 twait = nexttime - now;
135 if (twait <= 0) { /* time to run it */
138 bmicrosleep(twait, 0);
140 run = next_job->run; /* pick up needed values */
142 jobs_to_run->remove(next_job); /* remove from list */
143 run->last_run = now; /* mark as run */
145 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
147 set_jcr_defaults(jcr, job);
149 jcr->JobLevel = run->level; /* override run level */
152 jcr->pool = run->pool; /* override pool */
155 jcr->store = run->storage; /* override storage */
158 jcr->messages = run->msgs; /* override messages */
161 jcr->JobPriority = run->Priority;
163 Dmsg0(200, "Leave wait_for_next_job()\n");
169 * Shutdown the scheduler
171 void term_scheduler()
173 /* Release all queued job entries to be run */
174 for (void *je=NULL; (je=jobs_to_run->next(je)); ) {
181 * Find all jobs to be run this hour and the next hour.
183 static void find_runs()
185 time_t now, next_hour, runtime;
191 int hour, mday, wday, month, wom, woy;
192 /* Items corresponding to above at the next hour */
193 int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
195 Dmsg0(200, "enter find_runs()\n");
198 /* compute values for time now */
200 localtime_r(&now, &tm);
203 mday = tm.tm_mday - 1;
206 wom = tm_wom(tm.tm_mday, tm.tm_wday); /* get week of month */
207 woy = tm_woy(now); /* get week of year */
210 * Compute values for next hour from now.
211 * We do this to be sure we don't miss a job while
214 next_hour = now + 3600;
215 localtime_r(&next_hour, &tm);
216 nh_hour = tm.tm_hour;
218 nh_mday = tm.tm_mday - 1;
219 nh_wday = tm.tm_wday;
220 nh_month = tm.tm_mon;
221 nh_year = tm.tm_year;
222 nh_wom = tm_wom(tm.tm_mday, tm.tm_wday); /* get week of month */
223 nh_woy = tm_woy(now); /* get week of year */
225 /* Loop through all jobs */
227 for (job=NULL; (job=(JOB *)GetNextRes(R_JOB, (RES *)job)); ) {
228 sched = job->schedule;
229 if (sched == NULL) { /* scheduled? */
230 continue; /* no, skip this job */
232 for (run=sched->run; run; run=run->next) {
233 bool run_now, run_nh;
235 /* Find runs scheduled between now and the next
236 * check time (typically 60 seconds)
238 run_now = bit_is_set(hour, run->hour) &&
239 (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) &&
240 bit_is_set(month, run->month) &&
241 bit_is_set(wom, run->wom) &&
242 bit_is_set(woy, run->woy);
244 run_nh = bit_is_set(nh_hour, run->hour) &&
245 (bit_is_set(nh_mday, run->mday) || bit_is_set(nh_wday, run->wday)) &&
246 bit_is_set(nh_month, run->month) &&
247 bit_is_set(nh_wom, run->wom) &&
248 bit_is_set(nh_woy, run->woy);
250 /* find time (time_t) job is to be run */
251 localtime_r(&now, &tm); /* reset tm structure */
252 tm.tm_min = run->minute; /* set run minute */
253 tm.tm_sec = 0; /* zero secs */
255 runtime = mktime(&tm);
256 add_job(job, run, now, runtime);
258 /* If job is to be run in the next hour schedule it */
260 /* Set correct values */
261 tm.tm_hour = nh_hour;
262 tm.tm_mday = nh_mday;
263 tm.tm_mon = nh_month;
264 tm.tm_year = nh_year;
265 runtime = mktime(&tm);
266 add_job(job, run, now, runtime);
271 Dmsg0(200, "Leave find_runs()\n");
274 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
277 * Don't run any job that ran less than a minute ago, but
278 * do run any job scheduled less than a minute ago.
280 if ((runtime - run->last_run < 61) || (runtime+59 < now)) {
283 /* accept to run this job */
284 job_item *je = (job_item *)malloc(sizeof(job_item));
287 je->runtime = runtime;
288 /* ***FIXME**** (enhancement) insert by sorted runtime */
289 jobs_to_run->append(je);