source: trunk/minix/commands/cron/cron.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 10.1 KB
Line 
1/* cron 1.4 - clock daemon Author: Kees J. Bot
2 * 7 Dec 1996
3 */
4
5#define _MINIX_SOURCE
6#define _MINIX 1
7
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"
26
27#if __minix && !__minix_vmd
28#define initgroups(name, gid) (0)
29#endif
30
31static volatile int busy; /* Set when something is afoot, don't sleep! */
32static volatile int need_reload;/* Set if table reload required. */
33static volatile int need_quit; /* Set if cron must exit. */
34static volatile int debug; /* Debug level. */
35
36static 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.
41 */
42{
43 pid_t pid;
44 int need_mailer;
45 int mailfd[2], errfd[2];
46 struct passwd *pw;
47 crontab_t *tab= job->tab;
48
49 need_mailer= (tab->user != nil);
50
51 if (job->atjob) {
52 struct stat st;
53
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);
64 }
65 return;
66 }
67 /* Will need to determine the next AT job. */
68 need_reload= 1;
69
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;
75 }
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;
81 }
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;
88 }
89 }
90
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]);
99 }
100 tab_reschedule(job);
101 return;
102 }
103 (void) fcntl(errfd[1], F_SETFD,
104 fcntl(errfd[1], F_GETFD) | FD_CLOEXEC);
105
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;
115 }
116
117 if (pid == 0) {
118 /* Child that is to be the mailer. */
119 char subject[70+20], *ps;
120
121 close(errfd[0]);
122 close(mailfd[1]);
123 if (mailfd[0] != 0) {
124 dup2(mailfd[0], 0);
125 close(mailfd[0]);
126 }
127
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, "...");
135 }
136 for (ps= subject; *ps != 0; ps++) {
137 if (*ps == '\n') *ps= '%';
138 }
139
140 execl("/usr/bin/mail", "mail", "-s", subject,
141 pw->pw_name, (char *) nil);
142 write(errfd[1], &errno, sizeof(errno));
143 _exit(1);
144 }
145
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;
155 }
156 close(errfd[0]);
157 }
158
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;
164 }
165 (void) fcntl(errfd[1], F_SETFD, fcntl(errfd[1], F_GETFD) | FD_CLOEXEC);
166
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;
174 }
175
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]);
183 }
184 dup2(1, 2);
185 }
186
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;
198 }
199
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);
204 }
205 bad:
206 write(errfd[1], &errno, sizeof(errno));
207 _exit(1);
208 }
209
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;
217 }
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);
222}
223
224static 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.
227 */
228{
229 DIR *spool;
230#if __minix_vmd
231 FILE *pkgs;
232#endif
233
234 tab_parse("/usr/lib/crontab", nil);
235 tab_parse("/usr/local/lib/crontab", nil);
236 tab_parse("/var/lib/crontab", nil);
237
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];
244
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);
250 }
251 *np= 0;
252 while (c != EOF && c != '\n') c= fgetc(pkgs);
253
254 if (name[0] == 0) continue; /* ? */
255
256 strcpy(tab, "/var/opt/");
257 strcat(tab, name);
258 strcat(tab, "/lib/crontab");
259 tab_parse(tab, nil);
260 }
261 if (ferror(pkgs)) {
262 log(LOG_CRIT, "/usr/lib/packages: %s\n",
263 strerror(errno));
264 }
265 fclose(pkgs);
266 } else {
267 if (errno != ENOENT) {
268 log(LOG_ERR, "/usr/lib/packages: %s\n",
269 strerror(errno));
270 }
271 }
272#endif /* Minix-vmd */
273
274 if ((spool= opendir("/usr/spool/crontabs")) != nil) {
275 struct dirent *entry;
276 char tab[sizeof("/usr/spool/crontabs/") + NAME_MAX];
277
278 while ((entry= readdir(spool)) != nil) {
279 if (entry->d_name[0] == '.') continue;
280
281 strcpy(tab, "/usr/spool/crontabs/");
282 strcat(tab, entry->d_name);
283 tab_parse(tab, entry->d_name);
284 }
285 closedir(spool);
286 }
287
288 /* Find the first to be executed AT job. */
289 tab_find_atjob("/usr/spool/at");
290
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);
296 }
297}
298
299static void handler(int sig)
300{
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;
315 }
316 alarm(1); /* A signal may come just before a blocking call. */
317 busy= 1;
318}
319
320static void usage(void)
321{
322 fprintf(stderr, "Usage: %s [-d[#]]\n", prog_name);
323 exit(1);
324}
325
326int main(int argc, char **argv)
327{
328 int i;
329 struct sigaction sa, osa;
330 FILE *pf;
331 int r;
332
333 prog_name= strrchr(argv[0], '/');
334 if (prog_name == nil) prog_name= argv[0]; else prog_name++;
335
336 i= 1;
337 while (i < argc && argv[i][0] == '-') {
338 char *opt= argv[i++] + 1;
339
340 if (opt[0] == '-' && opt[1] == 0) break; /* -- */
341
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();
349 }
350 break;
351 default:
352 usage();
353 }
354 }
355 if (i != argc) usage();
356
357 selectlog(SYSLOG);
358 openlog(prog_name, LOG_PID, LOG_DAEMON);
359 setlogmask(LOG_UPTO(LOG_INFO));
360
361 /* Save process id. */
362 if ((pf= fopen(PIDFILE, "w")) == NULL) {
363 fprintf(stderr, "%s: %s\n", PIDFILE, strerror(errno));
364 exit(1);
365 }
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);
370 }
371
372 sigemptyset(&sa.sa_mask);
373 sa.sa_flags= 0;
374 sa.sa_handler= handler;
375
376 /* Hangup: Reload crontab files. */
377 sigaction(SIGHUP, &sa, nil);
378
379 /* User signal 1 & 2: Raise or reset debug level. */
380 sigaction(SIGUSR1, &sa, nil);
381 sigaction(SIGUSR2, &sa, nil);
382
383 /* Interrupt and Terminate: Cleanup and exit. */
384 if (sigaction(SIGINT, nil, &osa) == 0 && osa.sa_handler != SIG_IGN) {
385 sigaction(SIGINT, &sa, nil);
386 }
387 if (sigaction(SIGTERM, nil, &osa) == 0 && osa.sa_handler != SIG_IGN) {
388 sigaction(SIGTERM, &sa, nil);
389 }
390
391 /* Alarm: Wake up and run a job. */
392 sigaction(SIGALRM, &sa, nil);
393
394 /* Initialize current time and time next to do something. */
395 time(&now);
396 next= NEVER;
397
398 /* Table load required first time. */
399 need_reload= 1;
400
401 do {
402 if (need_reload) {
403 need_reload= 0;
404 load_crontabs();
405 busy= 1;
406 }
407
408 /* Run jobs whose time has come. */
409 if (next <= now) {
410 cronjob_t *job;
411
412 if ((job= tab_nextjob()) != nil) run_job(job);
413 busy= 1;
414 }
415
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;
427
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
435 }
436 if (debug >= 1) fprintf(stderr, "%s: sleep until %s",
437 prog_name, ctime(&next));
438
439 closelog(); /* Don't keep resources open. */
440
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);
446 }
447
448 if (r > 0) {
449 /* A job has finished, reschedule it. */
450 if (debug >= 1) fprintf(stderr, "pid %d has exited\n",
451 r);
452 tab_reap_job((pid_t) r);
453 busy= 1;
454 }
455 } while (!need_quit);
456
457 /* Remove the pid file to signal that cron is gone. */
458 unlink(PIDFILE);
459
460 return 0;
461}
462
463/*
464 * $PchId: cron.c,v 1.4 2000/07/17 19:00:35 philip Exp $
465 */
Note: See TracBrowser for help on using the repository browser.