1 |
+ |
/* $MidnightBSD$ */ |
2 |
|
/* Copyright 1988,1990,1993,1994 by Paul Vixie |
3 |
|
* All rights reserved |
4 |
|
* |
14 |
|
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and |
15 |
|
* I'll try to keep a version up to date. I can be reached as follows: |
16 |
|
* Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul |
16 |
– |
* $FreeBSD: src/usr.sbin/cron/cron/cron.c,v 1.15.8.1 2006/01/15 17:50:36 delphij Exp $ |
17 |
|
*/ |
18 |
|
|
19 |
|
#if !defined(lint) && !defined(LINT) |
20 |
|
static const char rcsid[] = |
21 |
< |
"$MidnightBSD: src/usr.sbin/cron/cron/cron.c,v 1.2 2007/08/18 06:59:04 laffer1 Exp $"; |
21 |
> |
"$FreeBSD: stable/10/usr.sbin/cron/cron/cron.c 321237 2017-07-19 19:41:13Z ngie $"; |
22 |
|
#endif |
23 |
|
|
24 |
|
#define MAIN_PROGRAM |
36 |
|
|
37 |
|
static void usage(void), |
38 |
|
run_reboot_jobs(cron_db *), |
39 |
< |
cron_tick(cron_db *), |
40 |
< |
cron_sync(void), |
41 |
< |
cron_sleep(cron_db *), |
39 |
> |
cron_tick(cron_db *, int), |
40 |
> |
cron_sync(int), |
41 |
> |
cron_sleep(cron_db *, int), |
42 |
|
cron_clean(cron_db *), |
43 |
|
#ifdef USE_SIGCHLD |
44 |
|
sigchld_handler(int), |
46 |
|
sighup_handler(int), |
47 |
|
parse_args(int c, char *v[]); |
48 |
|
|
49 |
+ |
static int run_at_secres(cron_db *); |
50 |
+ |
|
51 |
|
static time_t last_time = 0; |
52 |
|
static int dst_enabled = 0; |
53 |
+ |
static int dont_daemonize = 0; |
54 |
|
struct pidfh *pfh; |
55 |
|
|
56 |
|
static void |
57 |
|
usage() { |
58 |
+ |
#if DEBUGGING |
59 |
|
char **dflags; |
60 |
+ |
#endif |
61 |
|
|
62 |
|
fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] " |
63 |
< |
"[-m mailto] [-s] [-o] [-x debugflag[,...]]\n"); |
63 |
> |
"[-m mailto] [-n] [-s] [-o] [-x debugflag[,...]]\n"); |
64 |
> |
#if DEBUGGING |
65 |
|
fprintf(stderr, "\ndebugflags: "); |
66 |
|
|
67 |
|
for(dflags = DebugFlagNames; *dflags; dflags++) { |
68 |
|
fprintf(stderr, "%s ", *dflags); |
69 |
|
} |
70 |
|
fprintf(stderr, "\n"); |
71 |
+ |
#endif |
72 |
|
|
73 |
|
exit(ERROR_EXIT); |
74 |
|
} |
102 |
|
char *argv[]; |
103 |
|
{ |
104 |
|
cron_db database; |
105 |
+ |
int runnum; |
106 |
+ |
int secres1, secres2; |
107 |
+ |
struct tm *tm; |
108 |
|
|
109 |
|
ProgramName = argv[0]; |
110 |
|
|
138 |
|
if (0) { |
139 |
|
# endif |
140 |
|
(void) fprintf(stderr, "[%d] cron started\n", getpid()); |
141 |
< |
} else { |
141 |
> |
} else if (dont_daemonize == 0) { |
142 |
|
if (daemon(1, 0) == -1) { |
143 |
|
pidfile_remove(pfh); |
144 |
|
log_it("CRON",getpid(),"DEATH","can't become daemon"); |
154 |
|
database.tail = NULL; |
155 |
|
database.mtime = (time_t) 0; |
156 |
|
load_database(&database); |
157 |
+ |
secres1 = secres2 = run_at_secres(&database); |
158 |
|
run_reboot_jobs(&database); |
159 |
< |
cron_sync(); |
159 |
> |
cron_sync(secres1); |
160 |
> |
runnum = 0; |
161 |
|
while (TRUE) { |
162 |
|
# if DEBUGGING |
163 |
|
/* if (!(DebugFlags & DTEST)) */ |
164 |
|
# endif /*DEBUGGING*/ |
165 |
< |
cron_sleep(&database); |
165 |
> |
cron_sleep(&database, secres1); |
166 |
|
|
167 |
< |
load_database(&database); |
167 |
> |
if (secres1 == 0 || runnum % 60 == 0) { |
168 |
> |
load_database(&database); |
169 |
> |
secres2 = run_at_secres(&database); |
170 |
> |
if (secres2 != secres1) { |
171 |
> |
secres1 = secres2; |
172 |
> |
if (secres1 != 0) { |
173 |
> |
runnum = 0; |
174 |
> |
} else { |
175 |
> |
/* |
176 |
> |
* Going from 1 sec to 60 sec res. If we |
177 |
> |
* are already at minute's boundary, so |
178 |
> |
* let it run, otherwise schedule for the |
179 |
> |
* next minute. |
180 |
> |
*/ |
181 |
> |
tm = localtime(&TargetTime); |
182 |
> |
if (tm->tm_sec > 0) { |
183 |
> |
cron_sync(secres2); |
184 |
> |
continue; |
185 |
> |
} |
186 |
> |
} |
187 |
> |
} |
188 |
> |
} |
189 |
|
|
190 |
|
/* do this iteration |
191 |
|
*/ |
192 |
< |
cron_tick(&database); |
192 |
> |
cron_tick(&database, secres1); |
193 |
|
|
194 |
< |
/* sleep 1 minute |
194 |
> |
/* sleep 1 or 60 seconds |
195 |
|
*/ |
196 |
< |
TargetTime += 60; |
196 |
> |
TargetTime += (secres1 != 0) ? 1 : 60; |
197 |
> |
runnum += 1; |
198 |
|
} |
199 |
|
} |
200 |
|
|
218 |
|
|
219 |
|
|
220 |
|
static void |
221 |
< |
cron_tick(db) |
188 |
< |
cron_db *db; |
221 |
> |
cron_tick(cron_db *db, int secres) |
222 |
|
{ |
223 |
|
static struct tm lasttm; |
224 |
|
static time_t diff = 0, /* time difference in seconds from the last offset change */ |
225 |
|
difflimit = 0; /* end point for the time zone correction */ |
226 |
|
struct tm otztm; /* time in the old time zone */ |
227 |
< |
int otzminute, otzhour, otzdom, otzmonth, otzdow; |
227 |
> |
int otzsecond, otzminute, otzhour, otzdom, otzmonth, otzdow; |
228 |
|
register struct tm *tm = localtime(&TargetTime); |
229 |
< |
register int minute, hour, dom, month, dow; |
229 |
> |
register int second, minute, hour, dom, month, dow; |
230 |
|
register user *u; |
231 |
|
register entry *e; |
232 |
|
|
233 |
|
/* make 0-based values out of these so we can use them as indicies |
234 |
|
*/ |
235 |
+ |
second = (secres == 0) ? 0 : tm->tm_sec -FIRST_SECOND; |
236 |
|
minute = tm->tm_min -FIRST_MINUTE; |
237 |
|
hour = tm->tm_hour -FIRST_HOUR; |
238 |
|
dom = tm->tm_mday -FIRST_DOM; |
239 |
|
month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; |
240 |
|
dow = tm->tm_wday -FIRST_DOW; |
241 |
|
|
242 |
< |
Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n", |
243 |
< |
getpid(), minute, hour, dom, month, dow)) |
242 |
> |
Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d,%d)\n", |
243 |
> |
getpid(), second, minute, hour, dom, month, dow)) |
244 |
|
|
245 |
|
if (dst_enabled && last_time != 0 |
246 |
|
&& TargetTime > last_time /* exclude stepping back */ |
293 |
|
|
294 |
|
/* make 0-based values out of these so we can use them as indicies |
295 |
|
*/ |
296 |
+ |
otzsecond = (secres == 0) ? 0 : otztm.tm_sec -FIRST_SECOND; |
297 |
|
otzminute = otztm.tm_min -FIRST_MINUTE; |
298 |
|
otzhour = otztm.tm_hour -FIRST_HOUR; |
299 |
|
otzdom = otztm.tm_mday -FIRST_DOM; |
315 |
|
e->uid, e->gid, e->cmd)) |
316 |
|
|
317 |
|
if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) { |
318 |
< |
if (bit_test(e->minute, otzminute) |
318 |
> |
if (bit_test(e->second, otzsecond) |
319 |
> |
&& bit_test(e->minute, otzminute) |
320 |
|
&& bit_test(e->hour, otzhour) |
321 |
|
&& bit_test(e->month, otzmonth) |
322 |
|
&& ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) |
335 |
|
continue; |
336 |
|
} |
337 |
|
|
338 |
< |
if (bit_test(e->minute, minute) |
338 |
> |
if (bit_test(e->second, second) |
339 |
> |
&& bit_test(e->minute, minute) |
340 |
|
&& bit_test(e->hour, hour) |
341 |
|
&& bit_test(e->month, month) |
342 |
|
&& ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) |
366 |
|
* that's something sysadmin's know to expect what with crashing computers.. |
367 |
|
*/ |
368 |
|
static void |
369 |
< |
cron_sync() { |
370 |
< |
register struct tm *tm; |
369 |
> |
cron_sync(int secres) { |
370 |
> |
struct tm *tm; |
371 |
|
|
372 |
|
TargetTime = time((time_t*)0); |
373 |
< |
tm = localtime(&TargetTime); |
374 |
< |
TargetTime += (60 - tm->tm_sec); |
373 |
> |
if (secres != 0) { |
374 |
> |
TargetTime += 1; |
375 |
> |
} else { |
376 |
> |
tm = localtime(&TargetTime); |
377 |
> |
TargetTime += (60 - tm->tm_sec); |
378 |
> |
} |
379 |
|
} |
380 |
|
|
381 |
+ |
static void |
382 |
+ |
timespec_subtract(struct timespec *result, struct timespec *x, |
383 |
+ |
struct timespec *y) |
384 |
+ |
{ |
385 |
+ |
*result = *x; |
386 |
+ |
result->tv_sec -= y->tv_sec; |
387 |
+ |
result->tv_nsec -= y->tv_nsec; |
388 |
+ |
if (result->tv_nsec < 0) { |
389 |
+ |
result->tv_sec--; |
390 |
+ |
result->tv_nsec += 1000000000; |
391 |
+ |
} |
392 |
+ |
} |
393 |
|
|
394 |
|
static void |
395 |
< |
cron_sleep(db) |
343 |
< |
cron_db *db; |
395 |
> |
cron_sleep(cron_db *db, int secres) |
396 |
|
{ |
397 |
< |
int seconds_to_wait = 0; |
397 |
> |
int seconds_to_wait; |
398 |
> |
int rval; |
399 |
> |
struct timespec ctime, ttime, stime, remtime; |
400 |
|
|
401 |
|
/* |
402 |
|
* Loop until we reach the top of the next minute, sleep when possible. |
403 |
|
*/ |
404 |
|
|
405 |
|
for (;;) { |
406 |
< |
seconds_to_wait = (int) (TargetTime - time((time_t*)0)); |
406 |
> |
clock_gettime(CLOCK_REALTIME, &ctime); |
407 |
> |
ttime.tv_sec = TargetTime; |
408 |
> |
ttime.tv_nsec = 0; |
409 |
> |
timespec_subtract(&stime, &ttime, &ctime); |
410 |
|
|
411 |
|
/* |
412 |
|
* If the seconds_to_wait value is insane, jump the cron |
413 |
|
*/ |
414 |
|
|
415 |
< |
if (seconds_to_wait < -600 || seconds_to_wait > 600) { |
415 |
> |
if (stime.tv_sec < -600 || stime.tv_sec > 600) { |
416 |
|
cron_clean(db); |
417 |
< |
cron_sync(); |
417 |
> |
cron_sync(secres); |
418 |
|
continue; |
419 |
|
} |
420 |
|
|
421 |
+ |
seconds_to_wait = (stime.tv_nsec > 0) ? stime.tv_sec + 1 : |
422 |
+ |
stime.tv_sec; |
423 |
+ |
|
424 |
|
Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", |
425 |
|
getpid(), (long)TargetTime, seconds_to_wait)) |
426 |
|
|
429 |
|
* to run, break |
430 |
|
*/ |
431 |
|
|
432 |
< |
if (seconds_to_wait <= 0) |
432 |
> |
if (stime.tv_sec < 0) |
433 |
|
break; |
434 |
|
if (job_runqueue() == 0) { |
435 |
|
Debug(DSCH, ("[%d] sleeping for %d seconds\n", |
436 |
|
getpid(), seconds_to_wait)) |
437 |
|
|
438 |
< |
sleep(seconds_to_wait); |
438 |
> |
for (;;) { |
439 |
> |
rval = nanosleep(&stime, &remtime); |
440 |
> |
if (rval == 0 || errno != EINTR) |
441 |
> |
break; |
442 |
> |
stime.tv_sec = remtime.tv_sec; |
443 |
> |
stime.tv_nsec = remtime.tv_nsec; |
444 |
> |
} |
445 |
|
} |
446 |
|
} |
447 |
|
} |
514 |
|
int argch; |
515 |
|
char *endp; |
516 |
|
|
517 |
< |
while ((argch = getopt(argc, argv, "j:J:m:osx:")) != -1) { |
517 |
> |
while ((argch = getopt(argc, argv, "j:J:m:nosx:")) != -1) { |
518 |
|
switch (argch) { |
519 |
|
case 'j': |
520 |
|
Jitter = strtoul(optarg, &endp, 10); |
531 |
|
case 'm': |
532 |
|
defmailto = optarg; |
533 |
|
break; |
534 |
+ |
case 'n': |
535 |
+ |
dont_daemonize = 1; |
536 |
+ |
break; |
537 |
|
case 'o': |
538 |
|
dst_enabled = 0; |
539 |
|
break; |
550 |
|
} |
551 |
|
} |
552 |
|
|
553 |
+ |
static int |
554 |
+ |
run_at_secres(cron_db *db) |
555 |
+ |
{ |
556 |
+ |
user *u; |
557 |
+ |
entry *e; |
558 |
+ |
|
559 |
+ |
for (u = db->head; u != NULL; u = u->next) { |
560 |
+ |
for (e = u->crontab; e != NULL; e = e->next) { |
561 |
+ |
if ((e->flags & SEC_RES) != 0) |
562 |
+ |
return 1; |
563 |
+ |
} |
564 |
+ |
} |
565 |
+ |
return 0; |
566 |
+ |
} |