1 /* cron 1.4 - clock daemon Author: Kees J. Bot
24 #if __minix && !__minix_vmd
25 #define initgroups(name, gid) (0)
28 static volatile int busy
; /* Set when something is afoot, don't sleep! */
29 static volatile int need_reload
;/* Set if table reload required. */
30 static volatile int need_quit
; /* Set if cron must exit. */
31 static volatile int debug
; /* Debug level. */
33 static void run_job(cronjob_t
*job
)
34 /* Execute a cron job. Register its pid in the job structure. If a job's
35 * crontab has an owner then its output is mailed to that owner, otherwise
36 * no special provisions are made, so the output will go where cron's output
37 * goes. This keeps root's mailbox from filling up.
42 int mailfd
[2], errfd
[2];
44 crontab_t
*tab
= job
->tab
;
46 need_mailer
= (tab
->user
!= nil
);
52 if (rename(tab
->file
, tab
->data
) < 0) {
53 if (errno
== ENOENT
) {
54 /* Normal error, job deleted. */
57 /* Bad error, halt processing AT jobs. */
58 log(LOG_CRIT
, "Can't rename %s: %s\n",
59 tab
->file
, strerror(errno
));
64 /* Will need to determine the next AT job. */
67 if (stat(tab
->data
, &st
) < 0) {
68 log(LOG_ERR
, "Can't stat %s: %s\n",
69 tab
->data
, strerror(errno
));
73 if ((pw
= getpwuid(st
.st_uid
)) == nil
) {
74 log(LOG_ERR
, "Unknown owner for uid %lu of AT job %s\n",
75 (unsigned long) st
.st_uid
, job
->cmd
);
81 if (job
->user
!= nil
&& (pw
= getpwnam(job
->user
)) == nil
) {
82 log(LOG_ERR
, "%s: Unknown user\n", job
->user
);
90 if (pipe(errfd
) < 0 || pipe(mailfd
) < 0) {
91 log(LOG_ERR
, "pipe() call failed: %s\n",
100 (void) fcntl(errfd
[1], F_SETFD
,
101 fcntl(errfd
[1], F_GETFD
) | FD_CLOEXEC
);
103 if ((pid
= fork()) == -1) {
104 log(LOG_ERR
, "fork() call failed: %s\n",
115 /* Child that is to be the mailer. */
116 char subject
[70+20], *ps
;
120 if (mailfd
[0] != 0) {
125 memset(subject
, 0, sizeof(subject
));
127 "Output from your %s job: %.50s",
128 job
->atjob
? "AT" : "cron",
130 if (subject
[70] != 0) {
131 strcpy(subject
+70-3, "...");
133 for (ps
= subject
; *ps
!= 0; ps
++) {
134 if (*ps
== '\n') *ps
= '%';
137 execl("/usr/bin/mail", "mail", "-s", subject
,
138 pw
->pw_name
, (char *) nil
);
139 write(errfd
[1], &errno
, sizeof(errno
));
145 if (read(errfd
[0], &errno
, sizeof(errno
)) > 0) {
146 log(LOG_ERR
, "can't execute /usr/bin/mail: %s\n",
156 if (pipe(errfd
) < 0) {
157 log(LOG_ERR
, "pipe() call failed: %s\n", strerror(errno
));
158 if (need_mailer
) close(mailfd
[1]);
162 (void) fcntl(errfd
[1], F_SETFD
, fcntl(errfd
[1], F_GETFD
) | FD_CLOEXEC
);
164 if ((pid
= fork()) == -1) {
165 log(LOG_ERR
, "fork() call failed: %s\n", strerror(errno
));
168 if (need_mailer
) close(mailfd
[1]);
174 /* Child that is to be the cron job. */
177 if (mailfd
[1] != 1) {
185 /* Change id to the owner of the job. */
186 initgroups(pw
->pw_name
, pw
->pw_gid
);
190 if (setenv("USER", pw
->pw_name
, 1) < 0) goto bad
;
191 if (setenv("LOGNAME", pw
->pw_name
, 1) < 0) goto bad
;
192 if (setenv("HOME", pw
->pw_dir
, 1) < 0) goto bad
;
193 if (setenv("SHELL", pw
->pw_shell
[0] == 0 ? "/bin/sh"
194 : pw
->pw_shell
, 1) < 0) goto bad
;
198 execl("/bin/sh", "sh", tab
->data
, (char *) nil
);
200 execl("/bin/sh", "sh", "-c", job
->cmd
, (char *) nil
);
203 write(errfd
[1], &errno
, sizeof(errno
));
207 if (need_mailer
) close(mailfd
[1]);
209 if (read(errfd
[0], &errno
, sizeof(errno
)) > 0) {
210 log(LOG_ERR
, "can't execute /bin/sh: %s\n", strerror(errno
));
217 if (debug
>= 1) fprintf(stderr
, "executing >%s<, pid = %ld\n",
218 job
->cmd
, (long) job
->pid
);
221 static void load_crontabs(void)
222 /* Load all the crontabs we like to run. We didn't bother to make a list in
223 * an array or something, this is too system specific to make nice.
231 tab_parse("/usr/lib/crontab", nil
);
232 tab_parse("/usr/local/lib/crontab", nil
);
233 tab_parse("/var/lib/crontab", nil
);
236 if ((pkgs
= fopen("/usr/lib/packages", "r")) != nil
) {
237 char name
[NAME_MAX
+1];
240 char tab
[sizeof("/var/opt//lib/crontab") + NAME_MAX
];
242 while ((c
= fgetc(pkgs
)) != EOF
) {
244 while (c
!= EOF
&& c
!= '/' && c
!= '\n') {
245 if (np
< name
+NAME_MAX
) *np
++ = c
;
249 while (c
!= EOF
&& c
!= '\n') c
= fgetc(pkgs
);
251 if (name
[0] == 0) continue; /* ? */
253 strcpy(tab
, "/var/opt/");
255 strcat(tab
, "/lib/crontab");
259 log(LOG_CRIT
, "/usr/lib/packages: %s\n",
264 if (errno
!= ENOENT
) {
265 log(LOG_ERR
, "/usr/lib/packages: %s\n",
269 #endif /* Minix-vmd */
271 if ((spool
= opendir("/usr/spool/crontabs")) != nil
) {
272 struct dirent
*entry
;
273 char tab
[sizeof("/usr/spool/crontabs/") + NAME_MAX
];
275 while ((entry
= readdir(spool
)) != nil
) {
276 if (entry
->d_name
[0] == '.') continue;
278 strcpy(tab
, "/usr/spool/crontabs/");
279 strcat(tab
, entry
->d_name
);
280 tab_parse(tab
, entry
->d_name
);
285 /* Find the first to be executed AT job. */
286 tab_find_atjob("/usr/spool/at");
291 fprintf(stderr
, "%lu memory chunks in use\n",
292 (unsigned long) alloc_count
);
296 static void handler(int sig
)
313 alarm(1); /* A signal may come just before a blocking call. */
317 static void usage(void)
319 fprintf(stderr
, "Usage: %s [-d[#]]\n", prog_name
);
323 int main(int argc
, char **argv
)
326 struct sigaction sa
, osa
;
330 prog_name
= strrchr(argv
[0], '/');
331 if (prog_name
== nil
) prog_name
= argv
[0]; else prog_name
++;
334 while (i
< argc
&& argv
[i
][0] == '-') {
335 char *opt
= argv
[i
++] + 1;
337 if (opt
[0] == '-' && opt
[1] == 0) break; /* -- */
339 while (*opt
!= 0) switch (*opt
++) {
344 debug
= strtoul(opt
, &opt
, 10);
345 if (*opt
!= 0) usage();
352 if (i
!= argc
) usage();
355 openlog(prog_name
, LOG_PID
, LOG_DAEMON
);
356 setlogmask(LOG_UPTO(LOG_INFO
));
358 /* Save process id. */
359 if ((pf
= fopen(PIDFILE
, "w")) == NULL
) {
360 fprintf(stderr
, "%s: %s\n", PIDFILE
, strerror(errno
));
363 fprintf(pf
, "%d\n", getpid());
364 if (ferror(pf
) || fclose(pf
) == EOF
) {
365 fprintf(stderr
, "%s: %s\n", PIDFILE
, strerror(errno
));
369 sigemptyset(&sa
.sa_mask
);
371 sa
.sa_handler
= handler
;
373 /* Hangup: Reload crontab files. */
374 sigaction(SIGHUP
, &sa
, nil
);
376 /* User signal 1 & 2: Raise or reset debug level. */
377 sigaction(SIGUSR1
, &sa
, nil
);
378 sigaction(SIGUSR2
, &sa
, nil
);
380 /* Interrupt and Terminate: Cleanup and exit. */
381 if (sigaction(SIGINT
, nil
, &osa
) == 0 && osa
.sa_handler
!= SIG_IGN
) {
382 sigaction(SIGINT
, &sa
, nil
);
384 if (sigaction(SIGTERM
, nil
, &osa
) == 0 && osa
.sa_handler
!= SIG_IGN
) {
385 sigaction(SIGTERM
, &sa
, nil
);
388 /* Alarm: Wake up and run a job. */
389 sigaction(SIGALRM
, &sa
, nil
);
391 /* Initialize current time and time next to do something. */
395 /* Table load required first time. */
405 /* Run jobs whose time has come. */
409 if ((job
= tab_nextjob()) != nil
) run_job(job
);
414 /* Did a job finish? */
415 r
= waitpid(-1, nil
, WNOHANG
);
418 /* Sleep until the next job must be started. */
423 struct timeval tvnext
;
427 sysutime(UTIME_SETALARM
, &tvnext
);
429 alarm((next
- now
) > INT_MAX
430 ? INT_MAX
: (next
- now
));
433 if (debug
>= 1) fprintf(stderr
, "%s: sleep until %s",
434 prog_name
, ctime(&next
));
436 closelog(); /* Don't keep resources open. */
438 /* Wait for a job to exit or a timeout. */
439 r
= waitpid(-1, nil
, 0);
440 if (r
== -1 && errno
== ECHILD
) pause();
446 /* A job has finished, reschedule it. */
447 if (debug
>= 1) fprintf(stderr
, "pid %d has exited\n",
449 tab_reap_job((pid_t
) r
);
452 } while (!need_quit
);
454 /* Remove the pid file to signal that cron is gone. */
461 * $PchId: cron.c,v 1.4 2000/07/17 19:00:35 philip Exp $