]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/scheduler.c
Pull compiler warning cleanup code + other from master
[bacula/bacula] / bacula / src / dird / scheduler.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula scheduler
31  *     It looks at what jobs are to be run and when
32  *     and waits around until it is time to
33  *     fire them up.
34  *
35  *     Kern Sibbald, May MM, major revision December MMIII
36  *
37  */
38
39 #include "bacula.h"
40 #include "dird.h"
41
42 #if 0
43 #define SCHED_DEBUG
44 #define DBGLVL 0
45 #else
46 #undef SCHED_DEBUG
47 #define DBGLVL 200
48 #endif
49
50 const int dbglvl = DBGLVL;
51
52 /* Local variables */
53 struct job_item {
54    RUN *run;
55    JOB *job;
56    time_t runtime;
57    int Priority;
58    dlink link;                        /* link for list */
59 };
60
61 /* List of jobs to be run. They were scheduled in this hour or the next */
62 static dlist *jobs_to_run;               /* list of jobs to be run */
63
64 /* Time interval in secs to sleep if nothing to be run */
65 static int const next_check_secs = 60;
66
67 /* Forward referenced subroutines */
68 static void find_runs();
69 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
70 static void dump_job(job_item *ji, const char *msg);
71
72 /* Imported subroutines */
73
74 /* Imported variables */
75
76 /**
77  * called by reload_config to tell us that the schedules
78  * we may have based our next jobs to run queues have been
79  * invalidated.  In fact the schedules may not have changed
80  * but the run object that we have recorded the last_run time
81  * on are new and no longer have a valid last_run time which
82  * causes us to double run schedules that get put into the list
83  * because run_nh = 1.
84  */   
85 static bool schedules_invalidated = false;
86 void invalidate_schedules(void) {
87     schedules_invalidated = true;
88 }
89
90 /*********************************************************************
91  *
92  *         Main Bacula Scheduler
93  *
94  */
95 JCR *wait_for_next_job(char *one_shot_job_to_run)
96 {
97    JCR *jcr;
98    JOB *job;
99    RUN *run;
100    time_t now, prev;
101    static bool first = true;
102    job_item *next_job = NULL;
103
104    Dmsg0(dbglvl, "Enter wait_for_next_job\n");
105    if (first) {
106       first = false;
107       /* Create scheduled jobs list */
108       jobs_to_run = New(dlist(next_job, &next_job->link));
109       if (one_shot_job_to_run) {            /* one shot */
110          job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
111          if (!job) {
112             Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
113          }
114          Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
115          jcr = new_jcr(sizeof(JCR), dird_free_jcr);
116          set_jcr_defaults(jcr, job);
117          return jcr;
118       }
119    }
120
121    /* Wait until we have something in the
122     * next hour or so.
123     */
124 again:
125    while (jobs_to_run->empty()) {
126       find_runs();
127       if (!jobs_to_run->empty()) {
128          break;
129       }
130       bmicrosleep(next_check_secs, 0); /* recheck once per minute */
131    }
132
133 #ifdef  list_chain
134    job_item *je;
135    foreach_dlist(je, jobs_to_run) {
136       dump_job(je, _("Walk queue"));
137    }
138 #endif
139    /*
140     * Pull the first job to run (already sorted by runtime and
141     *  Priority, then wait around until it is time to run it.
142     */
143    next_job = (job_item *)jobs_to_run->first();
144    jobs_to_run->remove(next_job);
145
146    dump_job(next_job, _("Dequeued job"));
147
148    if (!next_job) {                /* we really should have something now */
149       Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
150    }
151
152    /* Now wait for the time to run the job */
153    for (;;) {
154       time_t twait;
155       /** discard scheduled queue and rebuild with new schedule objects. **/
156       lock_jobs();
157       if (schedules_invalidated) { 
158           dump_job(next_job, "Invalidated job");
159           free(next_job);
160           while (!jobs_to_run->empty()) {
161               next_job = (job_item *)jobs_to_run->first();
162               jobs_to_run->remove(next_job);
163               dump_job(next_job, "Invalidated job");
164               free(next_job);
165           }
166           schedules_invalidated = false;
167           unlock_jobs();
168           goto again;
169       }
170       unlock_jobs();
171       prev = now = time(NULL);
172       twait = next_job->runtime - now;
173       if (twait <= 0) {               /* time to run it */
174          break;
175       }
176       /* Recheck at least once per minute */
177       bmicrosleep((next_check_secs < twait)?next_check_secs:twait, 0);
178       /* Attempt to handle clock shift (but not daylight savings time changes)
179        * we allow a skew of 10 seconds before invalidating everything.
180        */
181       now = time(NULL);
182       if (now < prev-10 || now > (prev+next_check_secs+10)) {
183          schedules_invalidated = true;
184       }
185    }
186    jcr = new_jcr(sizeof(JCR), dird_free_jcr);
187    run = next_job->run;               /* pick up needed values */
188    job = next_job->job;
189    if (job->enabled) {
190       dump_job(next_job, _("Run job"));
191    }
192    free(next_job);
193    if (!job->enabled) {
194       free_jcr(jcr);
195       goto again;                     /* ignore this job */
196    }
197    run->last_run = now;               /* mark as run now */
198
199    ASSERT(job);
200    set_jcr_defaults(jcr, job);
201    if (run->level) {
202       jcr->setJobLevel(run->level);  /* override run level */
203    }
204    if (run->pool) {
205       jcr->pool = run->pool;          /* override pool */
206       jcr->run_pool_override = true;
207    }
208    if (run->full_pool) {
209       jcr->full_pool = run->full_pool; /* override full pool */
210       jcr->run_full_pool_override = true;
211    }
212    if (run->inc_pool) {
213       jcr->inc_pool = run->inc_pool;  /* override inc pool */
214       jcr->run_inc_pool_override = true;
215    }
216    if (run->diff_pool) {
217       jcr->diff_pool = run->diff_pool;  /* override dif pool */
218       jcr->run_diff_pool_override = true;
219    }
220    if (run->storage) {
221       USTORE store;
222       store.store = run->storage;
223       pm_strcpy(store.store_source, _("run override"));
224       set_rwstorage(jcr, &store);     /* override storage */
225    }
226    if (run->msgs) {
227       jcr->messages = run->msgs;      /* override messages */
228    }
229    if (run->Priority) {
230       jcr->JobPriority = run->Priority;
231    }
232    if (run->spool_data_set) {
233       jcr->spool_data = run->spool_data;
234    }
235    if (run->write_part_after_job_set) {
236       jcr->write_part_after_job = run->write_part_after_job;
237    }
238    Dmsg0(dbglvl, "Leave wait_for_next_job()\n");
239    return jcr;
240 }
241
242
243 /*
244  * Shutdown the scheduler
245  */
246 void term_scheduler()
247 {
248    if (jobs_to_run) {
249       delete jobs_to_run;
250    }
251 }
252
253 /*
254  * Find all jobs to be run this hour and the next hour.
255  */
256 static void find_runs()
257 {
258    time_t now, next_hour, runtime;
259    RUN *run;
260    JOB *job;
261    SCHED *sched;
262    struct tm tm;
263    int hour, mday, wday, month, wom, woy;
264    /* Items corresponding to above at the next hour */
265    int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy;
266
267    Dmsg0(dbglvl, "enter find_runs()\n");
268
269
270    /* compute values for time now */
271    now = time(NULL);
272    (void)localtime_r(&now, &tm);
273    hour = tm.tm_hour;
274    mday = tm.tm_mday - 1;
275    wday = tm.tm_wday;
276    month = tm.tm_mon;
277    wom = mday / 7;
278    woy = tm_woy(now);                     /* get week of year */
279
280    Dmsg7(dbglvl, "now = %x: h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
281          now, hour, month, mday, wday, wom, woy);
282
283    /*
284     * Compute values for next hour from now.
285     * We do this to be sure we don't miss a job while
286     * sleeping.
287     */
288    next_hour = now + 3600;
289    (void)localtime_r(&next_hour, &tm);
290    nh_hour = tm.tm_hour;
291    nh_mday = tm.tm_mday - 1;
292    nh_wday = tm.tm_wday;
293    nh_month = tm.tm_mon;
294    nh_wom = nh_mday / 7;
295    nh_woy = tm_woy(next_hour);              /* get week of year */
296
297    Dmsg7(dbglvl, "nh = %x: h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
298          next_hour, nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
299
300    /* Loop through all jobs */
301    LockRes();
302    foreach_res(job, R_JOB) {
303       sched = job->schedule;
304       if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
305          continue;                    /* no, skip this job */
306       }
307       Dmsg1(dbglvl, "Got job: %s\n", job->hdr.name);
308       for (run=sched->run; run; run=run->next) {
309          bool run_now, run_nh;
310          /*
311           * Find runs scheduled between now and the next hour.
312           */
313 #ifdef xxxx
314          Dmsg0(000, "\n");
315          Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
316             hour, month, mday, wday, wom, woy);
317          Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
318             bit_is_set(hour, run->hour),
319             bit_is_set(month, run->month),
320             bit_is_set(mday, run->mday),
321             bit_is_set(wday, run->wday),
322             bit_is_set(wom, run->wom),
323             bit_is_set(woy, run->woy));
324
325          Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
326             nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
327          Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
328             bit_is_set(nh_hour, run->hour),
329             bit_is_set(nh_month, run->month),
330             bit_is_set(nh_mday, run->mday),
331             bit_is_set(nh_wday, run->wday),
332             bit_is_set(nh_wom, run->wom),
333             bit_is_set(nh_woy, run->woy));
334 #endif
335
336          run_now = bit_is_set(hour, run->hour) &&
337             bit_is_set(mday, run->mday) &&
338             bit_is_set(wday, run->wday) &&
339             bit_is_set(month, run->month) &&
340             bit_is_set(wom, run->wom) &&
341             bit_is_set(woy, run->woy);
342
343          run_nh = bit_is_set(nh_hour, run->hour) &&
344             bit_is_set(nh_mday, run->mday) &&
345             bit_is_set(nh_wday, run->wday) &&
346             bit_is_set(nh_month, run->month) &&
347             bit_is_set(nh_wom, run->wom) &&
348             bit_is_set(nh_woy, run->woy);
349
350          Dmsg3(dbglvl, "run@%p: run_now=%d run_nh=%d\n", run, run_now, run_nh);
351
352          if (run_now || run_nh) {
353            /* find time (time_t) job is to be run */
354            (void)localtime_r(&now, &tm);      /* reset tm structure */
355            tm.tm_min = run->minute;     /* set run minute */
356            tm.tm_sec = 0;               /* zero secs */
357            runtime = mktime(&tm);
358            if (run_now) {
359              add_job(job, run, now, runtime);
360            }
361            /* If job is to be run in the next hour schedule it */
362            if (run_nh) {
363              add_job(job, run, now, runtime + 3600);
364            }
365          }
366       }
367    }
368    UnlockRes();
369    Dmsg0(dbglvl, "Leave find_runs()\n");
370 }
371
372 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
373 {
374    job_item *ji;
375    bool inserted = false;
376    /*
377     * Don't run any job that ran less than a minute ago, but
378     *  do run any job scheduled less than a minute ago.
379     */
380    if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
381 #ifdef SCHED_DEBUG
382       Dmsg4(000, "Drop: Job=\"%s\" run=%lld. last_run=%lld. now=%lld\n", job->hdr.name, 
383             (utime_t)runtime, (utime_t)run->last_run, (utime_t)now);
384       fflush(stdout);
385 #endif
386       return;
387    }
388 #ifdef SCHED_DEBUG
389    Dmsg4(000, "Add: Job=\"%s\" run=%lld last_run=%lld now=%lld\n", job->hdr.name, 
390             (utime_t)runtime, (utime_t)run->last_run, (utime_t)now);
391 #endif
392    /* accept to run this job */
393    job_item *je = (job_item *)malloc(sizeof(job_item));
394    je->run = run;
395    je->job = job;
396    je->runtime = runtime;
397    if (run->Priority) {
398       je->Priority = run->Priority;
399    } else {
400       je->Priority = job->Priority;
401    }
402
403    /* Add this job to the wait queue in runtime, priority sorted order */
404    foreach_dlist(ji, jobs_to_run) {
405       if (ji->runtime > je->runtime ||
406           (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
407          jobs_to_run->insert_before(je, ji);
408          dump_job(je, _("Inserted job"));
409          inserted = true;
410          break;
411       }
412    }
413    /* If place not found in queue, append it */
414    if (!inserted) {
415       jobs_to_run->append(je);
416       dump_job(je, _("Appended job"));
417    }
418 #ifdef SCHED_DEBUG
419    foreach_dlist(ji, jobs_to_run) {
420       dump_job(ji, _("Run queue"));
421    }
422    Dmsg0(000, "End run queue\n");
423 #endif
424 }
425
426 static void dump_job(job_item *ji, const char *msg)
427 {
428 #ifdef SCHED_DEBUG
429    char dt[MAX_TIME_LENGTH];
430    int save_debug = debug_level;
431    if (debug_level < dbglvl) {
432       return;
433    }
434    bstrftime_nc(dt, sizeof(dt), ji->runtime);
435    Dmsg4(dbglvl, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name, 
436       ji->Priority, dt);
437    fflush(stdout);
438    debug_level = save_debug;
439 #endif
440 }