]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/scheduler.c
Update from week away
[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, revised 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 /* Forward referenced subroutines */
37 static void find_runs();
38 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime);
39
40 /* Imported subroutines */
41
42 /* Imported variables */
43
44 /* Local variables */
45 struct job_item {  
46    RUN *run;
47    JOB *job;
48    time_t runtime;
49    dlink link;                        /* link for list */
50 };              
51
52 /* List of jobs to be run. They were scheduled in this hour or the next */
53 static dlist *jobs_to_run;               /* list of jobs to be run */
54
55 /* Time interval in secs to sleep if nothing to be run */
56 #define NEXT_CHECK_SECS 60
57
58 /*********************************************************************
59  *
60  *         Main Bacula Scheduler
61  *
62  */
63 JCR *wait_for_next_job(char *one_shot_job_to_run)
64 {
65    JCR *jcr;
66    JOB *job;
67    RUN *run;
68    time_t now, runtime, nexttime;
69    static bool first = true;
70    char dt[MAX_TIME_LENGTH];
71    job_item *next_job = NULL;
72
73    Dmsg0(200, "Enter wait_for_next_job\n");
74    if (first) {
75       first = false;
76       /* Create scheduled jobs list */
77       jobs_to_run = new dlist(next_job, &next_job->link);
78       if (one_shot_job_to_run) {            /* one shot */
79          job = (JOB *)GetResWithName(R_JOB, one_shot_job_to_run);
80          if (!job) {
81             Emsg1(M_ABORT, 0, _("Job %s not found\n"), one_shot_job_to_run);
82          }
83          Dmsg1(5, "Found one_shot_job_to_run %s\n", one_shot_job_to_run);
84          jcr = new_jcr(sizeof(JCR), dird_free_jcr);
85          set_jcr_defaults(jcr, job);
86          return jcr;
87       }
88    }
89    /* Wait until we have something in the
90     * next hour or so.
91     */
92    while (jobs_to_run->size() == 0) {
93       find_runs();
94       if (jobs_to_run->size() > 0) {
95          break;
96       }
97       bmicrosleep(NEXT_CHECK_SECS, 0); /* recheck once per minute */
98    }
99
100    /* 
101     * Sort through what is to be run in the next
102     * two hours to find the first job to be run,
103     * then wait around until it is time.
104     *
105     */
106    time(&now);
107    nexttime = now + 60 * 60 * 24;     /* a much later time */
108    bstrftime(dt, sizeof(dt), now);
109    Dmsg2(400, "jobs=%d. Now is %s\n", jobs_to_run->size(), dt);
110    next_job = NULL;
111    for (job_item *je=NULL; (je=(job_item *)jobs_to_run->next(je)); ) {
112       runtime = je->runtime;
113       if (runtime > 0 && runtime < nexttime) { /* find minimum time job */
114          nexttime = runtime;
115          next_job = je;
116       }
117
118 #define xxxx_debug
119 #ifdef  xxxx_debug
120       if (runtime > 0) {
121          bstrftime(dt, sizeof(dt), next_job->runtime);  
122          Dmsg2(100, "    %s run %s\n", dt, next_job->job->hdr.name);
123       }
124 #endif
125    }
126    if (!next_job) {                /* we really should have something now */
127       Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
128    }
129
130    /* Now wait for the time to run the job */
131    for (;;) {
132       time_t twait;
133       now = time(NULL);
134       twait = nexttime - now;
135       if (twait <= 0) {               /* time to run it */
136          break;
137       }
138       bmicrosleep(twait, 0);
139    }
140    run = next_job->run;               /* pick up needed values */
141    job = next_job->job;
142    jobs_to_run->remove(next_job);        /* remove from list */
143    run->last_run = now;               /* mark as run */
144
145    jcr = new_jcr(sizeof(JCR), dird_free_jcr);
146    ASSERT(job);
147    set_jcr_defaults(jcr, job);
148    if (run->level) {
149       jcr->JobLevel = run->level;        /* override run level */
150    }
151    if (run->pool) {
152       jcr->pool = run->pool;          /* override pool */
153    }
154    if (run->storage) {
155       jcr->store = run->storage;      /* override storage */
156    }
157    if (run->msgs) {
158       jcr->messages = run->msgs;      /* override messages */
159    }
160    if (run->Priority) {
161       jcr->JobPriority = run->Priority;
162    }
163    Dmsg0(200, "Leave wait_for_next_job()\n");
164    return jcr;
165 }
166
167
168 /*
169  * Shutdown the scheduler  
170  */
171 void term_scheduler()
172 {
173    /* Release all queued job entries to be run */
174    for (void *je=NULL; (je=jobs_to_run->next(je)); ) {
175       free(je);
176    }
177    delete jobs_to_run;
178 }
179
180 /*          
181  * Find all jobs to be run this hour and the next hour.
182  */
183 static void find_runs()
184 {
185    time_t now, next_hour, runtime;
186    RUN *run;
187    JOB *job;
188    SCHED *sched;
189    struct tm tm;
190    int minute;
191    int hour, mday, wday, month, wom, woy;
192    /* Items corresponding to above at the next hour */
193    int nh_hour, nh_mday, nh_wday, nh_month, nh_wom, nh_woy, nh_year;
194
195    Dmsg0(200, "enter find_runs()\n");
196
197    
198    /* compute values for time now */
199    now = time(NULL);
200    localtime_r(&now, &tm);
201    hour = tm.tm_hour;
202    minute = tm.tm_min;
203    mday = tm.tm_mday - 1;
204    wday = tm.tm_wday;
205    month = tm.tm_mon;
206    wom = tm_wom(tm.tm_mday, tm.tm_wday);  /* get week of month */
207    woy = tm_woy(now);                     /* get week of year */
208
209    /* 
210     * Compute values for next hour from now.
211     * We do this to be sure we don't miss a job while
212     * sleeping.
213     */
214    next_hour = now + 3600;  
215    localtime_r(&next_hour, &tm);
216    nh_hour = tm.tm_hour;
217    minute = tm.tm_min;
218    nh_mday = tm.tm_mday - 1;
219    nh_wday = tm.tm_wday;
220    nh_month = tm.tm_mon;
221    nh_year  = tm.tm_year;
222    nh_wom = tm_wom(tm.tm_mday, tm.tm_wday);  /* get week of month */
223    nh_woy = tm_woy(now);                     /* get week of year */
224
225    /* Loop through all jobs */
226    LockRes();
227    for (job=NULL; (job=(JOB *)GetNextRes(R_JOB, (RES *)job)); ) {
228       sched = job->schedule;
229       if (sched == NULL) {            /* scheduled? */
230          continue;                    /* no, skip this job */
231       }
232       for (run=sched->run; run; run=run->next) {
233          bool run_now, run_nh;
234
235          /* Find runs scheduled between now and the next
236           * check time (typically 60 seconds)
237           */
238          run_now = bit_is_set(hour, run->hour) &&
239             (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) &&
240             bit_is_set(month, run->month) &&
241             bit_is_set(wom, run->wom) &&
242             bit_is_set(woy, run->woy);
243
244          run_nh = bit_is_set(nh_hour, run->hour) &&
245             (bit_is_set(nh_mday, run->mday) || bit_is_set(nh_wday, run->wday)) &&
246             bit_is_set(nh_month, run->month) &&
247             bit_is_set(nh_wom, run->wom) &&
248             bit_is_set(nh_woy, run->woy);
249
250          /* find time (time_t) job is to be run */
251          localtime_r(&now, &tm);      /* reset tm structure */
252          tm.tm_min = run->minute;     /* set run minute */
253          tm.tm_sec = 0;               /* zero secs */
254          if (run_now) {
255             runtime = mktime(&tm);
256             add_job(job, run, now, runtime);
257          }
258          /* If job is to be run in the next hour schedule it */
259          if (run_nh) {
260             /* Set correct values */
261             tm.tm_hour = nh_hour;
262             tm.tm_mday = nh_mday;
263             tm.tm_mon = nh_month;
264             tm.tm_year = nh_year;
265             runtime = mktime(&tm);
266             add_job(job, run, now, runtime);
267          }
268       }  
269    }
270    UnlockRes();
271    Dmsg0(200, "Leave find_runs()\n");
272 }
273
274 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
275 {
276    /*
277     * Don't run any job that ran less than a minute ago, but
278     *  do run any job scheduled less than a minute ago.
279     */
280    if ((runtime - run->last_run < 61) || (runtime+59 < now)) {
281       return;
282    }
283    /* accept to run this job */
284    job_item *je = (job_item *)malloc(sizeof(job_item));
285    je->run = run;
286    je->job = job;
287    je->runtime = runtime;
288    /* ***FIXME**** (enhancement) insert by sorted runtime */
289    jobs_to_run->append(je);
290 }