Do not use .Xo/.Xc to work around ancient roff limits.
[netbsd-mini2440.git] / libexec / cron / cron.c
blob3f6f67d9dc016916806a6bcc8d7ee6a520053593
1 /* Copyright 1988,1990,1993 by Paul Vixie
2 * All rights reserved
4 * Distribute freely, except: don't remove my name from the source or
5 * documentation (don't take credit for my work), mark your changes (don't
6 * get me blamed for your possible bugs), don't alter or remove this
7 * notice. May be sold if buildable source is provided to buyer. No
8 * warrantee of any kind, express or implied, is included with this
9 * software; use at your own risk, responsibility for damages (if any) to
10 * anyone resulting from the use of this software rests entirely with the
11 * user.
13 * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
14 * I'll try to keep a version up to date. I can be reached as follows:
15 * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
18 #if !defined(lint) && !defined(LINT)
19 static char rcsid[] = "$Id: cron.c,v 1.1 1994/01/05 20:40:12 jtc Exp $";
20 #endif
23 #define MAIN_PROGRAM
26 #include "cron.h"
27 #include "externs.h"
28 #include <sys/signal.h>
29 #if SYS_TIME_H
30 # include <sys/time.h>
31 #else
32 # include <time.h>
33 #endif
36 static void usage __P((void)),
37 run_reboot_jobs __P((cron_db *)),
38 cron_tick __P((cron_db *)),
39 cron_sync __P((void)),
40 cron_sleep __P((void)),
41 #ifdef USE_SIGCHLD
42 sigchld_handler __P((int)),
43 #endif
44 parse_args __P((int c, char *v[]));
47 static void
48 usage() {
49 fprintf(stderr, "usage: %s [-x debugflag[,...]]\n", ProgramName);
50 exit(ERROR_EXIT);
54 int
55 main(argc, argv)
56 int argc;
57 char *argv[];
59 cron_db database;
61 ProgramName = argv[0];
63 #if defined(BSD)
64 setlinebuf(stdout);
65 setlinebuf(stderr);
66 #endif
68 parse_args(argc, argv);
70 #ifdef USE_SIGCHLD
71 (void) signal(SIGCHLD, sigchld_handler);
72 #else
73 (void) signal(SIGCLD, SIG_IGN);
74 #endif
76 acquire_daemonlock(0);
77 set_cron_uid();
78 set_cron_cwd();
80 #if defined(POSIX)
81 setenv("PATH", _PATH_DEFPATH, 1);
82 #endif
84 /* if there are no debug flags turned on, fork as a daemon should.
86 # if DEBUGGING
87 if (DebugFlags) {
88 # else
89 if (0) {
90 # endif
91 (void) fprintf(stderr, "[%d] cron started\n", getpid());
92 } else {
93 switch (fork()) {
94 case -1:
95 log_it("CRON",getpid(),"DEATH","can't fork");
96 exit(0);
97 break;
98 case 0:
99 /* child process */
100 log_it("CRON",getpid(),"STARTUP","fork ok");
101 (void) setsid();
102 break;
103 default:
104 /* parent process should just die */
105 _exit(0);
109 acquire_daemonlock(0);
110 database.head = NULL;
111 database.tail = NULL;
112 database.mtime = (time_t) 0;
113 load_database(&database);
114 run_reboot_jobs(&database);
115 cron_sync();
116 while (TRUE) {
117 # if DEBUGGING
118 if (!(DebugFlags & DTEST))
119 # endif /*DEBUGGING*/
120 cron_sleep();
122 load_database(&database);
124 /* do this iteration
126 cron_tick(&database);
128 /* sleep 1 minute
130 TargetTime += 60;
135 static void
136 run_reboot_jobs(db)
137 cron_db *db;
139 register user *u;
140 register entry *e;
142 for (u = db->head; u != NULL; u = u->next) {
143 for (e = u->crontab; e != NULL; e = e->next) {
144 if (e->flags & WHEN_REBOOT) {
145 job_add(e, u);
149 (void) job_runqueue();
153 static void
154 cron_tick(db)
155 cron_db *db;
157 register struct tm *tm = localtime(&TargetTime);
158 register int minute, hour, dom, month, dow;
159 register user *u;
160 register entry *e;
162 /* make 0-based values out of these so we can use them as indicies
164 minute = tm->tm_min -FIRST_MINUTE;
165 hour = tm->tm_hour -FIRST_HOUR;
166 dom = tm->tm_mday -FIRST_DOM;
167 month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
168 dow = tm->tm_wday -FIRST_DOW;
170 Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n",
171 getpid(), minute, hour, dom, month, dow))
173 /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
174 * first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
175 * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
176 * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
177 * like many bizarre things, it's the standard.
179 for (u = db->head; u != NULL; u = u->next) {
180 Debug(DSCH|DEXT, ("user [%s:%d:%d:...]\n",
181 env_get("LOGNAME", u->envp), u->uid, u->gid))
182 for (e = u->crontab; e != NULL; e = e->next) {
183 Debug(DSCH|DEXT, ("entry [%s]\n", e->cmd))
184 if (bit_test(e->minute, minute)
185 && bit_test(e->hour, hour)
186 && bit_test(e->month, month)
187 && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
188 ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
189 : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
192 job_add(e, u);
199 /* the task here is to figure out how long it's going to be until :00 of the
200 * following minute and initialize TargetTime to this value. TargetTime
201 * will subsequently slide 60 seconds at a time, with correction applied
202 * implicitly in cron_sleep(). it would be nice to let cron execute in
203 * the "current minute" before going to sleep, but by restarting cron you
204 * could then get it to execute a given minute's jobs more than once.
205 * instead we have the chance of missing a minute's jobs completely, but
206 * that's something sysadmin's know to expect what with crashing computers..
208 static void
209 cron_sync() {
210 register struct tm *tm;
212 TargetTime = time((time_t*)0);
213 tm = localtime(&TargetTime);
214 TargetTime += (60 - tm->tm_sec);
218 static void
219 cron_sleep() {
220 register int seconds_to_wait;
222 do {
223 seconds_to_wait = (int) (TargetTime - time((time_t*)0));
224 Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
225 getpid(), TargetTime, seconds_to_wait))
227 /* if we intend to sleep, this means that it's finally
228 * time to empty the job queue (execute it).
230 * if we run any jobs, we'll probably screw up our timing,
231 * so go recompute.
233 * note that we depend here on the left-to-right nature
234 * of &&, and the short-circuiting.
236 } while (seconds_to_wait > 0 && job_runqueue());
238 while (seconds_to_wait > 0) {
239 Debug(DSCH, ("[%d] sleeping for %d seconds\n",
240 getpid(), seconds_to_wait))
241 seconds_to_wait = (int) sleep((unsigned int) seconds_to_wait);
246 #ifdef USE_SIGCHLD
247 static void
248 sigchld_handler(x) {
249 WAIT_T waiter;
250 int pid;
252 for (;;) {
253 pid = wait3(&waiter, WNOHANG, (struct rusage *)0);
254 switch (pid) {
255 case -1:
256 Debug(DPROC,
257 ("[%d] sigchld...no children\n", getpid()))
258 return;
259 case 0:
260 Debug(DPROC,
261 ("[%d] sigchld...no dead kids\n", getpid()))
262 return;
263 default:
264 Debug(DPROC,
265 ("[%d] sigchld...pid #%d died, stat=%d\n",
266 getpid(), pid, WEXITSTATUS(waiter)))
270 #endif /*USE_SIGCHLD*/
273 static void
274 parse_args(argc, argv)
275 int argc;
276 char *argv[];
278 int argch;
280 while (EOF != (argch = getopt(argc, argv, "x:"))) {
281 switch (argch) {
282 default:
283 usage();
284 case 'x':
285 if (!set_debug_flags(optarg))
286 usage();
287 break;