4 * It looks at what jobs are to be run and when
5 * and waits around until it is time to
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 */
51 static int num_runjobs; /* total jobs found by find_runs() */
52 static int rem_runjobs; /* jobs remaining to be processed */
53 static int max_runjobs; /* max jobs in runjobs array */
54 static RUNJOB *runjobs; /* array of jobs to be run */
57 /*********************************************************************
59 * Main Bacula Scheduler
62 JCR *wait_for_next_job(char *job_to_run)
67 time_t now, runtime, nexttime;
69 static int first = TRUE;
70 char dt[MAX_TIME_LENGTH];
72 Dmsg0(200, "Enter wait_for_next_job\n");
76 runjobs = (RUNJOB *) malloc(sizeof(RUNJOB) * max_runjobs);
79 if (job_to_run) { /* one shot */
80 job = (JOB *)GetResWithName(R_JOB, job_to_run);
82 Emsg1(M_ABORT, 0, _("Job %s not found\n"), job_to_run);
84 Dmsg1(5, "Found job_to_run %s\n", job_to_run);
85 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
86 set_jcr_defaults(jcr, job);
90 /* Wait until we have something in the
93 while (rem_runjobs == 0) {
95 if (rem_runjobs > 0) {
98 bmicrosleep(60, 0); /* recheck once per minute */
102 * Sort through what is to be run in the next
103 * two hours to find the first job to be run,
104 * then wait around until it is time.
108 nexttime = now + 60 * 60 * 24; /* a much later time */
110 bstrftime(dt, sizeof(dt), now);
111 Dmsg2(400, "jobs=%d. Now is %s\n", rem_runjobs, dt);
112 for (i=0; i<num_runjobs; i++) {
113 runtime = runjobs[i].runtime;
114 if (runtime > 0 && runtime < nexttime) { /* find minimum time job */
120 bstrftime(dt, sizeof(dt), runjobs[i].runtime);
121 Dmsg2(100, " %s run %s\n", dt, runjobs[i].job->hdr.name);
125 if (jobindex < 0) { /* we really should have something now */
126 Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
129 /* Now wait for the time to run the job */
133 twait = nexttime - now;
134 if (twait <= 0) { /* time to run it */
137 bmicrosleep(twait, 0);
139 run = runjobs[jobindex].run;
140 job = runjobs[jobindex].job;
141 runjobs[jobindex].runtime = 0; /* remove from list */
142 run->last_run = now; /* mark as run */
143 rem_runjobs--; /* decrement count of remaining jobs */
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 if (runjobs) { /* free allocated memory */
182 * Find all jobs to be run this hour
185 static void find_runs()
192 int hour, next_hour, minute, mday, wday, month, wpos;
194 Dmsg0(200, "enter find_runs()\n");
198 localtime_r(&now, &tm);
201 next_hour = hour + 1;
205 mday = tm.tm_mday - 1;
208 wpos = (tm.tm_mday - 1) / 7;
210 /* Loop through all jobs */
212 for (job=NULL; (job=(JOB *)GetNextRes(R_JOB, (RES *)job)); ) {
213 sched = job->schedule;
214 if (sched == NULL) { /* scheduled? */
215 continue; /* no, skip this job */
217 for (run=sched->run; run; run=run->next) {
219 /* Find runs scheduled in this our or in the
220 * next hour (we may be one second before the next hour).
222 if ((bit_is_set(hour, run->hour) || bit_is_set(next_hour, run->hour)) &&
223 (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) &&
224 bit_is_set(month, run->month) && bit_is_set(wpos, run->wpos)) {
226 /* find time (time_t) job is to be run */
227 localtime_r(&now, &tm);
228 tm.tm_min = run->minute;
230 if (bit_is_set(hour, run->hour)) {
231 runtime = mktime(&tm);
232 add_job(job, run, now, runtime);
234 if (bit_is_set(next_hour, run->hour)) {
236 if (tm.tm_hour > 23) {
239 runtime = mktime(&tm);
240 add_job(job, run, now, runtime);
247 rem_runjobs = num_runjobs;
248 Dmsg0(200, "Leave find_runs()\n");
251 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
254 * Don't run any job that ran less than a minute ago, but
255 * do run any job scheduled less than a minute ago.
257 if ((runtime - run->last_run < 61) || (runtime+59 < now)) {
261 /* Make sure array is big enough */
262 if (num_runjobs == max_runjobs) {
264 runjobs = (RUNJOB *)realloc(runjobs, sizeof(RUNJOB) * max_runjobs);
266 Emsg0(M_ABORT, 0, _("Out of memory\n"));
268 /* accept to run this job */
269 runjobs[num_runjobs].run = run;
270 runjobs[num_runjobs].job = job;
271 runjobs[num_runjobs++].runtime = runtime; /* when to run it */