]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/scheduler.c
This commit was manufactured by cvs2svn to create tag
[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
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 typedef struct {
46    RUN *run;
47    JOB *job;
48    time_t runtime;
49 } RUNJOB;
50
51 static int num_runjobs;               /* total jobs found by find_runs() */
52 static int rem_runjobs;               /* jobs remaining to be processed */
53 static int max_runjobs;               /* max jobs in runjobs array */
54 static RUNJOB *runjobs;               /* array of jobs to be run */
55
56
57 /*********************************************************************
58  *
59  *         Main Bacula Scheduler
60  *
61  */
62 JCR *wait_for_next_job(char *job_to_run)
63 {
64    JCR *jcr;
65    JOB *job;
66    RUN *run;
67    time_t now, runtime, nexttime;
68    int jobindex, i;
69    static int first = TRUE;
70    char dt[MAX_TIME_LENGTH];
71
72    Dmsg0(200, "Enter wait_for_next_job\n");
73    if (first) {
74       first = FALSE;
75       max_runjobs = 10;
76       runjobs = (RUNJOB *) malloc(sizeof(RUNJOB) * max_runjobs);
77       num_runjobs = 0;
78       rem_runjobs = 0;
79       if (job_to_run) {               /* one shot */
80          job = (JOB *)GetResWithName(R_JOB, job_to_run);
81          if (!job) {
82             Emsg1(M_ABORT, 0, _("Job %s not found\n"), job_to_run);
83          }
84          Dmsg1(5, "Found job_to_run %s\n", job_to_run);
85          jcr = new_jcr(sizeof(JCR), dird_free_jcr);
86          set_jcr_defaults(jcr, job);
87          return jcr;
88       }
89    }
90    /* Wait until we have something in the
91     * next hour or so.
92     */
93    while (rem_runjobs == 0) {
94       find_runs();
95       if (rem_runjobs > 0) {
96          break;
97       }
98       bmicrosleep(60, 0);             /* recheck once per minute */
99    }
100
101    /* 
102     * Sort through what is to be run in the next
103     * two hours to find the first job to be run,
104     * then wait around until it is time.
105     *
106     */
107    time(&now);
108    nexttime = now + 60 * 60 * 24;     /* a much later time */
109    jobindex = -1;
110    bstrftime(dt, sizeof(dt), now);
111    Dmsg2(400, "jobs=%d. Now is %s\n", rem_runjobs, dt);
112    for (i=0; i<num_runjobs; i++) {
113       runtime = runjobs[i].runtime;
114       if (runtime > 0 && runtime < nexttime) { /* find minimum time job */
115          nexttime = runtime;
116          jobindex = i;
117       }
118 #ifdef xxxx_debug
119       if (runtime > 0) {
120          bstrftime(dt, sizeof(dt), runjobs[i].runtime);  
121          Dmsg2(100, "    %s run %s\n", dt, runjobs[i].job->hdr.name);
122       }
123 #endif
124    }
125    if (jobindex < 0) {                /* we really should have something now */
126       Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
127    }
128
129    /* Now wait for the time to run the job */
130    for (;;) {
131       time_t twait;
132       now = time(NULL);
133       twait = nexttime - now;
134       if (twait <= 0) {               /* time to run it */
135          break;
136       }
137       bmicrosleep(twait, 0);
138    }
139    run = runjobs[jobindex].run;
140    job = runjobs[jobindex].job;
141    runjobs[jobindex].runtime = 0;     /* remove from list */
142    run->last_run = now;               /* mark as run */
143    rem_runjobs--;                     /* decrement count of remaining jobs */
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    if (runjobs) {                     /* free allocated memory */
174       free(runjobs);
175       runjobs = NULL;
176       max_runjobs = 0;
177    }
178 }
179
180
181 /*          
182  * Find all jobs to be run this hour
183  * and the next hour.
184  */
185 static void find_runs()
186 {
187    time_t now, runtime;
188    RUN *run;
189    JOB *job;
190    SCHED *sched;
191    struct tm tm;
192    int hour, next_hour, minute, mday, wday, month, wom, woy;
193
194    Dmsg0(200, "enter find_runs()\n");
195    num_runjobs = 0;
196
197    now = time(NULL);
198    localtime_r(&now, &tm);
199    
200    hour = tm.tm_hour;
201    next_hour = hour + 1;
202    if (next_hour > 23)
203       next_hour -= 24;
204    minute = tm.tm_min;
205    mday = tm.tm_mday - 1;
206    wday = tm.tm_wday;
207    month = tm.tm_mon;
208    wom = mday / 7;
209    woy = tm_woy(now);                     /* get week of year */
210
211    /* Loop through all jobs */
212    LockRes();
213    for (job=NULL; (job=(JOB *)GetNextRes(R_JOB, (RES *)job)); ) {
214       sched = job->schedule;
215       if (sched == NULL) {            /* scheduled? */
216          continue;                    /* no, skip this job */
217       }
218       for (run=sched->run; run; run=run->next) {
219
220          /* Find runs scheduled in this our or in the
221           * next hour (we may be one second before the next hour).
222           */
223          if ((bit_is_set(hour, run->hour) || bit_is_set(next_hour, run->hour)) &&
224              (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) && 
225              bit_is_set(month, run->month) && 
226              bit_is_set(wom, run->wom) &&  
227              bit_is_set(woy, run->woy)) {
228
229             /* find time (time_t) job is to be run */
230             localtime_r(&now, &tm);
231             tm.tm_min = run->minute;
232             tm.tm_sec = 0;
233             if (bit_is_set(hour, run->hour)) {
234                runtime = mktime(&tm);
235                add_job(job, run, now, runtime);
236             }
237             if (bit_is_set(next_hour, run->hour)) {
238                tm.tm_hour++;
239                if (tm.tm_hour > 23) {
240                   continue;           /* next day */
241                }
242                runtime = mktime(&tm);
243                add_job(job, run, now, runtime);
244             }
245          }
246       }  
247    }
248
249    UnlockRes();
250    rem_runjobs = num_runjobs;
251    Dmsg0(200, "Leave find_runs()\n");
252 }
253
254 static void add_job(JOB *job, RUN *run, time_t now, time_t runtime)
255 {
256    /*
257     * Don't run any job that ran less than a minute ago, but
258     *  do run any job scheduled less than a minute ago.
259     */
260    if ((runtime - run->last_run < 61) || (runtime+59 < now)) {
261       return;
262    }
263
264    /* Make sure array is big enough */
265    if (num_runjobs == max_runjobs) {
266       max_runjobs += 10;
267       runjobs = (RUNJOB *)realloc(runjobs, sizeof(RUNJOB) * max_runjobs);
268       if (!runjobs)
269          Emsg0(M_ABORT, 0, _("Out of memory\n"));
270    } 
271    /* accept to run this job */
272    runjobs[num_runjobs].run = run;
273    runjobs[num_runjobs].job = job;
274    runjobs[num_runjobs++].runtime = runtime; /* when to run it */
275 }