coverity appeasement
[minix.git] / commands / cron / cron.c
blob1a09c2f40d313b904ce364b2994a3fd0650ae992
1 /* cron 1.4 - clock daemon Author: Kees J. Bot
2 * 7 Dec 1996
3 */
5 #define _MINIX_SOURCE
6 #define _MINIX 1
8 #define nil ((void*)0)
9 #include <sys/types.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <signal.h>
14 #include <limits.h>
15 #include <dirent.h>
16 #include <time.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <pwd.h>
21 #include <grp.h>
22 #include <sys/stat.h>
23 #include <sys/wait.h>
24 #include "misc.h"
25 #include "tab.h"
27 #if __minix && !__minix_vmd
28 #define initgroups(name, gid) (0)
29 #endif
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.
43 pid_t pid;
44 int need_mailer;
45 int mailfd[2], errfd[2];
46 struct passwd *pw;
47 crontab_t *tab= job->tab;
49 need_mailer= (tab->user != nil);
51 if (job->atjob) {
52 struct stat st;
54 need_mailer= 1;
55 if (rename(tab->file, tab->data) < 0) {
56 if (errno == ENOENT) {
57 /* Normal error, job deleted. */
58 need_reload= 1;
59 } else {
60 /* Bad error, halt processing AT jobs. */
61 log(LOG_CRIT, "Can't rename %s: %s\n",
62 tab->file, strerror(errno));
63 tab_reschedule(job);
65 return;
67 /* Will need to determine the next AT job. */
68 need_reload= 1;
70 if (stat(tab->data, &st) < 0) {
71 log(LOG_ERR, "Can't stat %s: %s\n",
72 tab->data, strerror(errno));
73 tab_reschedule(job);
74 return;
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);
79 tab_reschedule(job);
80 return;
82 } else {
83 pw= nil;
84 if (job->user != nil && (pw= getpwnam(job->user)) == nil) {
85 log(LOG_ERR, "%s: Unknown user\n", job->user);
86 tab_reschedule(job);
87 return;
91 if (need_mailer) {
92 errfd[0]= -1;
93 if (pipe(errfd) < 0 || pipe(mailfd) < 0) {
94 log(LOG_ERR, "pipe() call failed: %s\n",
95 strerror(errno));
96 if (errfd[0] != -1) {
97 close(errfd[0]);
98 close(errfd[1]);
100 tab_reschedule(job);
101 return;
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",
108 strerror(errno));
109 close(errfd[0]);
110 close(errfd[1]);
111 close(mailfd[0]);
112 close(mailfd[1]);
113 tab_reschedule(job);
114 return;
117 if (pid == 0) {
118 /* Child that is to be the mailer. */
119 char subject[70+20], *ps;
121 close(errfd[0]);
122 close(mailfd[1]);
123 if (mailfd[0] != 0) {
124 dup2(mailfd[0], 0);
125 close(mailfd[0]);
128 memset(subject, 0, sizeof(subject));
129 sprintf(subject,
130 "Output from your %s job: %.50s",
131 job->atjob ? "AT" : "cron",
132 job->cmd);
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));
143 _exit(1);
146 close(mailfd[0]);
147 close(errfd[1]);
148 if (read(errfd[0], &errno, sizeof(errno)) > 0) {
149 log(LOG_ERR, "can't execute /usr/bin/mail: %s\n",
150 strerror(errno));
151 close(errfd[0]);
152 close(mailfd[1]);
153 tab_reschedule(job);
154 return;
156 close(errfd[0]);
159 if (pipe(errfd) < 0) {
160 log(LOG_ERR, "pipe() call failed: %s\n", strerror(errno));
161 if (need_mailer) close(mailfd[1]);
162 tab_reschedule(job);
163 return;
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));
169 close(errfd[0]);
170 close(errfd[1]);
171 if (need_mailer) close(mailfd[1]);
172 tab_reschedule(job);
173 return;
176 if (pid == 0) {
177 /* Child that is to be the cron job. */
178 close(errfd[0]);
179 if (need_mailer) {
180 if (mailfd[1] != 1) {
181 dup2(mailfd[1], 1);
182 close(mailfd[1]);
184 dup2(1, 2);
187 if (pw != nil) {
188 /* Change id to the owner of the job. */
189 initgroups(pw->pw_name, pw->pw_gid);
190 setgid(pw->pw_gid);
191 setuid(pw->pw_uid);
192 chdir(pw->pw_dir);
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;
200 if (job->atjob) {
201 execl("/bin/sh", "sh", tab->data, (char *) nil);
202 } else {
203 execl("/bin/sh", "sh", "-c", job->cmd, (char *) nil);
205 bad:
206 write(errfd[1], &errno, sizeof(errno));
207 _exit(1);
210 if (need_mailer) close(mailfd[1]);
211 close(errfd[1]);
212 if (read(errfd[0], &errno, sizeof(errno)) > 0) {
213 log(LOG_ERR, "can't execute /bin/sh: %s\n", strerror(errno));
214 close(errfd[0]);
215 tab_reschedule(job);
216 return;
218 close(errfd[0]);
219 job->pid= pid;
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.
229 DIR *spool;
230 #if __minix_vmd
231 FILE *pkgs;
232 #endif
234 tab_parse("/usr/lib/crontab", nil);
235 tab_parse("/usr/local/lib/crontab", nil);
236 tab_parse("/var/lib/crontab", nil);
238 #if __minix_vmd
239 if ((pkgs= fopen("/usr/lib/packages", "r")) != nil) {
240 char name[NAME_MAX+1];
241 char *np;
242 int c;
243 char tab[sizeof("/var/opt//lib/crontab") + NAME_MAX];
245 while ((c= fgetc(pkgs)) != EOF) {
246 np= name;
247 while (c != EOF && c != '/' && c != '\n') {
248 if (np < name+NAME_MAX) *np++ = c;
249 c= fgetc(pkgs);
251 *np= 0;
252 while (c != EOF && c != '\n') c= fgetc(pkgs);
254 if (name[0] == 0) continue; /* ? */
256 strcpy(tab, "/var/opt/");
257 strcat(tab, name);
258 strcat(tab, "/lib/crontab");
259 tab_parse(tab, nil);
261 if (ferror(pkgs)) {
262 log(LOG_CRIT, "/usr/lib/packages: %s\n",
263 strerror(errno));
265 fclose(pkgs);
266 } else {
267 if (errno != ENOENT) {
268 log(LOG_ERR, "/usr/lib/packages: %s\n",
269 strerror(errno));
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);
285 closedir(spool);
288 /* Find the first to be executed AT job. */
289 tab_find_atjob("/usr/spool/at");
291 tab_purge();
292 if (debug >= 2) {
293 tab_print(stderr);
294 fprintf(stderr, "%lu memory chunks in use\n",
295 (unsigned long) alloc_count);
299 static void handler(int sig)
301 switch (sig) {
302 case SIGHUP:
303 need_reload= 1;
304 break;
305 case SIGINT:
306 case SIGTERM:
307 need_quit= 1;
308 break;
309 case SIGUSR1:
310 debug++;
311 break;
312 case SIGUSR2:
313 debug= 0;
314 break;
316 alarm(1); /* A signal may come just before a blocking call. */
317 busy= 1;
320 static void usage(void)
322 fprintf(stderr, "Usage: %s [-d[#]]\n", prog_name);
323 exit(1);
326 int main(int argc, char **argv)
328 int i;
329 struct sigaction sa, osa;
330 FILE *pf;
331 int r;
333 prog_name= strrchr(argv[0], '/');
334 if (prog_name == nil) prog_name= argv[0]; else prog_name++;
336 i= 1;
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++) {
343 case 'd':
344 if (*opt == 0) {
345 debug= 1;
346 } else {
347 debug= strtoul(opt, &opt, 10);
348 if (*opt != 0) usage();
350 break;
351 default:
352 usage();
355 if (i != argc) usage();
357 selectlog(SYSLOG);
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));
364 exit(1);
366 fprintf(pf, "%d\n", getpid());
367 if (ferror(pf) || fclose(pf) == EOF) {
368 fprintf(stderr, "%s: %s\n", PIDFILE, strerror(errno));
369 exit(1);
372 sigemptyset(&sa.sa_mask);
373 sa.sa_flags= 0;
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. */
395 time(&now);
396 next= NEVER;
398 /* Table load required first time. */
399 need_reload= 1;
401 do {
402 if (need_reload) {
403 need_reload= 0;
404 load_crontabs();
405 busy= 1;
408 /* Run jobs whose time has come. */
409 if (next <= now) {
410 cronjob_t *job;
412 if ((job= tab_nextjob()) != nil) run_job(job);
413 busy= 1;
416 if (busy) {
417 /* Did a job finish? */
418 r= waitpid(-1, nil, WNOHANG);
419 busy= 0;
420 } else {
421 /* Sleep until the next job must be started. */
422 if (next == NEVER) {
423 alarm(0);
424 } else {
425 #if __minix_vmd
426 struct timeval tvnext;
428 tvnext.tv_sec= next;
429 tvnext.tv_usec= 0;
430 sysutime(UTIME_SETALARM, &tvnext);
431 #else
432 alarm((next - now) > INT_MAX
433 ? INT_MAX : (next - now));
434 #endif
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();
444 alarm(0);
445 time(&now);
448 if (r > 0) {
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);
453 busy= 1;
455 } while (!need_quit);
457 /* Remove the pid file to signal that cron is gone. */
458 unlink(PIDFILE);
460 return 0;
464 * $PchId: cron.c,v 1.4 2000/07/17 19:00:35 philip Exp $