]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/scheduler.c
Initial revision
[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 /*
11    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "dird.h"
32
33
34 /* Forward referenced subroutines */
35 static void find_runs();
36
37 /* Imported subroutines */
38
39 /* Imported variables */
40
41 /* Local variables */
42 typedef struct {
43    RUN *run;
44    JOB *job;
45    time_t runtime;
46 } RUNJOB;
47
48 static int num_runjobs;               /* total jobs found by find_runs() */
49 static int rem_runjobs;               /* jobs remaining to be processed */
50 static int max_runjobs;               /* max jobs in runjobs array */
51 static RUNJOB *runjobs;               /* array of jobs to be run */
52
53
54 /*********************************************************************
55  *
56  *         Main Bacula Scheduler
57  *
58  */
59 JCR *wait_for_next_job(char *job_to_run)
60 {
61    JCR *jcr;
62    JOB *job;
63    RUN *run;
64    time_t now, runtime, nexttime;
65    int jobindex, i;
66    static int first = TRUE;
67
68    Dmsg0(200, "Enter wait_for_next_job\n");
69    if (first) {
70       first = FALSE;
71       max_runjobs = 10;
72       runjobs = (RUNJOB *) malloc(sizeof(RUNJOB) * max_runjobs);
73       num_runjobs = 0;
74       if (job_to_run) {               /* one shot */
75          job = (JOB *)GetResWithName(R_JOB, job_to_run);
76          if (!job) {
77             Emsg1(M_ERROR, 0, _("Job %s not found\n"), job_to_run);
78          }
79          Dmsg1(5, "Found job_to_run %s\n", job_to_run);
80          jcr = new_jcr(sizeof(JCR), dird_free_jcr);
81          set_jcr_defaults(jcr, job);
82          return jcr;
83       }
84       find_runs();
85    }
86    /* Wait until we have something in the
87     * next hour or so.
88     */
89    while (num_runjobs == 0 || rem_runjobs == 0) {
90       sleep(60);
91       find_runs();
92    }
93    /* 
94     * Sort through what is to be run in the next
95     * two hours to find the first job to be run,
96     * then wait around until it is time.
97     *
98     */
99    time(&now);
100    nexttime = now + 60 * 60 * 24;     /* a much later time */
101    jobindex = -1;
102    for (i=0; i<num_runjobs; i++) {
103       runtime = runjobs[i].runtime;
104       if (runtime > 0 && runtime < nexttime) { /* find minimum time job */
105          nexttime = runtime;
106          jobindex = i;
107       }
108    }
109    if (jobindex < 0) {                /* we really should have something now */
110       Emsg0(M_ABORT, 0, _("Scheduler logic error\n"));
111    }
112
113    /* Now wait for the time to run the job */
114    for (;;) {
115       int twait;
116       time(&now);
117       twait = nexttime - now;
118       if (twait <= 0)                 /* time to run it */
119          break;
120       if (twait > 20)                 /* sleep max 20 seconds */
121          twait = 20;
122       sleep(twait);
123    }
124    run = runjobs[jobindex].run;
125    job = runjobs[jobindex].job;
126    runjobs[jobindex].runtime = 0;     /* remove from list */
127    run->last_run = now;               /* mark as run */
128    rem_runjobs--;                     /* decrement count of remaining jobs */
129    jcr = new_jcr(sizeof(JCR), dird_free_jcr);
130    set_jcr_defaults(jcr, job);
131    jcr->level = run->level;           /* override run level */
132    Dmsg0(200, "Leave wait_for_next_job()\n");
133    return jcr;
134 }
135
136
137 /*
138  * Shutdown the scheduler  
139  */
140 void term_scheduler()
141 {
142    if (runjobs) {                     /* free allocated memory */
143       free(runjobs);
144       runjobs = NULL;
145       max_runjobs = 0;
146    }
147 }
148
149
150 /*          
151  * Find all jobs to be run this hour
152  * and the next hour.
153  */
154 static void find_runs()
155 {
156    time_t now, runtime;
157    RUN *run;
158    JOB *job;
159    SCHED *sched;
160    struct tm tm;
161    int hour, next_hour, minute, mday, wday, month;
162
163    Dmsg0(200, "enter find_runs()\n");
164    num_runjobs = 0;
165
166    now = time(NULL);
167    localtime_r(&now, &tm);
168    
169    hour = tm.tm_hour;
170    next_hour = hour + 1;
171    if (next_hour > 23)
172       next_hour -= 24;
173    minute = tm.tm_min;
174    mday = tm.tm_mday - 1;
175    wday = tm.tm_wday;
176    month = tm.tm_mon;
177
178    /* Loop through all jobs */
179    LockRes();
180    for (job=NULL; (job=(JOB *)GetNextRes(R_JOB, (RES *)job)); ) {
181       sched = job->schedule;
182       if (sched == NULL) {            /* scheduled? */
183          continue;                    /* no, skip this job */
184       }
185       for (run=sched->run; run; run=run->next) {
186
187          if (now - run->last_run < 60 * 20)
188             continue;                 /* wait at least 20 minutes */
189
190          /* Find runs scheduled in this our or in the
191           * next hour (we may be one second before the next hour).
192           */
193          if ((bit_is_set(hour, run->hour) || bit_is_set(next_hour, run->hour)) &&
194              (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) && 
195              bit_is_set(month, run->month)) {
196
197             /* find time (time_t) job is to be run */
198             localtime_r(&now, &tm);
199             if (bit_is_set(next_hour, run->hour))
200                tm.tm_hour++;
201             if (tm.tm_hour > 23)
202                tm.tm_hour = 0;
203             tm.tm_min = run->minute;
204             tm.tm_sec = 0;
205             runtime = mktime(&tm);
206             if (runtime < (now - 5 * 60)) /* give 5 min grace to pickup straglers */
207                continue;
208             /* Make sure array is big enough */
209             if (num_runjobs == max_runjobs) {
210                max_runjobs += 10;
211                runjobs = (RUNJOB *) realloc(runjobs, sizeof(RUNJOB) * max_runjobs);
212                if (!runjobs)
213                   Emsg0(M_ABORT, 0, _("Out of memory\n"));
214             }
215             /* accept to run this job */
216             runjobs[num_runjobs].run = run;
217             runjobs[num_runjobs].job = job;
218             runjobs[num_runjobs++].runtime = runtime; /* when to run it */
219
220          }
221       }  
222    }
223
224    UnlockRes();
225    rem_runjobs = num_runjobs;
226    Dmsg0(200, "Leave find_runs()\n");
227 }