xref: /minix3/minix/commands/cron/tab.c (revision d0055759dd8892194db7fce6acc5085d5c9aeaee)
1433d6423SLionel Sambuc /*	tab.c - process crontabs and create in-core crontab data
2433d6423SLionel Sambuc  *							Author: Kees J. Bot
3433d6423SLionel Sambuc  *								7 Dec 1996
4433d6423SLionel Sambuc  * Changes:
5433d6423SLionel Sambuc  * 17 Jul 2000 by Philip Homburg
6433d6423SLionel Sambuc  *	- Tab_reschedule() rewritten (and fixed).
7433d6423SLionel Sambuc  */
8433d6423SLionel Sambuc #define nil ((void*)0)
9433d6423SLionel Sambuc #include <sys/types.h>
10433d6423SLionel Sambuc #include <assert.h>
11433d6423SLionel Sambuc #include <stdio.h>
12433d6423SLionel Sambuc #include <unistd.h>
13433d6423SLionel Sambuc #include <fcntl.h>
14433d6423SLionel Sambuc #include <stdlib.h>
15433d6423SLionel Sambuc #include <string.h>
16433d6423SLionel Sambuc #include <errno.h>
17433d6423SLionel Sambuc #include <limits.h>
18433d6423SLionel Sambuc #include <time.h>
19433d6423SLionel Sambuc #include <dirent.h>
20433d6423SLionel Sambuc #include <sys/stat.h>
21433d6423SLionel Sambuc #include "misc.h"
22433d6423SLionel Sambuc #include "tab.h"
23433d6423SLionel Sambuc 
nextbit(bitmap_t map,int bit)24433d6423SLionel Sambuc static int nextbit(bitmap_t map, int bit)
25433d6423SLionel Sambuc /* Return the next bit set in 'map' from 'bit' onwards, cyclic. */
26433d6423SLionel Sambuc {
27433d6423SLionel Sambuc 	for (;;) {
28433d6423SLionel Sambuc 		bit= (bit+1) & 63;
29433d6423SLionel Sambuc 		if (bit_isset(map, bit)) break;
30433d6423SLionel Sambuc 	}
31433d6423SLionel Sambuc 	return bit;
32433d6423SLionel Sambuc }
33433d6423SLionel Sambuc 
tab_reschedule(cronjob_t * job)34433d6423SLionel Sambuc void tab_reschedule(cronjob_t *job)
35433d6423SLionel Sambuc /* Reschedule one job.  Compute the next time to run the job in job->rtime.
36433d6423SLionel Sambuc  */
37433d6423SLionel Sambuc {
38433d6423SLionel Sambuc 	struct tm prevtm, nexttm, tmptm;
39433d6423SLionel Sambuc 	time_t nodst_rtime, dst_rtime;
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc 	/* AT jobs are only run once. */
42433d6423SLionel Sambuc 	if (job->atjob) { job->rtime= NEVER; return; }
43433d6423SLionel Sambuc 
44433d6423SLionel Sambuc 	/* Was the job scheduled late last time? */
45433d6423SLionel Sambuc 	if (job->late) job->rtime= now;
46433d6423SLionel Sambuc 
47433d6423SLionel Sambuc 	prevtm= *localtime(&job->rtime);
48433d6423SLionel Sambuc 	prevtm.tm_sec= 0;
49433d6423SLionel Sambuc 
50433d6423SLionel Sambuc 	nexttm= prevtm;
51433d6423SLionel Sambuc 	nexttm.tm_min++;	/* Minimal increment */
52433d6423SLionel Sambuc 
53433d6423SLionel Sambuc 	for (;;)
54433d6423SLionel Sambuc 	{
55433d6423SLionel Sambuc 		if (nexttm.tm_min > 59)
56433d6423SLionel Sambuc 		{
57433d6423SLionel Sambuc 			nexttm.tm_min= 0;
58433d6423SLionel Sambuc 			nexttm.tm_hour++;
59433d6423SLionel Sambuc 		}
60433d6423SLionel Sambuc 		if (nexttm.tm_hour > 23)
61433d6423SLionel Sambuc 		{
62433d6423SLionel Sambuc 			nexttm.tm_min= 0;
63433d6423SLionel Sambuc 			nexttm.tm_hour= 0;
64433d6423SLionel Sambuc 			nexttm.tm_mday++;
65433d6423SLionel Sambuc 		}
66433d6423SLionel Sambuc 		if (nexttm.tm_mday > 31)
67433d6423SLionel Sambuc 		{
68433d6423SLionel Sambuc 			nexttm.tm_hour= nexttm.tm_min= 0;
69433d6423SLionel Sambuc 			nexttm.tm_mday= 1;
70433d6423SLionel Sambuc 			nexttm.tm_mon++;
71433d6423SLionel Sambuc 		}
72433d6423SLionel Sambuc 		if (nexttm.tm_mon >= 12)
73433d6423SLionel Sambuc 		{
74433d6423SLionel Sambuc 			nexttm.tm_hour= nexttm.tm_min= 0;
75433d6423SLionel Sambuc 			nexttm.tm_mday= 1;
76433d6423SLionel Sambuc 			nexttm.tm_mon= 0;
77433d6423SLionel Sambuc 			nexttm.tm_year++;
78433d6423SLionel Sambuc 		}
79433d6423SLionel Sambuc 
80433d6423SLionel Sambuc 		/* Verify tm_year. A crontab entry cannot restrict tm_year
81433d6423SLionel Sambuc 		 * directly. However, certain dates (such as Feb, 29th) do
82433d6423SLionel Sambuc 		 * not occur every year. We limit the difference between
83433d6423SLionel Sambuc 		 * nexttm.tm_year and prevtm.tm_year to detect impossible dates
84433d6423SLionel Sambuc 		 * (e.g, Feb, 31st). In theory every date occurs within a
85433d6423SLionel Sambuc 		 * period of 4 years. However, some years at the end of a
86433d6423SLionel Sambuc 		 * century are not leap years (e.g, the year 2100). An extra
87433d6423SLionel Sambuc 		 * factor of 2 should be enough.
88433d6423SLionel Sambuc 		 */
89433d6423SLionel Sambuc 		if (nexttm.tm_year-prevtm.tm_year > 2*4)
90433d6423SLionel Sambuc 		{
91433d6423SLionel Sambuc 			job->rtime= NEVER;
92433d6423SLionel Sambuc 			return;			/* Impossible combination */
93433d6423SLionel Sambuc 		}
94433d6423SLionel Sambuc 
95433d6423SLionel Sambuc 		if (!job->do_wday)
96433d6423SLionel Sambuc 		{
97433d6423SLionel Sambuc 			/* Verify the mon and mday fields. If do_wday and
98433d6423SLionel Sambuc 			 * do_mday are both true we have to merge both
99433d6423SLionel Sambuc 			 * schedules. This is done after the call to mktime.
100433d6423SLionel Sambuc 			 */
101433d6423SLionel Sambuc 			if (!bit_isset(job->mon, nexttm.tm_mon))
102433d6423SLionel Sambuc 			{
103433d6423SLionel Sambuc 				/* Clear other fields */
104433d6423SLionel Sambuc 				nexttm.tm_mday= 1;
105433d6423SLionel Sambuc 				nexttm.tm_hour= nexttm.tm_min= 0;
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc 				nexttm.tm_mon++;
108433d6423SLionel Sambuc 				continue;
109433d6423SLionel Sambuc 			}
110433d6423SLionel Sambuc 
111433d6423SLionel Sambuc 			/* Verify mday */
112433d6423SLionel Sambuc 			if (!bit_isset(job->mday, nexttm.tm_mday))
113433d6423SLionel Sambuc 			{
114433d6423SLionel Sambuc 				/* Clear other fields */
115433d6423SLionel Sambuc 				nexttm.tm_hour= nexttm.tm_min= 0;
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc 				nexttm.tm_mday++;
118433d6423SLionel Sambuc 				continue;
119433d6423SLionel Sambuc 			}
120433d6423SLionel Sambuc 		}
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc 		/* Verify hour */
123433d6423SLionel Sambuc 		if (!bit_isset(job->hour, nexttm.tm_hour))
124433d6423SLionel Sambuc 		{
125433d6423SLionel Sambuc 			/* Clear tm_min field */
126433d6423SLionel Sambuc 			nexttm.tm_min= 0;
127433d6423SLionel Sambuc 
128433d6423SLionel Sambuc 			nexttm.tm_hour++;
129433d6423SLionel Sambuc 			continue;
130433d6423SLionel Sambuc 		}
131433d6423SLionel Sambuc 
132433d6423SLionel Sambuc 		/* Verify min */
133433d6423SLionel Sambuc 		if (!bit_isset(job->min, nexttm.tm_min))
134433d6423SLionel Sambuc 		{
135433d6423SLionel Sambuc 			nexttm.tm_min++;
136433d6423SLionel Sambuc 			continue;
137433d6423SLionel Sambuc 		}
138433d6423SLionel Sambuc 
139433d6423SLionel Sambuc 		/* Verify that we don't have any problem with DST. Try
140433d6423SLionel Sambuc 		 * tm_isdst=0 first. */
141433d6423SLionel Sambuc 		tmptm= nexttm;
142433d6423SLionel Sambuc 		tmptm.tm_isdst= 0;
143433d6423SLionel Sambuc #if 0
144433d6423SLionel Sambuc 		fprintf(stderr,
145433d6423SLionel Sambuc 	"tab_reschedule: trying %04d-%02d-%02d %02d:%02d:%02d isdst=0\n",
146433d6423SLionel Sambuc 				1900+nexttm.tm_year, nexttm.tm_mon+1,
147433d6423SLionel Sambuc 				nexttm.tm_mday, nexttm.tm_hour,
148433d6423SLionel Sambuc 				nexttm.tm_min, nexttm.tm_sec);
149433d6423SLionel Sambuc #endif
150433d6423SLionel Sambuc 		nodst_rtime= job->rtime= mktime(&tmptm);
151433d6423SLionel Sambuc 		if (job->rtime == -1) {
152433d6423SLionel Sambuc 			/* This should not happen. */
153*d0055759SDavid van Moolenbroek 			cronlog(LOG_ERR,
154433d6423SLionel Sambuc 			"mktime failed for %04d-%02d-%02d %02d:%02d:%02d",
155433d6423SLionel Sambuc 				1900+nexttm.tm_year, nexttm.tm_mon+1,
156433d6423SLionel Sambuc 				nexttm.tm_mday, nexttm.tm_hour,
157433d6423SLionel Sambuc 				nexttm.tm_min, nexttm.tm_sec);
158433d6423SLionel Sambuc 			job->rtime= NEVER;
159433d6423SLionel Sambuc 			return;
160433d6423SLionel Sambuc 		}
161433d6423SLionel Sambuc 		tmptm= *localtime(&job->rtime);
162433d6423SLionel Sambuc 		if (tmptm.tm_hour != nexttm.tm_hour ||
163433d6423SLionel Sambuc 			tmptm.tm_min != nexttm.tm_min)
164433d6423SLionel Sambuc 		{
165433d6423SLionel Sambuc 			assert(tmptm.tm_isdst);
166433d6423SLionel Sambuc 			tmptm= nexttm;
167433d6423SLionel Sambuc 			tmptm.tm_isdst= 1;
168433d6423SLionel Sambuc #if 0
169433d6423SLionel Sambuc 			fprintf(stderr,
170433d6423SLionel Sambuc 	"tab_reschedule: trying %04d-%02d-%02d %02d:%02d:%02d isdst=1\n",
171433d6423SLionel Sambuc 				1900+nexttm.tm_year, nexttm.tm_mon+1,
172433d6423SLionel Sambuc 				nexttm.tm_mday, nexttm.tm_hour,
173433d6423SLionel Sambuc 				nexttm.tm_min, nexttm.tm_sec);
174433d6423SLionel Sambuc #endif
175433d6423SLionel Sambuc 			dst_rtime= job->rtime= mktime(&tmptm);
176433d6423SLionel Sambuc 			if (job->rtime == -1) {
177433d6423SLionel Sambuc 				/* This should not happen. */
178*d0055759SDavid van Moolenbroek 				cronlog(LOG_ERR,
179433d6423SLionel Sambuc 			"mktime failed for %04d-%02d-%02d %02d:%02d:%02d\n",
180433d6423SLionel Sambuc 					1900+nexttm.tm_year, nexttm.tm_mon+1,
181433d6423SLionel Sambuc 					nexttm.tm_mday, nexttm.tm_hour,
182433d6423SLionel Sambuc 					nexttm.tm_min, nexttm.tm_sec);
183433d6423SLionel Sambuc 				job->rtime= NEVER;
184433d6423SLionel Sambuc 				return;
185433d6423SLionel Sambuc 			}
186433d6423SLionel Sambuc 			tmptm= *localtime(&job->rtime);
187433d6423SLionel Sambuc 			if (tmptm.tm_hour != nexttm.tm_hour ||
188433d6423SLionel Sambuc 				tmptm.tm_min != nexttm.tm_min)
189433d6423SLionel Sambuc 			{
190433d6423SLionel Sambuc 				assert(!tmptm.tm_isdst);
191433d6423SLionel Sambuc 				/* We have a problem. This time neither
192433d6423SLionel Sambuc 				 * exists with DST nor without DST.
193433d6423SLionel Sambuc 				 * Use the latest time, which should be
194433d6423SLionel Sambuc 				 * nodst_rtime.
195433d6423SLionel Sambuc 				 */
196433d6423SLionel Sambuc 				assert(nodst_rtime > dst_rtime);
197433d6423SLionel Sambuc 				job->rtime= nodst_rtime;
198433d6423SLionel Sambuc #if 0
199433d6423SLionel Sambuc 				fprintf(stderr,
200433d6423SLionel Sambuc 			"During DST trans. %04d-%02d-%02d %02d:%02d:%02d\n",
201433d6423SLionel Sambuc 					1900+nexttm.tm_year, nexttm.tm_mon+1,
202433d6423SLionel Sambuc 					nexttm.tm_mday, nexttm.tm_hour,
203433d6423SLionel Sambuc 					nexttm.tm_min, nexttm.tm_sec);
204433d6423SLionel Sambuc #endif
205433d6423SLionel Sambuc 			}
206433d6423SLionel Sambuc 		}
207433d6423SLionel Sambuc 
208433d6423SLionel Sambuc 		/* Verify this the combination (tm_year, tm_mon, tm_mday). */
209433d6423SLionel Sambuc 		if (tmptm.tm_mday != nexttm.tm_mday ||
210433d6423SLionel Sambuc 			tmptm.tm_mon != nexttm.tm_mon ||
211433d6423SLionel Sambuc 			tmptm.tm_year != nexttm.tm_year)
212433d6423SLionel Sambuc 		{
213433d6423SLionel Sambuc 			/* Wrong day */
214433d6423SLionel Sambuc #if 0
215433d6423SLionel Sambuc 			fprintf(stderr, "Wrong day\n");
216433d6423SLionel Sambuc #endif
217433d6423SLionel Sambuc 			nexttm.tm_hour= nexttm.tm_min= 0;
218433d6423SLionel Sambuc 			nexttm.tm_mday++;
219433d6423SLionel Sambuc 			continue;
220433d6423SLionel Sambuc 		}
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc 		/* Check tm_wday */
223433d6423SLionel Sambuc 		if (job->do_wday && bit_isset(job->wday, tmptm.tm_wday))
224433d6423SLionel Sambuc 		{
225433d6423SLionel Sambuc 			/* OK, wday matched */
226433d6423SLionel Sambuc 			break;
227433d6423SLionel Sambuc 		}
228433d6423SLionel Sambuc 
229433d6423SLionel Sambuc 		/* Check tm_mday */
230433d6423SLionel Sambuc 		if (job->do_mday && bit_isset(job->mon, tmptm.tm_mon) &&
231433d6423SLionel Sambuc 			bit_isset(job->mday, tmptm.tm_mday))
232433d6423SLionel Sambuc 		{
233433d6423SLionel Sambuc 			/* OK, mon and mday matched */
234433d6423SLionel Sambuc 			break;
235433d6423SLionel Sambuc 		}
236433d6423SLionel Sambuc 
237433d6423SLionel Sambuc 		if (!job->do_wday && !job->do_mday)
238433d6423SLionel Sambuc 		{
239433d6423SLionel Sambuc 			/* No need to match wday and mday */
240433d6423SLionel Sambuc 			break;
241433d6423SLionel Sambuc 		}
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc 		/* Wrong day */
244433d6423SLionel Sambuc #if 0
245433d6423SLionel Sambuc 		fprintf(stderr, "Wrong mon+mday or wday\n");
246433d6423SLionel Sambuc #endif
247433d6423SLionel Sambuc 		nexttm.tm_hour= nexttm.tm_min= 0;
248433d6423SLionel Sambuc 		nexttm.tm_mday++;
249433d6423SLionel Sambuc 	}
250433d6423SLionel Sambuc #if 0
251433d6423SLionel Sambuc 	fprintf(stderr, "Using %04d-%02d-%02d %02d:%02d:%02d \n",
252433d6423SLionel Sambuc 		1900+nexttm.tm_year, nexttm.tm_mon+1, nexttm.tm_mday,
253433d6423SLionel Sambuc 		nexttm.tm_hour, nexttm.tm_min, nexttm.tm_sec);
254433d6423SLionel Sambuc 	tmptm= *localtime(&job->rtime);
255433d6423SLionel Sambuc 	fprintf(stderr, "Act. %04d-%02d-%02d %02d:%02d:%02d isdst=%d\n",
256433d6423SLionel Sambuc 		1900+tmptm.tm_year, tmptm.tm_mon+1, tmptm.tm_mday,
257433d6423SLionel Sambuc 		tmptm.tm_hour, tmptm.tm_min, tmptm.tm_sec,
258433d6423SLionel Sambuc 		tmptm.tm_isdst);
259433d6423SLionel Sambuc #endif
260433d6423SLionel Sambuc 
261433d6423SLionel Sambuc 
262433d6423SLionel Sambuc 	/* Is job issuing lagging behind with the progress of time? */
263433d6423SLionel Sambuc 	job->late= (job->rtime < now);
264433d6423SLionel Sambuc 
265433d6423SLionel Sambuc   	/* The result is in job->rtime. */
266433d6423SLionel Sambuc   	if (job->rtime < next) next= job->rtime;
267433d6423SLionel Sambuc }
268433d6423SLionel Sambuc 
269433d6423SLionel Sambuc #define isdigit(c)	((unsigned) ((c) - '0') < 10)
270433d6423SLionel Sambuc 
get_token(char ** ptr)271433d6423SLionel Sambuc static char *get_token(char **ptr)
272433d6423SLionel Sambuc /* Return a pointer to the next token in a string.  Move *ptr to the end of
273433d6423SLionel Sambuc  * the token, and return a pointer to the start.  If *ptr == start of token
274433d6423SLionel Sambuc  * then we're stuck against a newline or end of string.
275433d6423SLionel Sambuc  */
276433d6423SLionel Sambuc {
277433d6423SLionel Sambuc 	char *start, *p;
278433d6423SLionel Sambuc 
279433d6423SLionel Sambuc 	p= *ptr;
280433d6423SLionel Sambuc 	while (*p == ' ' || *p == '\t') p++;
281433d6423SLionel Sambuc 
282433d6423SLionel Sambuc 	start= p;
283433d6423SLionel Sambuc 	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != 0) p++;
284433d6423SLionel Sambuc 	*ptr= p;
285433d6423SLionel Sambuc 	return start;
286433d6423SLionel Sambuc }
287433d6423SLionel Sambuc 
range_parse(char * file,char * data,bitmap_t map,int min,int max,int * wildcard)288433d6423SLionel Sambuc static int range_parse(char *file, char *data, bitmap_t map,
289433d6423SLionel Sambuc 	int min, int max, int *wildcard)
290433d6423SLionel Sambuc /* Parse a comma separated series of 'n', 'n-m' or 'n:m' numbers.  'n'
291433d6423SLionel Sambuc  * includes number 'n' in the bit map, 'n-m' includes all numbers between
292433d6423SLionel Sambuc  * 'n' and 'm' inclusive, and 'n:m' includes 'n+k*m' for any k if in range.
293433d6423SLionel Sambuc  * Numbers must fall between 'min' and 'max'.  A '*' means all numbers.  A
294433d6423SLionel Sambuc  * '?' is allowed as a synonym for the current minute, which only makes sense
295433d6423SLionel Sambuc  * in the minute field, i.e. max must be 59.  Return true iff parsed ok.
296433d6423SLionel Sambuc  */
297433d6423SLionel Sambuc {
298433d6423SLionel Sambuc 	char *p;
299433d6423SLionel Sambuc 	int end;
300433d6423SLionel Sambuc 	int n, m;
301433d6423SLionel Sambuc 
302433d6423SLionel Sambuc 	/* Clear all bits. */
303433d6423SLionel Sambuc 	for (n= 0; n < 8; n++) map[n]= 0;
304433d6423SLionel Sambuc 
305433d6423SLionel Sambuc 	p= data;
306433d6423SLionel Sambuc 	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != 0) p++;
307433d6423SLionel Sambuc 	end= *p;
308433d6423SLionel Sambuc 	*p= 0;
309433d6423SLionel Sambuc 	p= data;
310433d6423SLionel Sambuc 
311433d6423SLionel Sambuc 	if (*p == 0) {
312*d0055759SDavid van Moolenbroek 		cronlog(LOG_ERR, "%s: not enough time fields\n", file);
313433d6423SLionel Sambuc 		return 0;
314433d6423SLionel Sambuc 	}
315433d6423SLionel Sambuc 
316433d6423SLionel Sambuc 	/* Is it a '*'? */
317433d6423SLionel Sambuc 	if (p[0] == '*' && p[1] == 0) {
318433d6423SLionel Sambuc 		for (n= min; n <= max; n++) bit_set(map, n);
319433d6423SLionel Sambuc 		p[1]= end;
320433d6423SLionel Sambuc 		*wildcard= 1;
321433d6423SLionel Sambuc 		return 1;
322433d6423SLionel Sambuc 	}
323433d6423SLionel Sambuc 	*wildcard= 0;
324433d6423SLionel Sambuc 
325433d6423SLionel Sambuc 	/* Parse a comma separated series of numbers or ranges. */
326433d6423SLionel Sambuc 	for (;;) {
327433d6423SLionel Sambuc 		if (*p == '?' && max == 59 && p[1] != '-') {
328433d6423SLionel Sambuc 			n= localtime(&now)->tm_min;
329433d6423SLionel Sambuc 			p++;
330433d6423SLionel Sambuc 		} else {
331433d6423SLionel Sambuc 			if (!isdigit(*p)) goto syntax;
332433d6423SLionel Sambuc 			n= 0;
333433d6423SLionel Sambuc 			do {
334433d6423SLionel Sambuc 				n= 10 * n + (*p++ - '0');
335433d6423SLionel Sambuc 				if (n > max) goto range;
336433d6423SLionel Sambuc 			} while (isdigit(*p));
337433d6423SLionel Sambuc 		}
338433d6423SLionel Sambuc 		if (n < min) goto range;
339433d6423SLionel Sambuc 
340433d6423SLionel Sambuc 		if (*p == '-') {	/* A range of the form 'n-m'? */
341433d6423SLionel Sambuc 			p++;
342433d6423SLionel Sambuc 			if (!isdigit(*p)) goto syntax;
343433d6423SLionel Sambuc 			m= 0;
344433d6423SLionel Sambuc 			do {
345433d6423SLionel Sambuc 				m= 10 * m + (*p++ - '0');
346433d6423SLionel Sambuc 				if (m > max) goto range;
347433d6423SLionel Sambuc 			} while (isdigit(*p));
348433d6423SLionel Sambuc 			if (m < n) goto range;
349433d6423SLionel Sambuc 			do {
350433d6423SLionel Sambuc 				bit_set(map, n);
351433d6423SLionel Sambuc 			} while (++n <= m);
352433d6423SLionel Sambuc 		} else
353433d6423SLionel Sambuc 		if (*p == ':') {	/* A repeat of the form 'n:m'? */
354433d6423SLionel Sambuc 			p++;
355433d6423SLionel Sambuc 			if (!isdigit(*p)) goto syntax;
356433d6423SLionel Sambuc 			m= 0;
357433d6423SLionel Sambuc 			do {
358433d6423SLionel Sambuc 				m= 10 * m + (*p++ - '0');
359433d6423SLionel Sambuc 				if (m > (max-min+1)) goto range;
360433d6423SLionel Sambuc 			} while (isdigit(*p));
361433d6423SLionel Sambuc 			if (m == 0) goto range;
362433d6423SLionel Sambuc 			while (n >= min) n-= m;
363433d6423SLionel Sambuc 			while ((n+= m) <= max) bit_set(map, n);
364433d6423SLionel Sambuc 		} else {
365433d6423SLionel Sambuc 					/* Simply a number */
366433d6423SLionel Sambuc 			bit_set(map, n);
367433d6423SLionel Sambuc 		}
368433d6423SLionel Sambuc 		if (*p == 0) break;
369433d6423SLionel Sambuc 		if (*p++ != ',') goto syntax;
370433d6423SLionel Sambuc 	}
371433d6423SLionel Sambuc 	*p= end;
372433d6423SLionel Sambuc 	return 1;
373433d6423SLionel Sambuc   syntax:
374*d0055759SDavid van Moolenbroek 	cronlog(LOG_ERR, "%s: field '%s': bad syntax for a %d-%d time field\n",
375433d6423SLionel Sambuc 		file, data, min, max);
376433d6423SLionel Sambuc 	return 0;
377433d6423SLionel Sambuc   range:
378*d0055759SDavid van Moolenbroek 	cronlog(LOG_ERR,
379*d0055759SDavid van Moolenbroek 		"%s: field '%s': values out of the %d-%d allowed range\n",
380433d6423SLionel Sambuc 		file, data, min, max);
381433d6423SLionel Sambuc 	return 0;
382433d6423SLionel Sambuc }
383433d6423SLionel Sambuc 
tab_parse(char * file,char * user)384433d6423SLionel Sambuc void tab_parse(char *file, char *user)
385433d6423SLionel Sambuc /* Parse a crontab file and add its data to the tables.  Handle errors by
386433d6423SLionel Sambuc  * yourself.  Table is owned by 'user' if non-null.
387433d6423SLionel Sambuc  */
388433d6423SLionel Sambuc {
389433d6423SLionel Sambuc 	crontab_t **atab, *tab;
390433d6423SLionel Sambuc 	cronjob_t **ajob, *job;
391433d6423SLionel Sambuc 	int fd;
392433d6423SLionel Sambuc 	struct stat st;
393433d6423SLionel Sambuc 	char *p, *q;
394433d6423SLionel Sambuc 	size_t n;
395433d6423SLionel Sambuc 	ssize_t r;
396433d6423SLionel Sambuc 	int ok, wc;
397433d6423SLionel Sambuc 
398433d6423SLionel Sambuc 	for (atab= &crontabs; (tab= *atab) != nil; atab= &tab->next) {
399433d6423SLionel Sambuc 		if (strcmp(file, tab->file) == 0) break;
400433d6423SLionel Sambuc 	}
401433d6423SLionel Sambuc 
402433d6423SLionel Sambuc 	/* Try to open the file. */
403433d6423SLionel Sambuc 	if ((fd= open(file, O_RDONLY)) < 0 || fstat(fd, &st) < 0) {
404433d6423SLionel Sambuc 		if (errno != ENOENT) {
405*d0055759SDavid van Moolenbroek 			cronlog(LOG_ERR, "%s: %s\n", file, strerror(errno));
406433d6423SLionel Sambuc 		}
407433d6423SLionel Sambuc 		if (fd != -1) close(fd);
408433d6423SLionel Sambuc 		return;
409433d6423SLionel Sambuc 	}
410433d6423SLionel Sambuc 
411433d6423SLionel Sambuc 	/* Forget it if the file is awfully big. */
412433d6423SLionel Sambuc 	if (st.st_size > TAB_MAX) {
413*d0055759SDavid van Moolenbroek 		cronlog(LOG_ERR, "%s: %lu bytes is bigger than my %lu limit\n",
414433d6423SLionel Sambuc 			file,
415433d6423SLionel Sambuc 			(unsigned long) st.st_size,
416433d6423SLionel Sambuc 			(unsigned long) TAB_MAX);
417433d6423SLionel Sambuc 		return;
418433d6423SLionel Sambuc 	}
419433d6423SLionel Sambuc 
420433d6423SLionel Sambuc 	/* If the file is the same as before then don't bother. */
421433d6423SLionel Sambuc 	if (tab != nil && st.st_mtime == tab->mtime) {
422433d6423SLionel Sambuc 		close(fd);
423433d6423SLionel Sambuc 		tab->current= 1;
424433d6423SLionel Sambuc 		return;
425433d6423SLionel Sambuc 	}
426433d6423SLionel Sambuc 
427433d6423SLionel Sambuc 	/* Create a new table structure. */
428433d6423SLionel Sambuc 	tab= allocate(sizeof(*tab));
429433d6423SLionel Sambuc 	tab->file= allocate((strlen(file) + 1) * sizeof(tab->file[0]));
430433d6423SLionel Sambuc 	strcpy(tab->file, file);
431433d6423SLionel Sambuc 	tab->user= nil;
432433d6423SLionel Sambuc 	if (user != nil) {
433433d6423SLionel Sambuc 		tab->user= allocate((strlen(user) + 1) * sizeof(tab->user[0]));
434433d6423SLionel Sambuc 		strcpy(tab->user, user);
435433d6423SLionel Sambuc 	}
436433d6423SLionel Sambuc 	tab->data= allocate((st.st_size + 1) * sizeof(tab->data[0]));
437433d6423SLionel Sambuc 	tab->jobs= nil;
438433d6423SLionel Sambuc 	tab->mtime= st.st_mtime;
439433d6423SLionel Sambuc 	tab->current= 0;
440433d6423SLionel Sambuc 	tab->next= *atab;
441433d6423SLionel Sambuc 	*atab= tab;
442433d6423SLionel Sambuc 
443433d6423SLionel Sambuc 	/* Pull a new table in core. */
444433d6423SLionel Sambuc 	n= 0;
445433d6423SLionel Sambuc 	while (n < st.st_size) {
446433d6423SLionel Sambuc 		if ((r = read(fd, tab->data + n, st.st_size - n)) < 0) {
447*d0055759SDavid van Moolenbroek 			cronlog(LOG_CRIT, "%s: %s", file, strerror(errno));
448433d6423SLionel Sambuc 			close(fd);
449433d6423SLionel Sambuc 			return;
450433d6423SLionel Sambuc 		}
451433d6423SLionel Sambuc 		if (r == 0) break;
452433d6423SLionel Sambuc 		n+= r;
453433d6423SLionel Sambuc 	}
454433d6423SLionel Sambuc 	close(fd);
455433d6423SLionel Sambuc 	tab->data[n]= 0;
456433d6423SLionel Sambuc 	if (strlen(tab->data) < n) {
457*d0055759SDavid van Moolenbroek 		cronlog(LOG_ERR, "%s contains a null character\n", file);
458433d6423SLionel Sambuc 		return;
459433d6423SLionel Sambuc 	}
460433d6423SLionel Sambuc 
461433d6423SLionel Sambuc 	/* Parse the file. */
462433d6423SLionel Sambuc 	ajob= &tab->jobs;
463433d6423SLionel Sambuc 	p= tab->data;
464433d6423SLionel Sambuc 	ok= 1;
465433d6423SLionel Sambuc 	while (ok && *p != 0) {
466433d6423SLionel Sambuc 		q= get_token(&p);
467433d6423SLionel Sambuc 		if (*q == '#' || q == p) {
468433d6423SLionel Sambuc 			/* Comment or empty. */
469433d6423SLionel Sambuc 			while (*p != 0 && *p++ != '\n') {}
470433d6423SLionel Sambuc 			continue;
471433d6423SLionel Sambuc 		}
472433d6423SLionel Sambuc 
473433d6423SLionel Sambuc 		/* One new job coming up. */
474433d6423SLionel Sambuc 		*ajob= job= allocate(sizeof(*job));
475433d6423SLionel Sambuc 		*(ajob= &job->next)= nil;
476433d6423SLionel Sambuc 		job->tab= tab;
477433d6423SLionel Sambuc 
478433d6423SLionel Sambuc 		if (!range_parse(file, q, job->min, 0, 59, &wc)) {
479433d6423SLionel Sambuc 			ok= 0;
480433d6423SLionel Sambuc 			break;
481433d6423SLionel Sambuc 		}
482433d6423SLionel Sambuc 
483433d6423SLionel Sambuc 		q= get_token(&p);
484433d6423SLionel Sambuc 		if (!range_parse(file, q, job->hour, 0, 23, &wc)) {
485433d6423SLionel Sambuc 			ok= 0;
486433d6423SLionel Sambuc 			break;
487433d6423SLionel Sambuc 		}
488433d6423SLionel Sambuc 
489433d6423SLionel Sambuc 		q= get_token(&p);
490433d6423SLionel Sambuc 		if (!range_parse(file, q, job->mday, 1, 31, &wc)) {
491433d6423SLionel Sambuc 			ok= 0;
492433d6423SLionel Sambuc 			break;
493433d6423SLionel Sambuc 		}
494433d6423SLionel Sambuc 		job->do_mday= !wc;
495433d6423SLionel Sambuc 
496433d6423SLionel Sambuc 		q= get_token(&p);
497433d6423SLionel Sambuc 		if (!range_parse(file, q, job->mon, 1, 12, &wc)) {
498433d6423SLionel Sambuc 			ok= 0;
499433d6423SLionel Sambuc 			break;
500433d6423SLionel Sambuc 		}
501433d6423SLionel Sambuc 		job->do_mday |= !wc;
502433d6423SLionel Sambuc 
503433d6423SLionel Sambuc 		q= get_token(&p);
504433d6423SLionel Sambuc 		if (!range_parse(file, q, job->wday, 0, 7, &wc)) {
505433d6423SLionel Sambuc 			ok= 0;
506433d6423SLionel Sambuc 			break;
507433d6423SLionel Sambuc 		}
508433d6423SLionel Sambuc 		job->do_wday= !wc;
509433d6423SLionel Sambuc 
510433d6423SLionel Sambuc 		/* 7 is Sunday, but 0 is a common mistake because it is in the
511433d6423SLionel Sambuc 		 * tm_wday range.  We allow and even prefer it internally.
512433d6423SLionel Sambuc 		 */
513433d6423SLionel Sambuc 		if (bit_isset(job->wday, 7)) {
514433d6423SLionel Sambuc 			bit_clr(job->wday, 7);
515433d6423SLionel Sambuc 			bit_set(job->wday, 0);
516433d6423SLionel Sambuc 		}
517433d6423SLionel Sambuc 
518433d6423SLionel Sambuc 		/* The month range is 1-12, but tm_mon likes 0-11. */
519433d6423SLionel Sambuc 		job->mon[0] >>= 1;
520433d6423SLionel Sambuc 		if (bit_isset(job->mon, 8)) bit_set(job->mon, 7);
521433d6423SLionel Sambuc 		job->mon[1] >>= 1;
522433d6423SLionel Sambuc 
523433d6423SLionel Sambuc 		/* Scan for options. */
524433d6423SLionel Sambuc 		job->user= nil;
525433d6423SLionel Sambuc 		while (q= get_token(&p), *q == '-') {
526433d6423SLionel Sambuc 			q++;
527433d6423SLionel Sambuc 			if (q[0] == '-' && q+1 == p) {
528433d6423SLionel Sambuc 				/* -- */
529433d6423SLionel Sambuc 				q= get_token(&p);
530433d6423SLionel Sambuc 				break;
531433d6423SLionel Sambuc 			}
532433d6423SLionel Sambuc 			while (q < p) switch (*q++) {
533433d6423SLionel Sambuc 			case 'u':
534433d6423SLionel Sambuc 				if (q == p) q= get_token(&p);
535433d6423SLionel Sambuc 				if (q == p) goto usage;
536433d6423SLionel Sambuc 				memmove(q-1, q, p-q);	/* gross... */
537433d6423SLionel Sambuc 				p[-1]= 0;
538433d6423SLionel Sambuc 				job->user= q-1;
539433d6423SLionel Sambuc 				q= p;
540433d6423SLionel Sambuc 				break;
541433d6423SLionel Sambuc 			default:
542433d6423SLionel Sambuc 			usage:
543*d0055759SDavid van Moolenbroek 				cronlog(LOG_ERR,
544433d6423SLionel Sambuc 			"%s: bad option -%c, good options are: -u username\n",
545433d6423SLionel Sambuc 					file, q[-1]);
546433d6423SLionel Sambuc 				ok= 0;
547433d6423SLionel Sambuc 				goto endtab;
548433d6423SLionel Sambuc 			}
549433d6423SLionel Sambuc 		}
550433d6423SLionel Sambuc 
551433d6423SLionel Sambuc 		/* A crontab owned by a user can only do things as that user. */
552433d6423SLionel Sambuc 		if (tab->user != nil) job->user= tab->user;
553433d6423SLionel Sambuc 
554433d6423SLionel Sambuc 		/* Inspect the first character of the command. */
555433d6423SLionel Sambuc 		job->cmd= q;
556433d6423SLionel Sambuc 		if (q == p || *q == '#') {
557433d6423SLionel Sambuc 			/* Rest of the line is empty, i.e. the commands are on
558433d6423SLionel Sambuc 			 * the following lines indented by one tab.
559433d6423SLionel Sambuc 			 */
560433d6423SLionel Sambuc 			while (*p != 0 && *p++ != '\n') {}
561433d6423SLionel Sambuc 			if (*p++ != '\t') {
562*d0055759SDavid van Moolenbroek 				cronlog(LOG_ERR,
563*d0055759SDavid van Moolenbroek 					"%s: contains an empty command\n",
564433d6423SLionel Sambuc 					file);
565433d6423SLionel Sambuc 				ok= 0;
566433d6423SLionel Sambuc 				goto endtab;
567433d6423SLionel Sambuc 			}
568433d6423SLionel Sambuc 			while (*p != 0) {
569433d6423SLionel Sambuc 				if ((*q = *p++) == '\n') {
570433d6423SLionel Sambuc 					if (*p != '\t') break;
571433d6423SLionel Sambuc 					p++;
572433d6423SLionel Sambuc 				}
573433d6423SLionel Sambuc 				q++;
574433d6423SLionel Sambuc 			}
575433d6423SLionel Sambuc 		} else {
576433d6423SLionel Sambuc 			/* The command is on this line.  Alas we must now be
577433d6423SLionel Sambuc 			 * backwards compatible and change %'s to newlines.
578433d6423SLionel Sambuc 			 */
579433d6423SLionel Sambuc 			p= q;
580433d6423SLionel Sambuc 			while (*p != 0) {
581433d6423SLionel Sambuc 				if ((*q = *p++) == '\n') break;
582433d6423SLionel Sambuc 				if (*q == '%') *q= '\n';
583433d6423SLionel Sambuc 				q++;
584433d6423SLionel Sambuc 			}
585433d6423SLionel Sambuc 		}
586433d6423SLionel Sambuc 		*q= 0;
587433d6423SLionel Sambuc 		job->rtime= now;
588433d6423SLionel Sambuc 		job->late= 0;		/* It is on time. */
589433d6423SLionel Sambuc 		job->atjob= 0;		/* True cron job. */
590433d6423SLionel Sambuc 		job->pid= IDLE_PID;	/* Not running yet. */
591433d6423SLionel Sambuc 		tab_reschedule(job);	/* Compute next time to run. */
592433d6423SLionel Sambuc 	}
593433d6423SLionel Sambuc   endtab:
594433d6423SLionel Sambuc 
595433d6423SLionel Sambuc 	if (ok) tab->current= 1;
596433d6423SLionel Sambuc }
597433d6423SLionel Sambuc 
tab_find_atjob(char * atdir)598433d6423SLionel Sambuc void tab_find_atjob(char *atdir)
599433d6423SLionel Sambuc /* Find the first to be executed AT job and kludge up an crontab job for it.
600433d6423SLionel Sambuc  * We set tab->file to "atdir/jobname", tab->data to "atdir/past/jobname",
601433d6423SLionel Sambuc  * and job->cmd to "jobname".
602433d6423SLionel Sambuc  */
603433d6423SLionel Sambuc {
604433d6423SLionel Sambuc 	DIR *spool;
605433d6423SLionel Sambuc 	struct dirent *entry;
606433d6423SLionel Sambuc 	time_t t0, t1;
607433d6423SLionel Sambuc 	struct tm tmnow, tmt1;
608433d6423SLionel Sambuc 	static char template[] = "96.365.1546.00";
609433d6423SLionel Sambuc 	char firstjob[sizeof(template)];
610433d6423SLionel Sambuc 	int i;
611433d6423SLionel Sambuc 	crontab_t *tab;
612433d6423SLionel Sambuc 	cronjob_t *job;
613433d6423SLionel Sambuc 
614433d6423SLionel Sambuc 	if ((spool= opendir(atdir)) == nil) return;
615433d6423SLionel Sambuc 
616433d6423SLionel Sambuc 	tmnow= *localtime(&now);
617433d6423SLionel Sambuc 	t0= NEVER;
618433d6423SLionel Sambuc 
619433d6423SLionel Sambuc 	while ((entry= readdir(spool)) != nil) {
620433d6423SLionel Sambuc 		/* Check if the name fits the template. */
621433d6423SLionel Sambuc 		for (i= 0; template[i] != 0; i++) {
622433d6423SLionel Sambuc 			if (template[i] == '.') {
623433d6423SLionel Sambuc 				if (entry->d_name[i] != '.') break;
624433d6423SLionel Sambuc 			} else {
625433d6423SLionel Sambuc 				if (!isdigit(entry->d_name[i])) break;
626433d6423SLionel Sambuc 			}
627433d6423SLionel Sambuc 		}
628433d6423SLionel Sambuc 		if (template[i] != 0 || entry->d_name[i] != 0) continue;
629433d6423SLionel Sambuc 
630433d6423SLionel Sambuc 		/* Convert the name to a time.  Careful with the century. */
631433d6423SLionel Sambuc 		memset(&tmt1, 0, sizeof(tmt1));
632433d6423SLionel Sambuc 		tmt1.tm_year= atoi(entry->d_name+0);
633433d6423SLionel Sambuc 		while (tmt1.tm_year < tmnow.tm_year-10) tmt1.tm_year+= 100;
634433d6423SLionel Sambuc 		tmt1.tm_mday= 1+atoi(entry->d_name+3);
635433d6423SLionel Sambuc 		tmt1.tm_min= atoi(entry->d_name+7);
636433d6423SLionel Sambuc 		tmt1.tm_hour= tmt1.tm_min / 100;
637433d6423SLionel Sambuc 		tmt1.tm_min%= 100;
638433d6423SLionel Sambuc 		tmt1.tm_isdst= -1;
639433d6423SLionel Sambuc 		if ((t1= mktime(&tmt1)) == -1) {
640433d6423SLionel Sambuc 			/* Illegal time?  Try in winter time. */
641433d6423SLionel Sambuc 			tmt1.tm_isdst= 0;
642433d6423SLionel Sambuc 			if ((t1= mktime(&tmt1)) == -1) continue;
643433d6423SLionel Sambuc 		}
644433d6423SLionel Sambuc 		if (t1 < t0) {
645433d6423SLionel Sambuc 			t0= t1;
646433d6423SLionel Sambuc 			strcpy(firstjob, entry->d_name);
647433d6423SLionel Sambuc 		}
648433d6423SLionel Sambuc 	}
649433d6423SLionel Sambuc 	closedir(spool);
650433d6423SLionel Sambuc 
651433d6423SLionel Sambuc 	if (t0 == NEVER) return;	/* AT job spool is empty. */
652433d6423SLionel Sambuc 
653433d6423SLionel Sambuc 	/* Create new table and job structures. */
654433d6423SLionel Sambuc 	tab= allocate(sizeof(*tab));
655433d6423SLionel Sambuc 	tab->file= allocate((strlen(atdir) + 1 + sizeof(template))
656433d6423SLionel Sambuc 						* sizeof(tab->file[0]));
657433d6423SLionel Sambuc 	strcpy(tab->file, atdir);
658433d6423SLionel Sambuc 	strcat(tab->file, "/");
659433d6423SLionel Sambuc 	strcat(tab->file, firstjob);
660433d6423SLionel Sambuc 	tab->data= allocate((strlen(atdir) + 6 + sizeof(template))
661433d6423SLionel Sambuc 						* sizeof(tab->data[0]));
662433d6423SLionel Sambuc 	strcpy(tab->data, atdir);
663433d6423SLionel Sambuc 	strcat(tab->data, "/past/");
664433d6423SLionel Sambuc 	strcat(tab->data, firstjob);
665433d6423SLionel Sambuc 	tab->user= nil;
666433d6423SLionel Sambuc 	tab->mtime= 0;
667433d6423SLionel Sambuc 	tab->current= 1;
668433d6423SLionel Sambuc 	tab->next= crontabs;
669433d6423SLionel Sambuc 	crontabs= tab;
670433d6423SLionel Sambuc 
671433d6423SLionel Sambuc 	tab->jobs= job= allocate(sizeof(*job));
672433d6423SLionel Sambuc 	job->next= nil;
673433d6423SLionel Sambuc 	job->tab= tab;
674433d6423SLionel Sambuc 	job->user= nil;
675433d6423SLionel Sambuc 	job->cmd= tab->data + strlen(atdir) + 6;
676433d6423SLionel Sambuc 	job->rtime= t0;
677433d6423SLionel Sambuc 	job->late= 0;
678433d6423SLionel Sambuc 	job->atjob= 1;		/* One AT job disguised as a cron job. */
679433d6423SLionel Sambuc 	job->pid= IDLE_PID;
680433d6423SLionel Sambuc 
681433d6423SLionel Sambuc 	if (job->rtime < next) next= job->rtime;
682433d6423SLionel Sambuc }
683433d6423SLionel Sambuc 
tab_purge(void)684433d6423SLionel Sambuc void tab_purge(void)
685433d6423SLionel Sambuc /* Remove table data that is no longer current.  E.g. a crontab got removed.
686433d6423SLionel Sambuc  */
687433d6423SLionel Sambuc {
688433d6423SLionel Sambuc 	crontab_t **atab, *tab;
689433d6423SLionel Sambuc 	cronjob_t *job;
690433d6423SLionel Sambuc 
691433d6423SLionel Sambuc 	atab= &crontabs;
692433d6423SLionel Sambuc 	while ((tab= *atab) != nil) {
693433d6423SLionel Sambuc 		if (tab->current) {
694433d6423SLionel Sambuc 			/* Table is fine. */
695433d6423SLionel Sambuc 			tab->current= 0;
696433d6423SLionel Sambuc 			atab= &tab->next;
697433d6423SLionel Sambuc 		} else {
698433d6423SLionel Sambuc 			/* Table is not, or no longer valid; delete. */
699433d6423SLionel Sambuc 			*atab= tab->next;
700433d6423SLionel Sambuc 			while ((job= tab->jobs) != nil) {
701433d6423SLionel Sambuc 				tab->jobs= job->next;
702433d6423SLionel Sambuc 				deallocate(job);
703433d6423SLionel Sambuc 			}
704433d6423SLionel Sambuc 			deallocate(tab->data);
705433d6423SLionel Sambuc 			deallocate(tab->file);
706433d6423SLionel Sambuc 			deallocate(tab->user);
707433d6423SLionel Sambuc 			deallocate(tab);
708433d6423SLionel Sambuc 		}
709433d6423SLionel Sambuc 	}
710433d6423SLionel Sambuc }
711433d6423SLionel Sambuc 
reap_or_find(pid_t pid)712433d6423SLionel Sambuc static cronjob_t *reap_or_find(pid_t pid)
713433d6423SLionel Sambuc /* Find a finished job or search for the next one to run. */
714433d6423SLionel Sambuc {
715433d6423SLionel Sambuc 	crontab_t *tab;
716433d6423SLionel Sambuc 	cronjob_t *job;
717433d6423SLionel Sambuc 	cronjob_t *nextjob;
718433d6423SLionel Sambuc 
719433d6423SLionel Sambuc 	nextjob= nil;
720433d6423SLionel Sambuc 	next= NEVER;
721433d6423SLionel Sambuc 	for (tab= crontabs; tab != nil; tab= tab->next) {
722433d6423SLionel Sambuc 		for (job= tab->jobs; job != nil; job= job->next) {
723433d6423SLionel Sambuc 			if (job->pid == pid) {
724433d6423SLionel Sambuc 				job->pid= IDLE_PID;
725433d6423SLionel Sambuc 				tab_reschedule(job);
726433d6423SLionel Sambuc 			}
727433d6423SLionel Sambuc 			if (job->pid != IDLE_PID) continue;
728433d6423SLionel Sambuc 			if (job->rtime < next) next= job->rtime;
729433d6423SLionel Sambuc 			if (job->rtime <= now) nextjob= job;
730433d6423SLionel Sambuc 		}
731433d6423SLionel Sambuc 	}
732433d6423SLionel Sambuc 	return nextjob;
733433d6423SLionel Sambuc }
734433d6423SLionel Sambuc 
tab_reap_job(pid_t pid)735433d6423SLionel Sambuc void tab_reap_job(pid_t pid)
736433d6423SLionel Sambuc /* A job has finished.  Try to find it among the crontab data and reschedule
737433d6423SLionel Sambuc  * it.  Recompute time next to run a job.
738433d6423SLionel Sambuc  */
739433d6423SLionel Sambuc {
740433d6423SLionel Sambuc 	(void) reap_or_find(pid);
741433d6423SLionel Sambuc }
742433d6423SLionel Sambuc 
tab_nextjob(void)743433d6423SLionel Sambuc cronjob_t *tab_nextjob(void)
744433d6423SLionel Sambuc /* Find a job that should be run now.  If none are found return null.
745433d6423SLionel Sambuc  * Update 'next'.
746433d6423SLionel Sambuc  */
747433d6423SLionel Sambuc {
748433d6423SLionel Sambuc 	return reap_or_find(NO_PID);
749433d6423SLionel Sambuc }
750433d6423SLionel Sambuc 
pr_map(FILE * fp,bitmap_t map)751433d6423SLionel Sambuc static void pr_map(FILE *fp, bitmap_t map)
752433d6423SLionel Sambuc {
753433d6423SLionel Sambuc 	int last_bit= -1, bit;
754433d6423SLionel Sambuc 	char *sep;
755433d6423SLionel Sambuc 
756433d6423SLionel Sambuc 	sep= "";
757433d6423SLionel Sambuc 	for (bit= 0; bit < 64; bit++) {
758433d6423SLionel Sambuc 		if (bit_isset(map, bit)) {
759433d6423SLionel Sambuc 			if (last_bit == -1) last_bit= bit;
760433d6423SLionel Sambuc 		} else {
761433d6423SLionel Sambuc 			if (last_bit != -1) {
762433d6423SLionel Sambuc 				fprintf(fp, "%s%d", sep, last_bit);
763433d6423SLionel Sambuc 				if (last_bit != bit-1) {
764433d6423SLionel Sambuc 					fprintf(fp, "-%d", bit-1);
765433d6423SLionel Sambuc 				}
766433d6423SLionel Sambuc 				last_bit= -1;
767433d6423SLionel Sambuc 				sep= ",";
768433d6423SLionel Sambuc 			}
769433d6423SLionel Sambuc 		}
770433d6423SLionel Sambuc 	}
771433d6423SLionel Sambuc }
772433d6423SLionel Sambuc 
tab_print(FILE * fp)773433d6423SLionel Sambuc void tab_print(FILE *fp)
774433d6423SLionel Sambuc /* Print out a stored crontab file for debugging purposes. */
775433d6423SLionel Sambuc {
776433d6423SLionel Sambuc 	crontab_t *tab;
777433d6423SLionel Sambuc 	cronjob_t *job;
778433d6423SLionel Sambuc 	char *p;
779433d6423SLionel Sambuc 	struct tm tm;
780433d6423SLionel Sambuc 
781433d6423SLionel Sambuc 	for (tab= crontabs; tab != nil; tab= tab->next) {
782433d6423SLionel Sambuc 		fprintf(fp, "tab->file = \"%s\"\n", tab->file);
783433d6423SLionel Sambuc 		fprintf(fp, "tab->user = \"%s\"\n",
784433d6423SLionel Sambuc 				tab->user == nil ? "(root)" : tab->user);
785433d6423SLionel Sambuc 		fprintf(fp, "tab->mtime = %s", ctime(&tab->mtime));
786433d6423SLionel Sambuc 
787433d6423SLionel Sambuc 		for (job= tab->jobs; job != nil; job= job->next) {
788433d6423SLionel Sambuc 			if (job->atjob) {
789433d6423SLionel Sambuc 				fprintf(fp, "AT job");
790433d6423SLionel Sambuc 			} else {
791433d6423SLionel Sambuc 				pr_map(fp, job->min); fputc(' ', fp);
792433d6423SLionel Sambuc 				pr_map(fp, job->hour); fputc(' ', fp);
793433d6423SLionel Sambuc 				pr_map(fp, job->mday); fputc(' ', fp);
794433d6423SLionel Sambuc 				pr_map(fp, job->mon); fputc(' ', fp);
795433d6423SLionel Sambuc 				pr_map(fp, job->wday);
796433d6423SLionel Sambuc 			}
797433d6423SLionel Sambuc 			if (job->user != nil && job->user != tab->user) {
798433d6423SLionel Sambuc 				fprintf(fp, " -u %s", job->user);
799433d6423SLionel Sambuc 			}
800433d6423SLionel Sambuc 			fprintf(fp, "\n\t");
801433d6423SLionel Sambuc 			for (p= job->cmd; *p != 0; p++) {
802433d6423SLionel Sambuc 				fputc(*p, fp);
803433d6423SLionel Sambuc 				if (*p == '\n') fputc('\t', fp);
804433d6423SLionel Sambuc 			}
805433d6423SLionel Sambuc 			fputc('\n', fp);
806433d6423SLionel Sambuc 			tm= *localtime(&job->rtime);
807433d6423SLionel Sambuc 			fprintf(fp, "    rtime = %.24s%s\n", asctime(&tm),
808433d6423SLionel Sambuc 				tm.tm_isdst ? " (DST)" : "");
809433d6423SLionel Sambuc 			if (job->pid != IDLE_PID) {
810433d6423SLionel Sambuc 				fprintf(fp, "    pid = %ld\n", (long) job->pid);
811433d6423SLionel Sambuc 			}
812433d6423SLionel Sambuc 		}
813433d6423SLionel Sambuc 	}
814433d6423SLionel Sambuc }
815433d6423SLionel Sambuc 
816433d6423SLionel Sambuc /*
817433d6423SLionel Sambuc  * $PchId: tab.c,v 1.5 2000/07/25 22:07:51 philip Exp $
818433d6423SLionel Sambuc  */
819