]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/scheduler.c
Define foreach_res() + work on jobdefs + bstrftime_nc
[bacula/bacula] / bacula / src / dird / scheduler.c
1 /*
2  *
3  *   Bacula scheduler
4  *     It looks at what jobs are to be run and when
5  *     and waits around until it is time to 
6  *     fire them up.
7  *
8  *     Kern Sibbald, May MM, major revision December MMIII
9  *
10  *   Version $Id$
11  */
12 /*
13    Copyright (C) 2000-2003 Kern Sibbald and John Walker
14
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.
19
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.
24
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,
28    MA 02111-1307, USA.
29
30  */
31
32 #include "bacula.h"
33 #include "dird.h"
34
35
36 /* Local variables */
37 struct job_item {  
38    RUN *run;
39    JOB *job;
40    time_t runtime;
41    int Priority;
42    dlink link;                        /* link for list */
43 };              
44
45 /* List of jobs to be run. They were scheduled in this hour or the next */
46 static dlist *jobs_to_run;               /* list of jobs to be run */
47
48 /* Time interval in secs to sleep if nothing to be run */
49 static int const NEXT_CHECK_SECS = 60;
50
51 /* Forward referenced subroutines */
52 static void find_runs();
53 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
54 static void dump_job(job_item *ji, char *msg);
55
56 /* Imported subroutines */
57
58 /* Imported variables */
59
60
61 /*********************************************************************
62  *
63  *         Main Bacula Scheduler
64  *
65  */
66 JCR *wait_for_next_job(char *one_shot_job_to_run)
67 {
68    JCR *jcr;
69    JOB *job;
70    RUN *run;
71    time_t now;
72    static bool first = true;
73    job_item *next_job = NULL;
74
75    Dmsg0(200, "Enter wait_for_next_job\n");
76    if (first) {
77       first = false;
78       /* Create scheduled jobs list */
79       jobs_to_run = new dlist(next_job, &next_job->link);
80       if (one_shot_job_to_run) {            /* one shot */
81          job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
82          if (!job) {
83             Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
84          }
85          Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
86          jcr = new_jcr(sizeof(JCR), dird_free_jcr);
87          set_jcr_defaults(jcr, job);
88          return jcr;
89       }
90    }
91    /* Wait until we have something in the
92     * next hour or so.
93     */
94    while (jobs_to_run->empty()) {
95       find_runs();
96       if (!jobs_to_run->empty()) {
97          break;
98       }
99       bmicrosleep(NEXT_CHECK_SECS, 0); /* recheck once per minute */
100    }
101
102 #define list_chain
103 #ifdef  list_chain
104    job_item *je;
105    foreach_dlist(je, jobs_to_run) {
106       dump_job(je, "Walk queue");
107    }
108 #endif
109    /* 
110     * Pull the first job to run (already sorted by runtime and
111     *  Priority, then wait around until it is time to run it.
112     */
113    next_job = (job_item *)jobs_to_run->first();
114    jobs_to_run->remove(next_job);
115
116    dump_job(next_job, "Dequeued job");
117
118    if (!next_job) {                /* we really should have something now */
119       Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
120    }
121
122    /* Now wait for the time to run the job */
123    for (;;) {
124       time_t twait;
125       now = time(NULL);
126       twait = next_job->runtime - now;
127       if (twait <= 0) {               /* time to run it */
128          break;
129       }
130       bmicrosleep(twait, 0);
131    }
132    run = next_job->run;               /* pick up needed values */
133    job = next_job->job;
134    run->last_run = now;               /* mark as run now */
135
136    dump_job(next_job, "Run job");
137
138    free(next_job);
139
140    jcr = new_jcr(sizeof(JCR), dird_free_jcr);
141    ASSERT(job);
142    set_jcr_defaults(jcr, job);
143    if (run->level) {
144       jcr->JobLevel = run->level;        /* override run level */
145    }
146    if (run->pool) {
147       jcr->pool = run->pool;          /* override pool */
148    }
149    if (run->storage) {
150       jcr->store = run->storage;      /* override storage */
151    }
152    if (run->msgs) {
153       jcr->messages = run->msgs;      /* override messages */
154    }
155    if (run->Priority) {
156       jcr->JobPriority = run->Priority;
157    }
158    Dmsg0(200, "Leave wait_for_next_job()\n");
159    return jcr;
160 }
161
162
163 /*
164  * Shutdown the scheduler  
165  */
166 void term_scheduler()
167 {
168    if (jobs_to_run) {
169       job_item *je;
170       /* Release all queued job entries to be run */
171       foreach_dlist(je, jobs_to_run) {
172          free(je);
173       }
174       delete jobs_to_run;
175    }
176 }
177
178 /*          
179  * Find all jobs to be run this hour and the next hour.
180  */
181 static void find_runs()
182 {
183    time_t now, next_hour, runtime;
184    RUN *run;
185    JOB *job;
186    SCHED *sched;
187    struct tm tm;
188    int minute;
189    int hour, mday, wday, month, wom, woy;
190    /* Items corresponding to above at the next hour */
191    int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
192
193    Dmsg0(200, "enter find_runs()\n");
194
195    
196    /* compute values for time now */
197    now = time(NULL);
198    localtime_r(&now, &tm);
199    hour = tm.tm_hour;
200    minute = tm.tm_min;
201    mday = tm.tm_mday - 1;
202    wday = tm.tm_wday;
203    month = tm.tm_mon;
204    wom = tm_wom(tm.tm_mday, tm.tm_wday);  /* get week of month */
205    woy = tm_woy(now);                     /* get week of year */
206
207    /* 
208     * Compute values for next hour from now.
209     * We do this to be sure we don't miss a job while
210     * sleeping.
211     */
212    next_hour = now + 3600;  
213    localtime_r(&next_hour, &tm);
214    nh_hour = tm.tm_hour;
215    nh_mday = tm.tm_mday - 1;
216    nh_wday = tm.tm_wday;
217    nh_month = tm.tm_mon;
218    nh_year  = tm.tm_year;
219    nh_wom = tm_wom(tm.tm_mday, tm.tm_wday);  /* get week of month */
220    nh_woy = tm_woy(now);                     /* get week of year */
221
222    /* Loop through all jobs */
223    LockRes();
224    foreach_res(job, R_JOB) {
225       sched = job->schedule;
226       if (sched == NULL) {            /* scheduled? */
227          continue;                    /* no, skip this job */
228       }
229       Dmsg1(200, "Got job: %s\n", job->hdr.name);
230       for (run=sched->run; run; run=run->next) {
231          bool run_now, run_nh;
232          /* 
233           * Find runs scheduled between now and the next hour.
234           */
235 #ifdef xxxx
236          Dmsg0(000, "\n");
237          Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n", 
238             hour, month, mday, wday, wom, woy);
239          Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n", 
240             bit_is_set(hour, run->hour),
241             bit_is_set(month, run->month),
242             bit_is_set(mday, run->mday),
243             bit_is_set(wday, run->wday),
244             bit_is_set(wom, run->wom),
245             bit_is_set(woy, run->woy));
246
247          Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n", 
248             nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
249          Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n", 
250             bit_is_set(nh_hour, run->hour),
251             bit_is_set(nh_month, run->month),
252             bit_is_set(nh_mday, run->mday),
253             bit_is_set(nh_wday, run->wday),
254             bit_is_set(nh_wom, run->wom),
255             bit_is_set(nh_woy, run->woy));
256 #endif
257
258          run_now = bit_is_set(hour, run->hour) &&
259             (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) &&
260             bit_is_set(month, run->month) &&
261             bit_is_set(wom, run->wom) &&
262             bit_is_set(woy, run->woy);
263
264          run_nh = bit_is_set(nh_hour, run->hour) &&
265             (bit_is_set(nh_mday, run->mday) || bit_is_set(nh_wday, run->wday)) &&
266             bit_is_set(nh_month, run->month) &&
267             bit_is_set(nh_wom, run->wom) &&
268             bit_is_set(nh_woy, run->woy);
269
270          Dmsg2(200, "run_now=%d run_nh=%d\n", run_now, run_nh);
271
272          /* find time (time_t) job is to be run */
273          localtime_r(&now, &tm);      /* reset tm structure */
274          tm.tm_min = run->minute;     /* set run minute */
275          tm.tm_sec = 0;               /* zero secs */
276          if (run_now) {
277             runtime = mktime(&tm);
278             add_job(job, run, now, runtime);
279          }
280          /* If job is to be run in the next hour schedule it */
281          if (run_nh) {
282             /* Set correct values */
283             tm.tm_hour = nh_hour;
284             tm.tm_mday = nh_mday + 1; /* fixup because we biased for tests above */
285             tm.tm_mon = nh_month;
286             tm.tm_year = nh_year;
287             runtime = mktime(&tm);
288             add_job(job, run, now, runtime);
289          }
290       }  
291    }
292    UnlockRes();
293    Dmsg0(200, "Leave find_runs()\n");
294 }
295
296 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
297 {
298    job_item *ji;
299    bool inserted = false;
300    /*
301     * Don't run any job that ran less than a minute ago, but
302     *  do run any job scheduled less than a minute ago.
303     */
304    if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
305 #ifdef xxx
306       char dt[50], dt1[50];
307       bstrftime(dt, sizeof(dt), runtime);  
308       strcpy(dt+7, dt+9);                /* cut century */
309       bstrftime(dt1, sizeof(dt1), now);  
310       strcpy(dt1+7, dt1+9);                /* cut century */
311       Dmsg2(000, "runtime=%s now=%s\n", dt, dt1);
312 #endif
313       return;
314    }
315    /* accept to run this job */
316    job_item *je = (job_item *)malloc(sizeof(job_item));
317    je->run = run;
318    je->job = job;
319    je->runtime = runtime;
320    if (run->Priority) {
321       je->Priority = run->Priority;
322    } else {
323       je->Priority = job->Priority;
324    }
325
326    /* Add this job to the wait queue in runtime, priority sorted order */
327    foreach_dlist(ji, jobs_to_run) {
328       if (ji->runtime > je->runtime || 
329           (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
330          jobs_to_run->insert_before(je, ji);
331          dump_job(je, "Inserted job");
332          inserted = true;
333          break;
334       }
335    }
336    /* If place not found in queue, append it */
337    if (!inserted) {
338       jobs_to_run->append(je);
339       dump_job(je, "Appended job");
340    }
341 }
342
343 static void dump_job(job_item *ji, char *msg) 
344 {
345    char dt[MAX_TIME_LENGTH];
346    if (debug_level < 200) {
347       return;
348    }
349    bstrftime(dt, sizeof(dt), ji->runtime);  
350    strcpy(dt+7, dt+9);                /* cut century */
351    Dmsg4(200, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name, 
352       ji->Priority, dt);
353 }