Remove building with NOCRYPTO option
[minix.git] / minix / commands / cron / cron.c
blob3f7e5a572e1fbedf028b2fdab1e656f63e36b475
1 /* cron 1.4 - clock daemon Author: Kees J. Bot
2 * 7 Dec 1996
3 */
5 #define nil ((void*)0)
6 #include <sys/types.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <signal.h>
11 #include <limits.h>
12 #include <dirent.h>
13 #include <time.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <pwd.h>
18 #include <grp.h>
19 #include <sys/stat.h>
20 #include <sys/wait.h>
21 #include "misc.h"
22 #include "tab.h"
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.
36 pid_t pid;
37 int need_mailer;
38 int mailfd[2], errfd[2];
39 struct passwd *pw;
40 crontab_t *tab= job->tab;
42 need_mailer= (tab->user != nil);
44 if (job->atjob) {
45 struct stat st;
47 need_mailer= 1;
48 if (rename(tab->file, tab->data) < 0) {
49 if (errno == ENOENT) {
50 /* Normal error, job deleted. */
51 need_reload= 1;
52 } else {
53 /* Bad error, halt processing AT jobs. */
54 cronlog(LOG_CRIT, "Can't rename %s: %s\n",
55 tab->file, strerror(errno));
56 tab_reschedule(job);
58 return;
60 /* Will need to determine the next AT job. */
61 need_reload= 1;
63 if (stat(tab->data, &st) < 0) {
64 cronlog(LOG_ERR, "Can't stat %s: %s\n",
65 tab->data, strerror(errno));
66 tab_reschedule(job);
67 return;
69 if ((pw= getpwuid(st.st_uid)) == nil) {
70 cronlog(LOG_ERR,
71 "Unknown owner for uid %lu of AT job %s\n",
72 (unsigned long) st.st_uid, job->cmd);
73 tab_reschedule(job);
74 return;
76 } else {
77 pw= nil;
78 if (job->user != nil && (pw= getpwnam(job->user)) == nil) {
79 cronlog(LOG_ERR, "%s: Unknown user\n", job->user);
80 tab_reschedule(job);
81 return;
85 if (need_mailer) {
86 errfd[0]= -1;
87 if (pipe(errfd) < 0 || pipe(mailfd) < 0) {
88 cronlog(LOG_ERR, "pipe() call failed: %s\n",
89 strerror(errno));
90 if (errfd[0] != -1) {
91 close(errfd[0]);
92 close(errfd[1]);
94 tab_reschedule(job);
95 return;
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",
102 strerror(errno));
103 close(errfd[0]);
104 close(errfd[1]);
105 close(mailfd[0]);
106 close(mailfd[1]);
107 tab_reschedule(job);
108 return;
111 if (pid == 0) {
112 /* Child that is to be the mailer. */
113 char subject[70+20], *ps;
115 close(errfd[0]);
116 close(mailfd[1]);
117 if (mailfd[0] != 0) {
118 dup2(mailfd[0], 0);
119 close(mailfd[0]);
122 memset(subject, 0, sizeof(subject));
123 sprintf(subject,
124 "Output from your %s job: %.50s",
125 job->atjob ? "AT" : "cron",
126 job->cmd);
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));
137 _exit(1);
140 close(mailfd[0]);
141 close(errfd[1]);
142 if (read(errfd[0], &errno, sizeof(errno)) > 0) {
143 cronlog(LOG_ERR, "can't execute /usr/bin/mail: %s\n",
144 strerror(errno));
145 close(errfd[0]);
146 close(mailfd[1]);
147 tab_reschedule(job);
148 return;
150 close(errfd[0]);
153 if (pipe(errfd) < 0) {
154 cronlog(LOG_ERR, "pipe() call failed: %s\n", strerror(errno));
155 if (need_mailer) close(mailfd[1]);
156 tab_reschedule(job);
157 return;
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));
163 close(errfd[0]);
164 close(errfd[1]);
165 if (need_mailer) close(mailfd[1]);
166 tab_reschedule(job);
167 return;
170 if (pid == 0) {
171 /* Child that is to be the cron job. */
172 close(errfd[0]);
173 if (need_mailer) {
174 if (mailfd[1] != 1) {
175 dup2(mailfd[1], 1);
176 close(mailfd[1]);
178 dup2(1, 2);
181 if (pw != nil) {
182 /* Change id to the owner of the job. */
183 initgroups(pw->pw_name, pw->pw_gid);
184 setgid(pw->pw_gid);
185 setuid(pw->pw_uid);
186 chdir(pw->pw_dir);
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;
194 if (job->atjob) {
195 execl("/bin/sh", "sh", tab->data, (char *) nil);
196 } else {
197 execl("/bin/sh", "sh", "-c", job->cmd, (char *) nil);
199 bad:
200 write(errfd[1], &errno, sizeof(errno));
201 _exit(1);
204 if (need_mailer) close(mailfd[1]);
205 close(errfd[1]);
206 if (read(errfd[0], &errno, sizeof(errno)) > 0) {
207 cronlog(LOG_ERR, "can't execute /bin/sh: %s\n",
208 strerror(errno));
209 close(errfd[0]);
210 tab_reschedule(job);
211 return;
213 close(errfd[0]);
214 job->pid= pid;
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.
224 DIR *spool;
225 #if __minix_vmd
226 FILE *pkgs;
227 #endif
229 tab_parse("/usr/lib/crontab", nil);
230 tab_parse("/usr/local/lib/crontab", nil);
231 tab_parse("/var/lib/crontab", nil);
233 #if __minix_vmd
234 if ((pkgs= fopen("/usr/lib/packages", "r")) != nil) {
235 char name[NAME_MAX+1];
236 char *np;
237 int c;
238 char tab[sizeof("/var/opt//lib/crontab") + NAME_MAX];
240 while ((c= fgetc(pkgs)) != EOF) {
241 np= name;
242 while (c != EOF && c != '/' && c != '\n') {
243 if (np < name+NAME_MAX) *np++ = c;
244 c= fgetc(pkgs);
246 *np= 0;
247 while (c != EOF && c != '\n') c= fgetc(pkgs);
249 if (name[0] == 0) continue; /* ? */
251 strcpy(tab, "/var/opt/");
252 strcat(tab, name);
253 strcat(tab, "/lib/crontab");
254 tab_parse(tab, nil);
256 if (ferror(pkgs)) {
257 cronlog(LOG_CRIT, "/usr/lib/packages: %s\n",
258 strerror(errno));
260 fclose(pkgs);
261 } else {
262 if (errno != ENOENT) {
263 cronlog(LOG_ERR, "/usr/lib/packages: %s\n",
264 strerror(errno));
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);
280 closedir(spool);
283 /* Find the first to be executed AT job. */
284 tab_find_atjob("/usr/spool/at");
286 tab_purge();
287 if (debug >= 2) {
288 tab_print(stderr);
289 fprintf(stderr, "%lu memory chunks in use\n",
290 (unsigned long) alloc_count);
294 static void handler(int sig)
296 switch (sig) {
297 case SIGHUP:
298 need_reload= 1;
299 break;
300 case SIGINT:
301 case SIGTERM:
302 need_quit= 1;
303 break;
304 case SIGUSR1:
305 debug++;
306 break;
307 case SIGUSR2:
308 debug= 0;
309 break;
311 alarm(1); /* A signal may come just before a blocking call. */
312 busy= 1;
315 static void usage(void)
317 fprintf(stderr, "Usage: %s [-d[#]]\n", prog_name);
318 exit(1);
321 int main(int argc, char **argv)
323 int i;
324 struct sigaction sa, osa;
325 FILE *pf;
326 int r;
328 prog_name= strrchr(argv[0], '/');
329 if (prog_name == nil) prog_name= argv[0]; else prog_name++;
331 i= 1;
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++) {
338 case 'd':
339 if (*opt == 0) {
340 debug= 1;
341 } else {
342 debug= strtoul(opt, &opt, 10);
343 if (*opt != 0) usage();
345 break;
346 default:
347 usage();
350 if (i != argc) usage();
352 selectlog(SYSLOG);
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));
359 exit(1);
361 fprintf(pf, "%d\n", getpid());
362 if (ferror(pf) || fclose(pf) == EOF) {
363 fprintf(stderr, "%s: %s\n", PIDFILE, strerror(errno));
364 exit(1);
367 sigemptyset(&sa.sa_mask);
368 sa.sa_flags= 0;
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. */
390 time(&now);
391 next= NEVER;
393 /* Table load required first time. */
394 need_reload= 1;
396 do {
397 if (need_reload) {
398 need_reload= 0;
399 load_crontabs();
400 busy= 1;
403 /* Run jobs whose time has come. */
404 if (next <= now) {
405 cronjob_t *job;
407 if ((job= tab_nextjob()) != nil) run_job(job);
408 busy= 1;
411 if (busy) {
412 /* Did a job finish? */
413 r= waitpid(-1, nil, WNOHANG);
414 busy= 0;
415 } else {
416 /* Sleep until the next job must be started. */
417 if (next == NEVER) {
418 alarm(0);
419 } else {
420 #if __minix_vmd
421 struct timeval tvnext;
423 tvnext.tv_sec= next;
424 tvnext.tv_usec= 0;
425 sysutime(UTIME_SETALARM, &tvnext);
426 #else
427 alarm((next - now) > INT_MAX
428 ? INT_MAX : (next - now));
429 #endif
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();
439 alarm(0);
440 time(&now);
443 if (r > 0) {
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);
448 busy= 1;
450 } while (!need_quit);
452 /* Remove the pid file to signal that cron is gone. */
453 unlink(PIDFILE);
455 return 0;
459 * $PchId: cron.c,v 1.4 2000/07/17 19:00:35 philip Exp $