1 /* cron 1.4 - clock daemon Author: Kees J. Bot
24 static volatile int busy
; /* Set when something is afoot, don't sleep! */
25 static volatile int need_reload
;/* Set if table reload required. */
26 static volatile int need_quit
; /* Set if cron must exit. */
27 static volatile int debug
; /* Debug level. */
29 static void run_job(cronjob_t
*job
)
30 /* Execute a cron job. Register its pid in the job structure. If a job's
31 * crontab has an owner then its output is mailed to that owner, otherwise
32 * no special provisions are made, so the output will go where cron's output
33 * goes. This keeps root's mailbox from filling up.
38 int mailfd
[2], errfd
[2];
40 crontab_t
*tab
= job
->tab
;
42 need_mailer
= (tab
->user
!= nil
);
48 if (rename(tab
->file
, tab
->data
) < 0) {
49 if (errno
== ENOENT
) {
50 /* Normal error, job deleted. */
53 /* Bad error, halt processing AT jobs. */
54 cronlog(LOG_CRIT
, "Can't rename %s: %s\n",
55 tab
->file
, strerror(errno
));
60 /* Will need to determine the next AT job. */
63 if (stat(tab
->data
, &st
) < 0) {
64 cronlog(LOG_ERR
, "Can't stat %s: %s\n",
65 tab
->data
, strerror(errno
));
69 if ((pw
= getpwuid(st
.st_uid
)) == nil
) {
71 "Unknown owner for uid %lu of AT job %s\n",
72 (unsigned long) st
.st_uid
, job
->cmd
);
78 if (job
->user
!= nil
&& (pw
= getpwnam(job
->user
)) == nil
) {
79 cronlog(LOG_ERR
, "%s: Unknown user\n", job
->user
);
87 if (pipe(errfd
) < 0 || pipe(mailfd
) < 0) {
88 cronlog(LOG_ERR
, "pipe() call failed: %s\n",
97 (void) fcntl(errfd
[1], F_SETFD
,
98 fcntl(errfd
[1], F_GETFD
) | FD_CLOEXEC
);
100 if ((pid
= fork()) == -1) {
101 cronlog(LOG_ERR
, "fork() call failed: %s\n",
112 /* Child that is to be the mailer. */
113 char subject
[70+20], *ps
;
117 if (mailfd
[0] != 0) {
122 memset(subject
, 0, sizeof(subject
));
124 "Output from your %s job: %.50s",
125 job
->atjob
? "AT" : "cron",
127 if (subject
[70] != 0) {
128 strcpy(subject
+70-3, "...");
130 for (ps
= subject
; *ps
!= 0; ps
++) {
131 if (*ps
== '\n') *ps
= '%';
134 execl("/usr/bin/mail", "mail", "-s", subject
,
135 pw
->pw_name
, (char *) nil
);
136 write(errfd
[1], &errno
, sizeof(errno
));
142 if (read(errfd
[0], &errno
, sizeof(errno
)) > 0) {
143 cronlog(LOG_ERR
, "can't execute /usr/bin/mail: %s\n",
153 if (pipe(errfd
) < 0) {
154 cronlog(LOG_ERR
, "pipe() call failed: %s\n", strerror(errno
));
155 if (need_mailer
) close(mailfd
[1]);
159 (void) fcntl(errfd
[1], F_SETFD
, fcntl(errfd
[1], F_GETFD
) | FD_CLOEXEC
);
161 if ((pid
= fork()) == -1) {
162 cronlog(LOG_ERR
, "fork() call failed: %s\n", strerror(errno
));
165 if (need_mailer
) close(mailfd
[1]);
171 /* Child that is to be the cron job. */
174 if (mailfd
[1] != 1) {
182 /* Change id to the owner of the job. */
183 initgroups(pw
->pw_name
, pw
->pw_gid
);
187 if (setenv("USER", pw
->pw_name
, 1) < 0) goto bad
;
188 if (setenv("LOGNAME", pw
->pw_name
, 1) < 0) goto bad
;
189 if (setenv("HOME", pw
->pw_dir
, 1) < 0) goto bad
;
190 if (setenv("SHELL", pw
->pw_shell
[0] == 0 ? "/bin/sh"
191 : pw
->pw_shell
, 1) < 0) goto bad
;
195 execl("/bin/sh", "sh", tab
->data
, (char *) nil
);
197 execl("/bin/sh", "sh", "-c", job
->cmd
, (char *) nil
);
200 write(errfd
[1], &errno
, sizeof(errno
));
204 if (need_mailer
) close(mailfd
[1]);
206 if (read(errfd
[0], &errno
, sizeof(errno
)) > 0) {
207 cronlog(LOG_ERR
, "can't execute /bin/sh: %s\n",
215 if (debug
>= 1) fprintf(stderr
, "executing >%s<, pid = %ld\n",
216 job
->cmd
, (long) job
->pid
);
219 static void load_crontabs(void)
220 /* Load all the crontabs we like to run. We didn't bother to make a list in
221 * an array or something, this is too system specific to make nice.
229 tab_parse("/usr/lib/crontab", nil
);
230 tab_parse("/usr/local/lib/crontab", nil
);
231 tab_parse("/var/lib/crontab", nil
);
234 if ((pkgs
= fopen("/usr/lib/packages", "r")) != nil
) {
235 char name
[NAME_MAX
+1];
238 char tab
[sizeof("/var/opt//lib/crontab") + NAME_MAX
];
240 while ((c
= fgetc(pkgs
)) != EOF
) {
242 while (c
!= EOF
&& c
!= '/' && c
!= '\n') {
243 if (np
< name
+NAME_MAX
) *np
++ = c
;
247 while (c
!= EOF
&& c
!= '\n') c
= fgetc(pkgs
);
249 if (name
[0] == 0) continue; /* ? */
251 strcpy(tab
, "/var/opt/");
253 strcat(tab
, "/lib/crontab");
257 cronlog(LOG_CRIT
, "/usr/lib/packages: %s\n",
262 if (errno
!= ENOENT
) {
263 cronlog(LOG_ERR
, "/usr/lib/packages: %s\n",
267 #endif /* Minix-vmd */
269 if ((spool
= opendir("/usr/spool/crontabs")) != nil
) {
270 struct dirent
*entry
;
271 char tab
[sizeof("/usr/spool/crontabs/") + NAME_MAX
];
273 while ((entry
= readdir(spool
)) != nil
) {
274 if (entry
->d_name
[0] == '.') continue;
276 strcpy(tab
, "/usr/spool/crontabs/");
277 strcat(tab
, entry
->d_name
);
278 tab_parse(tab
, entry
->d_name
);
283 /* Find the first to be executed AT job. */
284 tab_find_atjob("/usr/spool/at");
289 fprintf(stderr
, "%lu memory chunks in use\n",
290 (unsigned long) alloc_count
);
294 static void handler(int sig
)
311 alarm(1); /* A signal may come just before a blocking call. */
315 static void usage(void)
317 fprintf(stderr
, "Usage: %s [-d[#]]\n", prog_name
);
321 int main(int argc
, char **argv
)
324 struct sigaction sa
, osa
;
328 prog_name
= strrchr(argv
[0], '/');
329 if (prog_name
== nil
) prog_name
= argv
[0]; else prog_name
++;
332 while (i
< argc
&& argv
[i
][0] == '-') {
333 char *opt
= argv
[i
++] + 1;
335 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
337 while (*opt
!= 0) switch (*opt
++) {
342 debug
= strtoul(opt
, &opt
, 10);
343 if (*opt
!= 0) usage();
350 if (i
!= argc
) usage();
353 openlog(prog_name
, LOG_PID
, LOG_DAEMON
);
354 setlogmask(LOG_UPTO(LOG_INFO
));
356 /* Save process id. */
357 if ((pf
= fopen(PIDFILE
, "w")) == NULL
) {
358 fprintf(stderr
, "%s: %s\n", PIDFILE
, strerror(errno
));
361 fprintf(pf
, "%d\n", getpid());
362 if (ferror(pf
) || fclose(pf
) == EOF
) {
363 fprintf(stderr
, "%s: %s\n", PIDFILE
, strerror(errno
));
367 sigemptyset(&sa
.sa_mask
);
369 sa
.sa_handler
= handler
;
371 /* Hangup: Reload crontab files. */
372 sigaction(SIGHUP
, &sa
, nil
);
374 /* User signal 1 & 2: Raise or reset debug level. */
375 sigaction(SIGUSR1
, &sa
, nil
);
376 sigaction(SIGUSR2
, &sa
, nil
);
378 /* Interrupt and Terminate: Cleanup and exit. */
379 if (sigaction(SIGINT
, nil
, &osa
) == 0 && osa
.sa_handler
!= SIG_IGN
) {
380 sigaction(SIGINT
, &sa
, nil
);
382 if (sigaction(SIGTERM
, nil
, &osa
) == 0 && osa
.sa_handler
!= SIG_IGN
) {
383 sigaction(SIGTERM
, &sa
, nil
);
386 /* Alarm: Wake up and run a job. */
387 sigaction(SIGALRM
, &sa
, nil
);
389 /* Initialize current time and time next to do something. */
393 /* Table load required first time. */
403 /* Run jobs whose time has come. */
407 if ((job
= tab_nextjob()) != nil
) run_job(job
);
412 /* Did a job finish? */
413 r
= waitpid(-1, nil
, WNOHANG
);
416 /* Sleep until the next job must be started. */
421 struct timeval tvnext
;
425 sysutime(UTIME_SETALARM
, &tvnext
);
427 alarm((next
- now
) > INT_MAX
428 ? INT_MAX
: (next
- now
));
431 if (debug
>= 1) fprintf(stderr
, "%s: sleep until %s",
432 prog_name
, ctime(&next
));
434 closelog(); /* Don't keep resources open. */
436 /* Wait for a job to exit or a timeout. */
437 r
= waitpid(-1, nil
, 0);
438 if (r
== -1 && errno
== ECHILD
) pause();
444 /* A job has finished, reschedule it. */
445 if (debug
>= 1) fprintf(stderr
, "pid %d has exited\n",
447 tab_reap_job((pid_t
) r
);
450 } while (!need_quit
);
452 /* Remove the pid file to signal that cron is gone. */
459 * $PchId: cron.c,v 1.4 2000/07/17 19:00:35 philip Exp $