1 /* cron 1.4 - clock daemon Author: Kees J. Bot
27 #if __minix && !__minix_vmd
28 #define initgroups(name, gid) (0)
31 static volatile int busy
; /* Set when something is afoot, don't sleep! */
32 static volatile int need_reload
;/* Set if table reload required. */
33 static volatile int need_quit
; /* Set if cron must exit. */
34 static volatile int debug
; /* Debug level. */
36 static void run_job(cronjob_t
*job
)
37 /* Execute a cron job. Register its pid in the job structure. If a job's
38 * crontab has an owner then its output is mailed to that owner, otherwise
39 * no special provisions are made, so the output will go where cron's output
40 * goes. This keeps root's mailbox from filling up.
45 int mailfd
[2], errfd
[2];
47 crontab_t
*tab
= job
->tab
;
49 need_mailer
= (tab
->user
!= nil
);
55 if (rename(tab
->file
, tab
->data
) < 0) {
56 if (errno
== ENOENT
) {
57 /* Normal error, job deleted. */
60 /* Bad error, halt processing AT jobs. */
61 log(LOG_CRIT
, "Can't rename %s: %s\n",
62 tab
->file
, strerror(errno
));
67 /* Will need to determine the next AT job. */
70 if (stat(tab
->data
, &st
) < 0) {
71 log(LOG_ERR
, "Can't stat %s: %s\n",
72 tab
->data
, strerror(errno
));
76 if ((pw
= getpwuid(st
.st_uid
)) == nil
) {
77 log(LOG_ERR
, "Unknown owner for uid %lu of AT job %s\n",
78 (unsigned long) st
.st_uid
, job
->cmd
);
84 if (job
->user
!= nil
&& (pw
= getpwnam(job
->user
)) == nil
) {
85 log(LOG_ERR
, "%s: Unknown user\n", job
->user
);
93 if (pipe(errfd
) < 0 || pipe(mailfd
) < 0) {
94 log(LOG_ERR
, "pipe() call failed: %s\n",
103 (void) fcntl(errfd
[1], F_SETFD
,
104 fcntl(errfd
[1], F_GETFD
) | FD_CLOEXEC
);
106 if ((pid
= fork()) == -1) {
107 log(LOG_ERR
, "fork() call failed: %s\n",
118 /* Child that is to be the mailer. */
119 char subject
[70+20], *ps
;
123 if (mailfd
[0] != 0) {
128 memset(subject
, 0, sizeof(subject
));
130 "Output from your %s job: %.50s",
131 job
->atjob
? "AT" : "cron",
133 if (subject
[70] != 0) {
134 strcpy(subject
+70-3, "...");
136 for (ps
= subject
; *ps
!= 0; ps
++) {
137 if (*ps
== '\n') *ps
= '%';
140 execl("/usr/bin/mail", "mail", "-s", subject
,
141 pw
->pw_name
, (char *) nil
);
142 write(errfd
[1], &errno
, sizeof(errno
));
148 if (read(errfd
[0], &errno
, sizeof(errno
)) > 0) {
149 log(LOG_ERR
, "can't execute /usr/bin/mail: %s\n",
159 if (pipe(errfd
) < 0) {
160 log(LOG_ERR
, "pipe() call failed: %s\n", strerror(errno
));
161 if (need_mailer
) close(mailfd
[1]);
165 (void) fcntl(errfd
[1], F_SETFD
, fcntl(errfd
[1], F_GETFD
) | FD_CLOEXEC
);
167 if ((pid
= fork()) == -1) {
168 log(LOG_ERR
, "fork() call failed: %s\n", strerror(errno
));
171 if (need_mailer
) close(mailfd
[1]);
177 /* Child that is to be the cron job. */
180 if (mailfd
[1] != 1) {
188 /* Change id to the owner of the job. */
189 initgroups(pw
->pw_name
, pw
->pw_gid
);
193 if (setenv("USER", pw
->pw_name
, 1) < 0) goto bad
;
194 if (setenv("LOGNAME", pw
->pw_name
, 1) < 0) goto bad
;
195 if (setenv("HOME", pw
->pw_dir
, 1) < 0) goto bad
;
196 if (setenv("SHELL", pw
->pw_shell
[0] == 0 ? "/bin/sh"
197 : pw
->pw_shell
, 1) < 0) goto bad
;
201 execl("/bin/sh", "sh", tab
->data
, (char *) nil
);
203 execl("/bin/sh", "sh", "-c", job
->cmd
, (char *) nil
);
206 write(errfd
[1], &errno
, sizeof(errno
));
210 if (need_mailer
) close(mailfd
[1]);
212 if (read(errfd
[0], &errno
, sizeof(errno
)) > 0) {
213 log(LOG_ERR
, "can't execute /bin/sh: %s\n", strerror(errno
));
220 if (debug
>= 1) fprintf(stderr
, "executing >%s<, pid = %ld\n",
221 job
->cmd
, (long) job
->pid
);
224 static void load_crontabs(void)
225 /* Load all the crontabs we like to run. We didn't bother to make a list in
226 * an array or something, this is too system specific to make nice.
234 tab_parse("/usr/lib/crontab", nil
);
235 tab_parse("/usr/local/lib/crontab", nil
);
236 tab_parse("/var/lib/crontab", nil
);
239 if ((pkgs
= fopen("/usr/lib/packages", "r")) != nil
) {
240 char name
[NAME_MAX
+1];
243 char tab
[sizeof("/var/opt//lib/crontab") + NAME_MAX
];
245 while ((c
= fgetc(pkgs
)) != EOF
) {
247 while (c
!= EOF
&& c
!= '/' && c
!= '\n') {
248 if (np
< name
+NAME_MAX
) *np
++ = c
;
252 while (c
!= EOF
&& c
!= '\n') c
= fgetc(pkgs
);
254 if (name
[0] == 0) continue; /* ? */
256 strcpy(tab
, "/var/opt/");
258 strcat(tab
, "/lib/crontab");
262 log(LOG_CRIT
, "/usr/lib/packages: %s\n",
267 if (errno
!= ENOENT
) {
268 log(LOG_ERR
, "/usr/lib/packages: %s\n",
272 #endif /* Minix-vmd */
274 if ((spool
= opendir("/usr/spool/crontabs")) != nil
) {
275 struct dirent
*entry
;
276 char tab
[sizeof("/usr/spool/crontabs/") + NAME_MAX
];
278 while ((entry
= readdir(spool
)) != nil
) {
279 if (entry
->d_name
[0] == '.') continue;
281 strcpy(tab
, "/usr/spool/crontabs/");
282 strcat(tab
, entry
->d_name
);
283 tab_parse(tab
, entry
->d_name
);
288 /* Find the first to be executed AT job. */
289 tab_find_atjob("/usr/spool/at");
294 fprintf(stderr
, "%lu memory chunks in use\n",
295 (unsigned long) alloc_count
);
299 static void handler(int sig
)
316 alarm(1); /* A signal may come just before a blocking call. */
320 static void usage(void)
322 fprintf(stderr
, "Usage: %s [-d[#]]\n", prog_name
);
326 int main(int argc
, char **argv
)
329 struct sigaction sa
, osa
;
333 prog_name
= strrchr(argv
[0], '/');
334 if (prog_name
== nil
) prog_name
= argv
[0]; else prog_name
++;
337 while (i
< argc
&& argv
[i
][0] == '-') {
338 char *opt
= argv
[i
++] + 1;
340 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
342 while (*opt
!= 0) switch (*opt
++) {
347 debug
= strtoul(opt
, &opt
, 10);
348 if (*opt
!= 0) usage();
355 if (i
!= argc
) usage();
358 openlog(prog_name
, LOG_PID
, LOG_DAEMON
);
359 setlogmask(LOG_UPTO(LOG_INFO
));
361 /* Save process id. */
362 if ((pf
= fopen(PIDFILE
, "w")) == NULL
) {
363 fprintf(stderr
, "%s: %s\n", PIDFILE
, strerror(errno
));
366 fprintf(pf
, "%d\n", getpid());
367 if (ferror(pf
) || fclose(pf
) == EOF
) {
368 fprintf(stderr
, "%s: %s\n", PIDFILE
, strerror(errno
));
372 sigemptyset(&sa
.sa_mask
);
374 sa
.sa_handler
= handler
;
376 /* Hangup: Reload crontab files. */
377 sigaction(SIGHUP
, &sa
, nil
);
379 /* User signal 1 & 2: Raise or reset debug level. */
380 sigaction(SIGUSR1
, &sa
, nil
);
381 sigaction(SIGUSR2
, &sa
, nil
);
383 /* Interrupt and Terminate: Cleanup and exit. */
384 if (sigaction(SIGINT
, nil
, &osa
) == 0 && osa
.sa_handler
!= SIG_IGN
) {
385 sigaction(SIGINT
, &sa
, nil
);
387 if (sigaction(SIGTERM
, nil
, &osa
) == 0 && osa
.sa_handler
!= SIG_IGN
) {
388 sigaction(SIGTERM
, &sa
, nil
);
391 /* Alarm: Wake up and run a job. */
392 sigaction(SIGALRM
, &sa
, nil
);
394 /* Initialize current time and time next to do something. */
398 /* Table load required first time. */
408 /* Run jobs whose time has come. */
412 if ((job
= tab_nextjob()) != nil
) run_job(job
);
417 /* Did a job finish? */
418 r
= waitpid(-1, nil
, WNOHANG
);
421 /* Sleep until the next job must be started. */
426 struct timeval tvnext
;
430 sysutime(UTIME_SETALARM
, &tvnext
);
432 alarm((next
- now
) > INT_MAX
433 ? INT_MAX
: (next
- now
));
436 if (debug
>= 1) fprintf(stderr
, "%s: sleep until %s",
437 prog_name
, ctime(&next
));
439 closelog(); /* Don't keep resources open. */
441 /* Wait for a job to exit or a timeout. */
442 r
= waitpid(-1, nil
, 0);
443 if (r
== -1 && errno
== ECHILD
) pause();
449 /* A job has finished, reschedule it. */
450 if (debug
>= 1) fprintf(stderr
, "pid %d has exited\n",
452 tab_reap_job((pid_t
) r
);
455 } while (!need_quit
);
457 /* Remove the pid file to signal that cron is gone. */
464 * $PchId: cron.c,v 1.4 2000/07/17 19:00:35 philip Exp $