]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/scheduler.c
ebl add Error status in update volume=xxx status=yyyy
[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    Bacula® - The Network Backup Solution
14
15    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
16
17    The main author of Bacula is Kern Sibbald, with contributions from
18    many others, a complete list can be found in the file AUTHORS.
19    This program is Free Software; you can redistribute it and/or
20    modify it under the terms of version two of the GNU General Public
21    License as published by the Free Software Foundation plus additions
22    that are listed in the file LICENSE.
23
24    This program is distributed in the hope that it will be useful, but
25    WITHOUT ANY WARRANTY; without even the implied warranty of
26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27    General Public License for more details.
28
29    You should have received a copy of the GNU General Public License
30    along with this program; if not, write to the Free Software
31    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32    02110-1301, USA.
33
34    Bacula® is a registered trademark of John Walker.
35    The licensor of Bacula is the Free Software Foundation Europe
36    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
37    Switzerland, email:ftf@fsfeurope.org.
38 */
39
40 #include "bacula.h"
41 #include "dird.h"
42
43 #if 0
44 #define SCHED_DEBUG
45 #define DBGLVL 0
46 #else
47 #undef SCHED_DEBUG
48 #define DBGLVL 200
49 #endif
50
51 const int dbglvl = DBGLVL;
52
53 /* Local variables */
54 struct job_item {
55    RUN *run;
56    JOB *job;
57    time_t runtime;
58    int Priority;
59    dlink link;                        /* link for list */
60 };
61
62 /* List of jobs to be run. They were scheduled in this hour or the next */
63 static dlist *jobs_to_run;               /* list of jobs to be run */
64
65 /* Time interval in secs to sleep if nothing to be run */
66 static int const next_check_secs = 60;
67
68 /* Forward referenced subroutines */
69 static void find_runs();
70 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
71 static void dump_job(job_item *ji, const char *msg);
72
73 /* Imported subroutines */
74
75 /* Imported variables */
76
77 /**
78  * called by reload_config to tell us that the schedules
79  * we may have based our next jobs to run queues have been
80  * invalidated.  In fact the schedules may not have changed
81  * but the run object that we have recorded the last_run time
82  * on are new and no longer have a valid last_run time which
83  * causes us to double run schedules that get put into the list
84  * because run_nh = 1.
85  */   
86 static bool schedules_invalidated = false;
87 void invalidate_schedules(void) {
88     schedules_invalidated = true;
89 }
90
91 /*********************************************************************
92  *
93  *         Main Bacula Scheduler
94  *
95  */
96 JCR *wait_for_next_job(char *one_shot_job_to_run)
97 {
98    JCR *jcr;
99    JOB *job;
100    RUN *run;
101    time_t now, prev;
102    static bool first = true;
103    job_item *next_job = NULL;
104
105    Dmsg0(dbglvl, "Enter wait_for_next_job\n");
106    if (first) {
107       first = false;
108       /* Create scheduled jobs list */
109       jobs_to_run = New(dlist(next_job, &next_job->link));
110       if (one_shot_job_to_run) {            /* one shot */
111          job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
112          if (!job) {
113             Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
114          }
115          Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
116          jcr = new_jcr(sizeof(JCR), dird_free_jcr);
117          set_jcr_defaults(jcr, job);
118          return jcr;
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 from/to daylight savings time
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->JobLevel = 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       set_rwstorage(jcr, run->storage); /* override storage */
222    }
223    if (run->msgs) {
224       jcr->messages = run->msgs;      /* override messages */
225    }
226    if (run->Priority) {
227       jcr->JobPriority = run->Priority;
228    }
229    if (run->spool_data_set) {
230       jcr->spool_data = run->spool_data;
231    }
232    if (run->write_part_after_job_set) {
233       jcr->write_part_after_job = run->write_part_after_job;
234    }
235    Dmsg0(dbglvl, "Leave wait_for_next_job()\n");
236    return jcr;
237 }
238
239
240 /*
241  * Shutdown the scheduler
242  */
243 void term_scheduler()
244 {
245    if (jobs_to_run) {
246       job_item *je;
247       /* Release all queued job entries to be run */
248       foreach_dlist(je, jobs_to_run) {
249          free(je);
250       }
251       delete jobs_to_run;
252    }
253 }
254
255 /*
256  * Find all jobs to be run this hour and the next hour.
257  */
258 static void find_runs()
259 {
260    time_t now, next_hour, runtime;
261    RUN *run;
262    JOB *job;
263    SCHED *sched;
264    struct tm tm;
265    int minute;
266    int hour, mday, wday, month, wom, woy;
267    /* Items corresponding to above at the next hour */
268    int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
269
270    Dmsg0(dbglvl, "enter find_runs()\n");
271
272
273    /* compute values for time now */
274    now = time(NULL);
275    (void)localtime_r(&now, &tm);
276    hour = tm.tm_hour;
277    minute = tm.tm_min;
278    mday = tm.tm_mday - 1;
279    wday = tm.tm_wday;
280    month = tm.tm_mon;
281    wom = mday / 7;
282    woy = tm_woy(now);                     /* get week of year */
283
284    /*
285     * Compute values for next hour from now.
286     * We do this to be sure we don't miss a job while
287     * sleeping.
288     */
289    next_hour = now + 3600;
290    (void)localtime_r(&next_hour, &tm);
291    nh_hour = tm.tm_hour;
292    nh_mday = tm.tm_mday - 1;
293    nh_wday = tm.tm_wday;
294    nh_month = tm.tm_mon;
295    nh_year  = tm.tm_year;
296    nh_wom = nh_mday / 7;
297    nh_woy = tm_woy(now);                     /* get week of year */
298
299    /* Loop through all jobs */
300    LockRes();
301    foreach_res(job, R_JOB) {
302       sched = job->schedule;
303       if (sched == NULL || !job->enabled) { /* scheduled? or enabled? */
304          continue;                    /* no, skip this job */
305       }
306       Dmsg1(dbglvl, "Got job: %s\n", job->hdr.name);
307       for (run=sched->run; run; run=run->next) {
308          bool run_now, run_nh;
309          /*
310           * Find runs scheduled between now and the next hour.
311           */
312 #ifdef xxxx
313          Dmsg0(000, "\n");
314          Dmsg6(000, "run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
315             hour, month, mday, wday, wom, woy);
316          Dmsg6(000, "bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
317             bit_is_set(hour, run->hour),
318             bit_is_set(month, run->month),
319             bit_is_set(mday, run->mday),
320             bit_is_set(wday, run->wday),
321             bit_is_set(wom, run->wom),
322             bit_is_set(woy, run->woy));
323
324          Dmsg6(000, "nh_run h=%d m=%d md=%d wd=%d wom=%d woy=%d\n",
325             nh_hour, nh_month, nh_mday, nh_wday, nh_wom, nh_woy);
326          Dmsg6(000, "nh_bitset bsh=%d bsm=%d bsmd=%d bswd=%d bswom=%d bswoy=%d\n",
327             bit_is_set(nh_hour, run->hour),
328             bit_is_set(nh_month, run->month),
329             bit_is_set(nh_mday, run->mday),
330             bit_is_set(nh_wday, run->wday),
331             bit_is_set(nh_wom, run->wom),
332             bit_is_set(nh_woy, run->woy));
333 #endif
334
335          run_now = bit_is_set(hour, run->hour) &&
336             bit_is_set(mday, run->mday) &&
337             bit_is_set(wday, run->wday) &&
338             bit_is_set(month, run->month) &&
339             bit_is_set(wom, run->wom) &&
340             bit_is_set(woy, run->woy);
341
342          run_nh = bit_is_set(nh_hour, run->hour) &&
343             bit_is_set(nh_mday, run->mday) &&
344             bit_is_set(nh_wday, run->wday) &&
345             bit_is_set(nh_month, run->month) &&
346             bit_is_set(nh_wom, run->wom) &&
347             bit_is_set(nh_woy, run->woy);
348
349          Dmsg3(dbglvl, "run@%p: run_now=%d run_nh=%d\n", run, run_now, run_nh);
350
351          /* find time (time_t) job is to be run */
352          (void)localtime_r(&now, &tm);      /* reset tm structure */
353          tm.tm_min = run->minute;     /* set run minute */
354          tm.tm_sec = 0;               /* zero secs */
355          if (run_now) {
356             runtime = mktime(&tm);
357             add_job(job, run, now, runtime);
358          }
359          /* If job is to be run in the next hour schedule it */
360          if (run_nh) {
361             /* Set correct values */
362             tm.tm_hour = nh_hour;
363             tm.tm_mday = nh_mday + 1; /* fixup because we biased for tests above */
364             tm.tm_mon = nh_month;
365             tm.tm_year = nh_year;
366             runtime = mktime(&tm);
367             add_job(job, run, now, runtime);
368          }
369       }
370    }
371    UnlockRes();
372    Dmsg0(dbglvl, "Leave find_runs()\n");
373 }
374
375 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
376 {
377    job_item *ji;
378    bool inserted = false;
379    /*
380     * Don't run any job that ran less than a minute ago, but
381     *  do run any job scheduled less than a minute ago.
382     */
383    if (((runtime - run->last_run) < 61) || ((runtime+59) < now)) {
384 #ifdef SCHED_DEBUG
385       char dt[50], dt1[50], dt2[50];
386       bstrftime_nc(dt, sizeof(dt), runtime);
387       bstrftime_nc(dt1, sizeof(dt1), run->last_run);
388       bstrftime_nc(dt2, sizeof(dt2), now);
389       Dmsg7(000, "Drop: Job=\"%s\" run=%s(%x). last_run=%s(%x). now=%s(%x)\n", job->hdr.name, 
390             dt, runtime, dt1, run->last_run, dt2, now);
391       fflush(stdout);
392 #endif
393       return;
394    }
395 #ifdef SCHED_DEBUG
396    Dmsg4(000, "Add: Job=\"%s\" run=%x last_run=%x now=%x\n", job->hdr.name, 
397             runtime, run->last_run, now);
398 #endif
399    /* accept to run this job */
400    job_item *je = (job_item *)malloc(sizeof(job_item));
401    je->run = run;
402    je->job = job;
403    je->runtime = runtime;
404    if (run->Priority) {
405       je->Priority = run->Priority;
406    } else {
407       je->Priority = job->Priority;
408    }
409
410    /* Add this job to the wait queue in runtime, priority sorted order */
411    foreach_dlist(ji, jobs_to_run) {
412       if (ji->runtime > je->runtime ||
413           (ji->runtime == je->runtime && ji->Priority > je->Priority)) {
414          jobs_to_run->insert_before(je, ji);
415          dump_job(je, _("Inserted job"));
416          inserted = true;
417          break;
418       }
419    }
420    /* If place not found in queue, append it */
421    if (!inserted) {
422       jobs_to_run->append(je);
423       dump_job(je, _("Appended job"));
424    }
425 #ifdef SCHED_DEBUG
426    foreach_dlist(ji, jobs_to_run) {
427       dump_job(ji, _("Run queue"));
428    }
429    Dmsg0(000, "End run queue\n");
430 #endif
431 }
432
433 static void dump_job(job_item *ji, const char *msg)
434 {
435 #ifdef SCHED_DEBUG
436    char dt[MAX_TIME_LENGTH];
437    int save_debug = debug_level;
438    if (debug_level < dbglvl) {
439       return;
440    }
441    bstrftime_nc(dt, sizeof(dt), ji->runtime);
442    Dmsg4(dbglvl, "%s: Job=%s priority=%d run %s\n", msg, ji->job->hdr.name, 
443       ji->Priority, dt);
444    fflush(stdout);
445    debug_level = save_debug;
446 #endif
447 }