]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/scheduler.c
SetIP command + CloseOnPoll + Full,Inc,Diff Pools + more access control checks
[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-2004 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->full_pool) {
150       jcr->pool = run->full_pool;     /* override full pool */
151    }
152    if (run->inc_pool) {
153       jcr->pool = run->inc_pool;      /* override inc pool */
154    }
155    if (run->dif_pool) {
156       jcr->pool = run->dif_pool;      /* override dif pool */
157    }
158    if (run->storage) {
159       jcr->store = run->storage;      /* override storage */
160    }
161    if (run->msgs) {
162       jcr->messages = run->msgs;      /* override messages */
163    }
164    if (run->Priority) {
165       jcr->JobPriority = run->Priority;
166    }
167    Dmsg0(200, "Leave wait_for_next_job()\n");
168    return jcr;
169 }
170
171
172 /*
173  * Shutdown the scheduler  
174  */
175 void term_scheduler()
176 {
177    if (jobs_to_run) {
178       job_item *je;
179       /* Release all queued job entries to be run */
180       foreach_dlist(je, jobs_to_run) {
181          free(je);
182       }
183       delete jobs_to_run;
184    }
185 }
186
187 /*          
188  * Find all jobs to be run this hour and the next hour.
189  */
190 static void find_runs()
191 {
192    time_t now, next_hour, runtime;
193    RUN *run;
194    JOB *job;
195    SCHED *sched;
196    struct tm tm;
197    int minute;
198    int hour, mday, wday, month, wom, woy;
199    /* Items corresponding to above at the next hour */
200    int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
201
202    Dmsg0(200, "enter find_runs()\n");
203
204    
205    /* compute values for time now */
206    now = time(NULL);
207    localtime_r(&now, &tm);
208    hour = tm.tm_hour;
209    minute = tm.tm_min;
210    mday = tm.tm_mday - 1;
211    wday = tm.tm_wday;
212    month = tm.tm_mon;
213    wom = mday / 7;
214    woy = tm_woy(now);                     /* get week of year */
215
216    /* 
217     * Compute values for next hour from now.
218     * We do this to be sure we don't miss a job while
219     * sleeping.
220     */
221    next_hour = now + 3600;  
222    localtime_r(&next_hour, &tm);
223    nh_hour = tm.tm_hour;
224    nh_mday = tm.tm_mday - 1;
225    nh_wday = tm.tm_wday;
226    nh_month = tm.tm_mon;
227    nh_year  = tm.tm_year;
228    nh_wom = nh_mday / 7;
229    nh_woy = tm_woy(now);                     /* get week of year */
230
231    /* Loop through all jobs */
232    LockRes();
233    foreach_res(job, R_JOB) {
234       sched = job->schedule;
235       if (sched == NULL) {            /* scheduled? */
236          continue;                    /* no, skip this job */
237       }
238       Dmsg1(200, "Got job: %s\n", job->hdr.name);
239       for (run=sched->run; run; run=run->next) {
240          bool run_now, run_nh;
241          /* 
242           * Find runs scheduled between now and the next hour.
243           */
244 #ifdef xxxx
245          Dmsg0(000, "\n");
246          Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n", 
247             hour, month, mday, wday, wom, woy);
248          Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n", 
249             bit_is_set(hour, run->hour),
250             bit_is_set(month, run->month),
251             bit_is_set(mday, run->mday),
252             bit_is_set(wday, run->wday),
253             bit_is_set(wom, run->wom),
254             bit_is_set(woy, run->woy));
255
256          Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n", 
257             nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
258          Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n", 
259             bit_is_set(nh_hour, run->hour),
260             bit_is_set(nh_month, run->month),
261             bit_is_set(nh_mday, run->mday),
262             bit_is_set(nh_wday, run->wday),
263             bit_is_set(nh_wom, run->wom),
264             bit_is_set(nh_woy, run->woy));
265 #endif
266
267          run_now = bit_is_set(hour, run->hour) &&
268             (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) &&
269             bit_is_set(month, run->month) &&
270             bit_is_set(wom, run->wom) &&
271             bit_is_set(woy, run->woy);
272
273          run_nh = bit_is_set(nh_hour, run->hour) &&
274             (bit_is_set(nh_mday, run->mday) || bit_is_set(nh_wday, run->wday)) &&
275             bit_is_set(nh_month, run->month) &&
276             bit_is_set(nh_wom, run->wom) &&
277             bit_is_set(nh_woy, run->woy);
278
279          Dmsg2(200, "run_now=%d run_nh=%d\n", run_now, run_nh);
280
281          /* find time (time_t) job is to be run */
282          localtime_r(&now, &tm);      /* reset tm structure */
283          tm.tm_min = run->minute;     /* set run minute */
284          tm.tm_sec = 0;               /* zero secs */
285          if (run_now) {
286             runtime = mktime(&tm);
287             add_job(job, run, now, runtime);
288          }
289          /* If job is to be run in the next hour schedule it */
290          if (run_nh) {
291             /* Set correct values */
292             tm.tm_hour = nh_hour;
293             tm.tm_mday = nh_mday + 1; /* fixup because we biased for tests above */
294             tm.tm_mon = nh_month;
295             tm.tm_year = nh_year;
296             runtime = mktime(&tm);
297             add_job(job, run, now, runtime);
298          }
299       }  
300    }
301    UnlockRes();
302    Dmsg0(200, "Leave find_runs()\n");
303 }
304
305 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
306 {
307    job_item *ji;
308    bool inserted = false;
309    /*
310     * Don't run any job that ran less than a minute ago, but
311     *  do run any job scheduled less than a minute ago.
312     */
313    if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
314 #ifdef xxx
315       char dt[50], dt1[50];
316       bstrftime(dt, sizeof(dt), runtime);  
317       strcpy(dt+7, dt+9);                /* cut century */
318       bstrftime(dt1, sizeof(dt1), now);  
319       strcpy(dt1+7, dt1+9);                /* cut century */
320       Dmsg2(000, "runtime=%s now=%s\n", dt, dt1);
321 #endif
322       return;
323    }
324    /* accept to run this job */
325    job_item *je = (job_item *)malloc(sizeof(job_item));
326    je->run = run;
327    je->job = job;
328    je->runtime = runtime;
329    if (run->Priority) {
330       je->Priority = run->Priority;
331    } else {
332       je->Priority = job->Priority;
333    }
334
335    /* Add this job to the wait queue in runtime, priority sorted order */
336    foreach_dlist(ji, jobs_to_run) {
337       if (ji->runtime > je->runtime || 
338           (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
339          jobs_to_run->insert_before(je, ji);
340          dump_job(je, "Inserted job");
341          inserted = true;
342          break;
343       }
344    }
345    /* If place not found in queue, append it */
346    if (!inserted) {
347       jobs_to_run->append(je);
348       dump_job(je, "Appended job");
349    }
350 }
351
352 static void dump_job(job_item *ji, char *msg) 
353 {
354    char dt[MAX_TIME_LENGTH];
355    if (debug_level < 200) {
356       return;
357    }
358    bstrftime(dt, sizeof(dt), ji->runtime);  
359    strcpy(dt+7, dt+9);                /* cut century */
360    Dmsg4(200, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name, 
361       ji->Priority, dt);
362 }