xref: /titanic_51/usr/src/cmd/cron/cron.c (revision a776e90690d98f629ab57f6493ff5949af9d65bd)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
30 /*	  All Rights Reserved	*/
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 #ifdef lint
35 /* make lint happy */
36 #define	__EXTENSIONS__
37 #endif
38 
39 #include <sys/contract/process.h>
40 #include <sys/ctfs.h>
41 #include <sys/param.h>
42 #include <sys/resource.h>
43 #include <sys/stat.h>
44 #include <sys/task.h>
45 #include <sys/time.h>
46 #include <sys/types.h>
47 #include <sys/utsname.h>
48 #include <sys/wait.h>
49 
50 #include <security/pam_appl.h>
51 
52 #include <alloca.h>
53 #include <ctype.h>
54 #include <deflt.h>
55 #include <dirent.h>
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <grp.h>
59 #include <libcontract.h>
60 #include <libcontract_priv.h>
61 #include <limits.h>
62 #include <locale.h>
63 #include <poll.h>
64 #include <project.h>
65 #include <pwd.h>
66 #include <signal.h>
67 #include <stdarg.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <stropts.h>
72 #include <time.h>
73 #include <unistd.h>
74 
75 #include "cron.h"
76 
77 /*
78  * #define	DEBUG
79  */
80 
81 #define	MAIL		"/usr/bin/mail"	/* mail program to use */
82 #define	CONSOLE		"/dev/console"	/* where messages go when cron dies */
83 
84 #define	TMPINFILE	"/tmp/crinXXXXXX"  /* file to put stdin in for cmd  */
85 #define	TMPDIR		"/tmp"
86 #define	PFX		"crout"
87 #define	TMPOUTFILE	"/tmp/croutXXXXXX" /* file to place stdout, stderr */
88 
89 #define	INMODE		00400		/* mode for stdin file	*/
90 #define	OUTMODE		00600		/* mode for stdout file */
91 #define	ISUID		S_ISUID		/* mode for verifing at jobs */
92 
93 #define	INFINITY	2147483647L	/* upper bound on time	*/
94 #define	CUSHION		180L
95 #define	ZOMB		100		/* proc slot used for mailing output */
96 
97 #define	JOBF		'j'
98 #define	NICEF		'n'
99 #define	USERF		'u'
100 #define	WAITF		'w'
101 
102 #define	BCHAR		'>'
103 #define	ECHAR		'<'
104 
105 #define	DEFAULT		0
106 #define	LOAD		1
107 #define	QBUFSIZ		80
108 
109 /* Defined actions for crabort() routine */
110 #define	NO_ACTION	000
111 #define	REMOVE_FIFO	001
112 #define	CONSOLE_MSG	002
113 
114 #define	BADCD		"can't change directory to the crontab directory."
115 #define	NOREADDIR	"can't read the crontab directory."
116 
117 #define	BADJOBOPEN	"unable to read your at job."
118 #define	BADSHELL	"because your login shell \
119 isn't /usr/bin/sh, you can't use cron."
120 
121 #define	BADSTAT		"can't access your crontab or at-job file. Resubmit it."
122 #define	BADPROJID	"can't set project id for your job."
123 #define	CANTCDHOME	"can't change directory to your home directory.\
124 \nYour commands will not be executed."
125 #define	CANTEXECSH	"unable to exec the shell for one of your commands."
126 #define	NOREAD		"can't read your crontab file.  Resubmit it."
127 #define	BADTYPE		"crontab or at-job file is not a regular file.\n"
128 #define	NOSTDIN		"unable to create a standard input file for \
129 one of your crontab commands. \
130 \nThat command was not executed."
131 
132 #define	NOTALLOWED	"you are not authorized to use cron.  Sorry."
133 #define	STDERRMSG	"\n\n********************************************\
134 *****\nCron: The previous message is the \
135 standard output and standard error \
136 \nof one of your cron commands.\n"
137 
138 #define	STDOUTERR	"one of your commands generated output or errors, \
139 but cron was unable to mail you this output.\
140 \nRemember to redirect standard output and standard \
141 error for each of your commands."
142 
143 #define	CLOCK_DRIFT	"clock time drifted backwards after event!\n"
144 #define	PIDERR		"unexpected pid returned %d (ignored)"
145 #define	CRONTABERR	"Subject: Your crontab file has an error in it\n\n"
146 #define	CRONOUT		"Subject: Output from \"cron\" command\n\n"
147 #define	MALLOCERR	"out of space, cannot create new string\n"
148 
149 #define	DIDFORK didfork
150 #define	NOFORK !didfork
151 
152 #define	MAILBUFLEN	(8*1024)
153 #define	LINELIMIT	80
154 #define	MAILBINITFREE	(MAILBUFLEN - (sizeof (cte_intro) - 1) \
155 	    - (sizeof (cte_trail1) - 1) - (sizeof (cte_trail2) - 1) - 1)
156 
157 #define	ERR_CRONTABENT	0	/* error in crontab file entry */
158 #define	ERR_UNIXERR	1	/* error in some system call */
159 #define	ERR_CANTEXECCRON 2	/* error setting up "cron" job environment */
160 #define	ERR_CANTEXECAT	3	/* error setting up "at" job environment */
161 #define	ERR_NOTREG	4	/* error not a regular file */
162 
163 #define	PROJECT		"project="
164 
165 #define	MAX_LOST_CONTRACTS	2048	/* reset if this many failed abandons */
166 
167 #define	FORMAT	"%a %b %e %H:%M:%S %Y"
168 static char	timebuf[80];
169 
170 struct event {
171 	time_t time;	/* time of the event	*/
172 	short etype;	/* what type of event; 0=cron, 1=at	*/
173 	char *cmd;	/* command for cron, job name for at	*/
174 	struct usr *u;	/* ptr to the owner (usr) of this event	*/
175 	struct event *link;	/* ptr to another event for this user */
176 	union {
177 		struct { /* for crontab events */
178 			char *minute;	/*  (these	*/
179 			char *hour;	/*   fields	*/
180 			char *daymon;	/*   are	*/
181 			char *month;	/*   from	*/
182 			char *dayweek;	/*   crontab)	*/
183 			char *input;	/* ptr to stdin	*/
184 		} ct;
185 		struct { /* for at events */
186 			short exists;	/* for revising at events	*/
187 			int eventid;	/* for el_remove-ing at events	*/
188 		} at;
189 	} of;
190 };
191 
192 struct usr {
193 	char *name;	/* name of user (e.g. "root")	*/
194 	char *home;	/* home directory for user	*/
195 	uid_t uid;	/* user id	*/
196 	gid_t gid;	/* group id	*/
197 	int aruncnt;	/* counter for running jobs per uid */
198 	int cruncnt;	/* counter for running cron jobs per uid */
199 	int ctid;	/* for el_remove-ing crontab events */
200 	short ctexists;	/* for revising crontab events	*/
201 	struct event *ctevents;	/* list of this usr's crontab events */
202 	struct event *atevents;	/* list of this usr's at events */
203 	struct usr *nextusr;
204 };	/* ptr to next user	*/
205 
206 static struct	queue
207 {
208 	int njob;	/* limit */
209 	int nice;	/* nice for execution */
210 	int nwait;	/* wait time to next execution attempt */
211 	int nrun;	/* number running */
212 }
213 	qd = {100, 2, 60},		/* default values for queue defs */
214 	qt[NQUEUE];
215 static struct	queue	qq;
216 
217 static struct runinfo
218 {
219 	pid_t	pid;
220 	short	que;
221 	struct  usr *rusr;	/* pointer to usr struct */
222 	char	*outfile;	/* file where stdout & stderr are trapped */
223 	short	jobtype;	/* what type of event: 0=cron, 1=at */
224 	char	*jobname;	/* command for "cron", jobname for "at" */
225 	int	mailwhendone;	/* 1 = send mail even if no ouptut */
226 	struct runinfo *next;
227 }	*rthead;
228 
229 static struct miscpid {
230 	pid_t		pid;
231 	struct miscpid	*next;
232 }	*miscpid_head;
233 
234 static pid_t cron_pid;	/* own pid */
235 static char didfork = 0; /* flag to see if I'm process group leader */
236 static int msgfd;	/* file descriptor for fifo queue */
237 static int ecid = 1;	/* event class id for el_remove(); MUST be set to 1 */
238 static int delayed;	/* is job being rescheduled or did it run first time */
239 static int cwd;		/* current working directory */
240 static struct event *next_event;	/* the next event to execute	*/
241 static struct usr *uhead;		/* ptr to the list of users	*/
242 
243 /* Variables for error handling at reading crontabs. */
244 static char cte_intro[] = "Line(s) with errors:\n\n";
245 static char cte_trail1[] = "\nMax number of errors encountered.";
246 static char cte_trail2[] = " Evaluation of crontab aborted.\n";
247 static int cte_free = MAILBINITFREE;	/* Free buffer space */
248 static char *cte_text = NULL;		/* Text buffer pointer */
249 static char *cte_lp;			/* Next free line in cte_text */
250 static int cte_nvalid;			/* Valid lines found */
251 
252 /* user's default environment for the shell */
253 #define	ROOTPATH	"PATH=/usr/sbin:/usr/bin"
254 #define	NONROOTPATH	"PATH=/usr/bin:"
255 
256 static char *Def_supath	= NULL;
257 static char *Def_path		= NULL;
258 static char path[LINE_MAX]	= "PATH=";
259 static char supath[LINE_MAX]	= "PATH=";
260 static char homedir[LINE_MAX]	= "HOME=";
261 static char logname[LINE_MAX]	= "LOGNAME=";
262 static char tzone[LINE_MAX]	= "TZ=";
263 static char *envinit[] = {
264 	homedir,
265 	logname,
266 	ROOTPATH,
267 	"SHELL=/usr/bin/sh",
268 	tzone,
269 	NULL
270 };
271 
272 extern char **environ;
273 
274 #define	DEFTZ		"GMT"
275 static	int	log = 0;
276 static	char	hzname[10];
277 
278 static void cronend(int);
279 static void thaw_handler(int);
280 static void child_handler(int);
281 static void child_sigreset(void);
282 
283 static void mod_ctab(char *, time_t);
284 static void mod_atjob(char *, time_t);
285 static void add_atevent(struct usr *, char *, time_t, int);
286 static void rm_ctevents(struct usr *);
287 static void cleanup(struct runinfo *rn, int r);
288 static void crabort(char *, int);
289 static void msg(char *fmt, ...);
290 static void logit(int, struct runinfo *, int);
291 static void parsqdef(char *);
292 static void defaults();
293 static void initialize(int);
294 static void quedefs(int);
295 static int idle(long);
296 static struct usr *find_usr(char *);
297 static int ex(struct event *e);
298 static void read_dirs(int);
299 static void mail(char *, char *, int);
300 static char *next_field(int, int);
301 static void readcron(struct usr *, time_t);
302 static int next_ge(int, char *);
303 static void free_if_unused(struct usr *);
304 static void del_atjob(char *, char *);
305 static void del_ctab(char *);
306 static void resched(int);
307 static int msg_wait(long);
308 static struct runinfo *rinfo_get(pid_t);
309 static void rinfo_free(struct runinfo *rp);
310 static void mail_result(struct usr *p, struct runinfo *pr, size_t filesize);
311 static time_t next_time(struct event *, time_t);
312 static time_t get_switching_time(int, time_t);
313 static time_t xmktime(struct tm *);
314 static void process_msg(struct message *, time_t);
315 static void reap_child(void);
316 static void miscpid_insert(pid_t);
317 static int miscpid_delete(pid_t);
318 static void contract_set_template(void);
319 static void contract_clear_template(void);
320 static void contract_abandon_latest(pid_t);
321 
322 static void cte_init(void);
323 static void cte_add(int, char *);
324 static void cte_valid(void);
325 static int cte_istoomany(void);
326 static void cte_sendmail(char *);
327 
328 static int set_user_cred(const struct usr *, struct project *);
329 
330 /*
331  * last_time is set immediately prior to exection of an event (via ex())
332  * to indicate the last time an event was executed.  This was (surely)
333  * it's original intended use.
334  */
335 static time_t last_time, init_time, t_old;
336 
337 static int		accept_sigcld, notifypipe[2];
338 static sigset_t		defmask, childmask;
339 
340 /*
341  * BSM hooks
342  */
343 extern int	audit_cron_session(char *, char *, uid_t, gid_t, char *);
344 extern void	audit_cron_new_job(char *, int, void *);
345 extern void	audit_cron_bad_user(char *);
346 extern void	audit_cron_user_acct_expired(char *);
347 extern int	audit_cron_create_anc_file(char *, char *, char *, uid_t);
348 extern int	audit_cron_delete_anc_file(char *, char *);
349 extern int	audit_cron_is_anc_name(char *);
350 extern int	audit_cron_mode();
351 
352 static int cron_conv(int, struct pam_message **,
353 		struct pam_response **, void *);
354 
355 static struct pam_conv pam_conv = {cron_conv, NULL};
356 static pam_handle_t *pamh;	/* Authentication handle */
357 
358 /*
359  * Function to help check a user's credentials.
360  */
361 
362 static int verify_user_cred(struct usr *u);
363 
364 /*
365  * Values returned by verify_user_cred and set_user_cred:
366  */
367 
368 #define	VUC_OK		0
369 #define	VUC_BADUSER	1
370 #define	VUC_NOTINGROUP	2
371 #define	VUC_EXPIRED	3
372 #define	VUC_NEW_AUTH	4
373 
374 /*
375  * Modes of process_anc_files function
376  */
377 #define	CRON_ANC_DELETE	1
378 #define	CRON_ANC_CREATE	0
379 
380 /*
381  * Functions to remove a user or job completely from the running database.
382  */
383 static void clean_out_atjobs(struct usr *u);
384 static void clean_out_ctab(struct usr *u);
385 static void clean_out_user(struct usr *u);
386 static void cron_unlink(char *name);
387 static void process_anc_files(int);
388 
389 /*
390  * functions in elm.c
391  */
392 extern void el_init(int, time_t, time_t, int);
393 extern void el_add(void *, time_t, int);
394 extern void el_remove(int, int);
395 extern int el_empty(void);
396 extern void *el_first(void);
397 extern void el_delete(void);
398 
399 static int valid_entry(char *, int);
400 static struct usr *create_ulist(char *, int);
401 static void init_cronevent(char *, int);
402 static void init_atevent(char *, time_t, int, int);
403 static void update_atevent(struct usr *, char *, time_t, int);
404 
405 int
406 main(int argc, char *argv[])
407 {
408 	time_t t;
409 	time_t ne_time;		/* amt of time until next event execution */
410 	time_t newtime, lastmtime = 0L;
411 	struct usr *u;
412 	struct event *e, *e2, *eprev;
413 	struct stat buf;
414 	pid_t rfork;
415 	struct sigaction act;
416 
417 	/*
418 	 * reset is set to 1 via the return from ex() should ex() find
419 	 * that the event to be executed is being run at the wrong time.
420 	 * We immediately return to the top of the while (TRUE) loop in
421 	 * main() where the event list is cleared and rebuilt, and reset
422 	 * is set back to 0.
423 	 */
424 	int reset = 0;
425 
426 	/*
427 	 * Only the privileged user can run this command.
428 	 */
429 	if (getuid() != 0)
430 		crabort(NOTALLOWED, 0);
431 
432 begin:
433 	(void) setlocale(LC_ALL, "");
434 	/* fork unless 'nofork' is specified */
435 	if ((argc <= 1) || (strcmp(argv[1], "nofork"))) {
436 		if (rfork = fork()) {
437 			if (rfork == (pid_t)-1) {
438 				(void) sleep(30);
439 				goto begin;
440 			}
441 			return (0);
442 		}
443 		didfork++;
444 		(void) setpgrp();	/* detach cron from console */
445 	}
446 
447 	(void) umask(022);
448 	(void) signal(SIGHUP, SIG_IGN);
449 	(void) signal(SIGINT, SIG_IGN);
450 	(void) signal(SIGQUIT, SIG_IGN);
451 	(void) signal(SIGTERM, cronend);
452 
453 	defaults();
454 	initialize(1);
455 	quedefs(DEFAULT);	/* load default queue definitions */
456 	cron_pid = getpid();
457 	msg("*** cron started ***   pid = %d", cron_pid);
458 	(void) sigset(SIGTHAW, thaw_handler);
459 	/*
460 	 * setup SIGCLD handler/mask
461 	 */
462 	act.sa_handler = child_handler;
463 	act.sa_flags = 0;
464 	(void) sigemptyset(&act.sa_mask);
465 	(void) sigaddset(&act.sa_mask, SIGCLD);
466 	(void) sigaction(SIGCLD, &act, NULL);
467 	(void) sigemptyset(&childmask);
468 	(void) sigaddset(&childmask, SIGCLD);
469 	(void) sigprocmask(SIG_BLOCK, &childmask, &defmask);
470 
471 	if (pipe(notifypipe) != 0) {
472 		crabort("cannot create pipe", REMOVE_FIFO|CONSOLE_MSG);
473 	}
474 	/*
475 	 * will set O_NONBLOCK, so that the write() from child_handler
476 	 * never be blocked.
477 	 */
478 	(void) fcntl(notifypipe[0], F_SETFL, O_WRONLY|O_NONBLOCK);
479 
480 	t_old = time(NULL);
481 	last_time = t_old;
482 	for (;;) {		/* MAIN LOOP */
483 		t = time(NULL);
484 		if ((t_old > t) || (t-last_time > CUSHION) || reset) {
485 			reset = 0;
486 			/* the time was set backwards or forward */
487 			el_delete();
488 			u = uhead;
489 			while (u != NULL) {
490 				rm_ctevents(u);
491 				e = u->atevents;
492 				while (e != NULL) {
493 					free(e->cmd);
494 					e2 = e->link;
495 					free(e);
496 					e = e2;
497 				}
498 				u->atevents = NULL;
499 				u = u->nextusr;
500 			}
501 			(void) close(msgfd);
502 			initialize(0);
503 			t = time(NULL);
504 			last_time = t;
505 		}
506 		t_old = t;
507 
508 		if (next_event == NULL && !el_empty()) {
509 			next_event = (struct event *)el_first();
510 		}
511 		if (next_event == NULL) {
512 			ne_time = INFINITY;
513 		} else {
514 			ne_time = next_event->time - t;
515 #ifdef DEBUG
516 			cftime(timebuf, "%C", &next_event->time);
517 			(void) fprintf(stderr, "next_time=%ld %s\n",
518 			    next_event->time, timebuf);
519 #endif
520 		}
521 		if (ne_time > 0) {
522 			if ((reset = idle(ne_time)) != 0)
523 				continue;
524 		}
525 
526 		if (stat(QUEDEFS, &buf)) {
527 			msg("cannot stat QUEDEFS file");
528 		} else if (lastmtime != buf.st_mtime) {
529 			quedefs(LOAD);
530 			lastmtime = buf.st_mtime;
531 		}
532 
533 		last_time = next_event->time; /* save execution time */
534 
535 		if (reset = ex(next_event))
536 			continue;
537 
538 		switch (next_event->etype) {
539 		case CRONEVENT:
540 			/* add cronevent back into the main event list */
541 			if (delayed) {
542 				delayed = 0;
543 				break;
544 			}
545 
546 			/*
547 			 * check if time(0)< last_time. if so, then the
548 			 * system clock has gone backwards. to prevent this
549 			 * job from being started twice, we reschedule this
550 			 * job for the >>next time after last_time<<, and
551 			 * then set next_event->time to this. note that
552 			 * crontab's resolution is 1 minute.
553 			 */
554 
555 			if (last_time > time(NULL)) {
556 				msg(CLOCK_DRIFT);
557 				/*
558 				 * bump up to next 30 second
559 				 * increment
560 				 * 1 <= newtime <= 30
561 				 */
562 				newtime = 30 - (last_time % 30);
563 				newtime += last_time;
564 
565 				/*
566 				 * get the next scheduled event,
567 				 * not the one that we just
568 				 * kicked off!
569 				 */
570 				next_event->time =
571 				    next_time(next_event, newtime);
572 				t_old = time(NULL);
573 			} else {
574 				next_event->time =
575 				    next_time(next_event, (time_t)0);
576 			}
577 #ifdef DEBUG
578 			cftime(timebuf, "%C", &next_event->time);
579 			(void) fprintf(stderr,
580 			    "pushing back cron event %s at %ld (%s)\n",
581 			    next_event->cmd, next_event->time, timebuf);
582 #endif
583 
584 			el_add(next_event, next_event->time,
585 			    (next_event->u)->ctid);
586 			break;
587 		default:
588 			/* remove at or batch job from system */
589 			if (delayed) {
590 				delayed = 0;
591 				break;
592 			}
593 			eprev = NULL;
594 			e = (next_event->u)->atevents;
595 			while (e != NULL) {
596 				if (e == next_event) {
597 					if (eprev == NULL)
598 						(e->u)->atevents = e->link;
599 					else
600 						eprev->link = e->link;
601 					free(e->cmd);
602 					free(e);
603 					break;
604 				} else {
605 					eprev = e;
606 					e = e->link;
607 				}
608 			}
609 			break;
610 		}
611 		next_event = NULL;
612 	}
613 
614 	/*NOTREACHED*/
615 }
616 
617 static void
618 initialize(int firstpass)
619 {
620 #ifdef DEBUG
621 	(void) fprintf(stderr, "in initialize\n");
622 #endif
623 	init_time = time(NULL);
624 	el_init(8, init_time, (time_t)(60*60*24), 10);
625 	if (firstpass) {
626 		/* for mail(1), make sure messages come from root */
627 		if (putenv("LOGNAME=root") != 0) {
628 			crabort("cannot expand env variable",
629 			    REMOVE_FIFO|CONSOLE_MSG);
630 		}
631 		if (access(FIFO, R_OK) == -1) {
632 			if (errno == ENOENT) {
633 				if (mknod(FIFO, S_IFIFO|0600, 0) != 0)
634 					crabort("cannot create fifo queue",
635 					    REMOVE_FIFO|CONSOLE_MSG);
636 			} else {
637 				if (NOFORK) {
638 					/* didn't fork... init(1M) is waiting */
639 					(void) sleep(60);
640 				}
641 				perror("FIFO");
642 				crabort("cannot access fifo queue",
643 				    REMOVE_FIFO|CONSOLE_MSG);
644 			}
645 		} else {
646 			if (NOFORK) {
647 				/* didn't fork... init(1M) is waiting */
648 				(void) sleep(60);
649 				/*
650 				 * the wait is painful, but we don't want
651 				 * init respawning this quickly
652 				 */
653 			}
654 			crabort("cannot start cron; FIFO exists", CONSOLE_MSG);
655 		}
656 	}
657 
658 	if ((msgfd = open(FIFO, O_RDWR)) < 0) {
659 		perror("! open");
660 		crabort("cannot open fifo queue", REMOVE_FIFO|CONSOLE_MSG);
661 	}
662 
663 	/*
664 	 * read directories, create users list, and add events to the
665 	 * main event list. Only zero user list on firstpass.
666 	 */
667 	if (firstpass)
668 		uhead = NULL;
669 	read_dirs(firstpass);
670 	next_event = NULL;
671 
672 	if (!firstpass)
673 		return;
674 
675 	/* stdout is log file */
676 	if (freopen(ACCTFILE, "a", stdout) == NULL)
677 		(void) fprintf(stderr, "cannot open %s\n", ACCTFILE);
678 
679 	/* log should be root-only */
680 	(void) fchmod(1, S_IRUSR|S_IWUSR);
681 
682 	/* stderr also goes to ACCTFILE */
683 	(void) close(fileno(stderr));
684 	(void) dup(1);
685 
686 	/* null for stdin */
687 	(void) freopen("/dev/null", "r", stdin);
688 
689 	contract_set_template();
690 }
691 
692 static void
693 read_dirs(int first)
694 {
695 	DIR		*dir;
696 	struct dirent	*dp;
697 	char		*ptr;
698 	int		jobtype;
699 	time_t		tim;
700 
701 
702 	if (chdir(CRONDIR) == -1)
703 		crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG);
704 	cwd = CRON;
705 	if ((dir = opendir(".")) == NULL)
706 		crabort(NOREADDIR, REMOVE_FIFO|CONSOLE_MSG);
707 	while ((dp = readdir(dir)) != NULL) {
708 		if (!valid_entry(dp->d_name, CRONEVENT))
709 			continue;
710 		init_cronevent(dp->d_name, first);
711 	}
712 	(void) closedir(dir);
713 
714 	if (chdir(ATDIR) == -1) {
715 		msg("cannot chdir to at directory");
716 		return;
717 	}
718 	if ((dir = opendir(".")) == NULL) {
719 		msg("cannot read at at directory");
720 		return;
721 	}
722 	cwd = AT;
723 	while ((dp = readdir(dir)) != NULL) {
724 		if (!valid_entry(dp->d_name, ATEVENT))
725 			continue;
726 		ptr = dp->d_name;
727 		if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
728 			continue;
729 		ptr++;
730 		if (!isalpha(*ptr))
731 			continue;
732 		jobtype = *ptr - 'a';
733 		if (jobtype >= NQUEUE) {
734 			cron_unlink(dp->d_name);
735 			continue;
736 		}
737 		init_atevent(dp->d_name, tim, jobtype, first);
738 	}
739 	(void) closedir(dir);
740 }
741 
742 static int
743 valid_entry(char *name, int type)
744 {
745 	struct stat	buf;
746 
747 	if (strcmp(name, ".") == 0 ||
748 	    strcmp(name, "..") == 0)
749 		return (0);
750 
751 	/* skip over ancillary file names */
752 	if (audit_cron_is_anc_name(name))
753 		return (0);
754 
755 	if (stat(name, &buf)) {
756 		mail(name, BADSTAT, ERR_UNIXERR);
757 		cron_unlink(name);
758 		return (0);
759 	}
760 	if (!S_ISREG(buf.st_mode)) {
761 		mail(name, BADTYPE, ERR_NOTREG);
762 		cron_unlink(name);
763 		return (0);
764 	}
765 	if (type == ATEVENT) {
766 		if (!(buf.st_mode & ISUID)) {
767 			cron_unlink(name);
768 			return (0);
769 		}
770 	}
771 	return (1);
772 }
773 
774 struct usr *
775 create_ulist(char *name, int type)
776 {
777 	struct usr	*u;
778 
779 	u = xmalloc(sizeof (struct usr));
780 	u->name = xmalloc(strlen(name) + 1);
781 	(void) strcpy(u->name, name);
782 	u->home = NULL;
783 	if (type == CRONEVENT) {
784 		u->ctexists = TRUE;
785 		u->ctid = ecid++;
786 	} else {
787 		u->ctexists = FALSE;
788 		u->ctid = 0;
789 	}
790 	u->ctevents = NULL;
791 	u->atevents = NULL;
792 	u->aruncnt = 0;
793 	u->cruncnt = 0;
794 	u->nextusr = uhead;
795 	uhead = u;
796 	return (u);
797 }
798 
799 void
800 init_cronevent(char *name, int first)
801 {
802 	struct usr	*u;
803 
804 	if (first) {
805 		u = create_ulist(name, CRONEVENT);
806 		readcron(u, 0);
807 	} else {
808 		if ((u = find_usr(name)) == NULL) {
809 			u = create_ulist(name, CRONEVENT);
810 			readcron(u, 0);
811 		} else {
812 			u->ctexists = TRUE;
813 			rm_ctevents(u);
814 			el_remove(u->ctid, 0);
815 			readcron(u, 0);
816 		}
817 	}
818 }
819 
820 void
821 init_atevent(char *name, time_t tim, int jobtype, int first)
822 {
823 	struct usr	*u;
824 
825 	if (first) {
826 		u = create_ulist(name, ATEVENT);
827 		add_atevent(u, name, tim, jobtype);
828 	} else {
829 		if ((u = find_usr(name)) == NULL) {
830 			u = create_ulist(name, ATEVENT);
831 			add_atevent(u, name, tim, jobtype);
832 		} else {
833 			update_atevent(u, name, tim, jobtype);
834 		}
835 	}
836 }
837 
838 static void
839 mod_ctab(char *name, time_t reftime)
840 {
841 	struct	passwd	*pw;
842 	struct	stat	buf;
843 	struct	usr	*u;
844 	char	namebuf[PATH_MAX];
845 	char	*pname;
846 
847 	/* skip over ancillary file names */
848 	if (audit_cron_is_anc_name(name))
849 		return;
850 
851 	if ((pw = getpwnam(name)) == NULL) {
852 		msg("No such user as %s - cron entries not created", name);
853 		return;
854 	}
855 	if (cwd != CRON) {
856 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
857 		    CRONDIR, name) >= sizeof (namebuf)) {
858 			msg("Too long path name %s - cron entries not created",
859 			    namebuf);
860 			return;
861 		}
862 		pname = namebuf;
863 	} else {
864 		pname = name;
865 	}
866 	/*
867 	 * a warning message is given by the crontab command so there is
868 	 * no need to give one here......  use this code if you only want
869 	 * users with a login shell of /usr/bin/sh to use cron
870 	 */
871 #ifdef BOURNESHELLONLY
872 	if ((strcmp(pw->pw_shell, "") != 0) &&
873 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
874 		mail(name, BADSHELL, ERR_CANTEXECCRON);
875 		cron_unlink(pname);
876 		return;
877 	}
878 #endif
879 	if (stat(pname, &buf)) {
880 		mail(name, BADSTAT, ERR_UNIXERR);
881 		cron_unlink(pname);
882 		return;
883 	}
884 	if (!S_ISREG(buf.st_mode)) {
885 		mail(name, BADTYPE, ERR_CRONTABENT);
886 		return;
887 	}
888 	if ((u = find_usr(name)) == NULL) {
889 #ifdef DEBUG
890 		(void) fprintf(stderr, "new user (%s) with a crontab\n", name);
891 #endif
892 		u = create_ulist(name, CRONEVENT);
893 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
894 		(void) strcpy(u->home, pw->pw_dir);
895 		u->uid = pw->pw_uid;
896 		u->gid = pw->pw_gid;
897 		readcron(u, reftime);
898 	} else {
899 		u->uid = pw->pw_uid;
900 		u->gid = pw->pw_gid;
901 		if (u->home != NULL) {
902 			if (strcmp(u->home, pw->pw_dir) != 0) {
903 				free(u->home);
904 				u->home = xmalloc(strlen(pw->pw_dir) + 1);
905 				(void) strcpy(u->home, pw->pw_dir);
906 			}
907 		} else {
908 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
909 			(void) strcpy(u->home, pw->pw_dir);
910 		}
911 		u->ctexists = TRUE;
912 		if (u->ctid == 0) {
913 #ifdef DEBUG
914 			(void) fprintf(stderr, "%s now has a crontab\n",
915 			    u->name);
916 #endif
917 			/* user didnt have a crontab last time */
918 			u->ctid = ecid++;
919 			u->ctevents = NULL;
920 			readcron(u, reftime);
921 			return;
922 		}
923 #ifdef DEBUG
924 		(void) fprintf(stderr, "%s has revised his crontab\n", u->name);
925 #endif
926 		rm_ctevents(u);
927 		el_remove(u->ctid, 0);
928 		readcron(u, reftime);
929 	}
930 }
931 
932 /* ARGSUSED */
933 static void
934 mod_atjob(char *name, time_t reftime)
935 {
936 	char	*ptr;
937 	time_t	tim;
938 	struct	passwd	*pw;
939 	struct	stat	buf;
940 	struct	usr	*u;
941 	char	namebuf[PATH_MAX];
942 	char	*pname;
943 	int	jobtype;
944 
945 	ptr = name;
946 	if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
947 		return;
948 	ptr++;
949 	if (!isalpha(*ptr))
950 		return;
951 	jobtype = *ptr - 'a';
952 
953 	/* check for audit ancillary file */
954 	if (audit_cron_is_anc_name(name))
955 		return;
956 
957 	if (cwd != AT) {
958 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name)
959 		    >= sizeof (namebuf)) {
960 			return;
961 		}
962 		pname = namebuf;
963 	} else {
964 		pname = name;
965 	}
966 	if (stat(pname, &buf) || jobtype >= NQUEUE) {
967 		cron_unlink(pname);
968 		return;
969 	}
970 	if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) {
971 		cron_unlink(pname);
972 		return;
973 	}
974 	if ((pw = getpwuid(buf.st_uid)) == NULL) {
975 		cron_unlink(pname);
976 		return;
977 	}
978 	/*
979 	 * a warning message is given by the at command so there is no
980 	 * need to give one here......use this code if you only want
981 	 * users with a login shell of /usr/bin/sh to use cron
982 	 */
983 #ifdef BOURNESHELLONLY
984 	if ((strcmp(pw->pw_shell, "") != 0) &&
985 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
986 		mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT);
987 		cron_unlink(pname);
988 		return;
989 	}
990 #endif
991 	if ((u = find_usr(pw->pw_name)) == NULL) {
992 #ifdef DEBUG
993 		(void) fprintf(stderr, "new user (%s) with an at job = %s\n",
994 		    pw->pw_name, name);
995 #endif
996 		u = create_ulist(pw->pw_name, ATEVENT);
997 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
998 		(void) strcpy(u->home, pw->pw_dir);
999 		u->uid = pw->pw_uid;
1000 		u->gid = pw->pw_gid;
1001 		add_atevent(u, name, tim, jobtype);
1002 	} else {
1003 		u->uid = pw->pw_uid;
1004 		u->gid = pw->pw_gid;
1005 		free(u->home);
1006 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
1007 		(void) strcpy(u->home, pw->pw_dir);
1008 		update_atevent(u, name, tim, jobtype);
1009 	}
1010 }
1011 
1012 static void
1013 add_atevent(struct usr *u, char *job, time_t tim, int jobtype)
1014 {
1015 	struct event *e;
1016 
1017 	e = xmalloc(sizeof (struct event));
1018 	e->etype = jobtype;
1019 	e->cmd = xmalloc(strlen(job) + 1);
1020 	(void) strcpy(e->cmd, job);
1021 	e->u = u;
1022 	e->link = u->atevents;
1023 	u->atevents = e;
1024 	e->of.at.exists = TRUE;
1025 	e->of.at.eventid = ecid++;
1026 	if (tim < init_time)	/* old job */
1027 		e->time = init_time;
1028 	else
1029 		e->time = tim;
1030 #ifdef DEBUG
1031 	(void) fprintf(stderr, "add_atevent: user=%s, job=%s, time=%ld\n",
1032 	    u->name, e->cmd, e->time);
1033 #endif
1034 	el_add(e, e->time, e->of.at.eventid);
1035 }
1036 
1037 void
1038 update_atevent(struct usr *u, char *name, time_t tim, int jobtype)
1039 {
1040 	struct event *e;
1041 
1042 	e = u->atevents;
1043 	while (e != NULL) {
1044 		if (strcmp(e->cmd, name) == 0) {
1045 			e->of.at.exists = TRUE;
1046 			break;
1047 		} else {
1048 			e = e->link;
1049 		}
1050 	}
1051 	if (e == NULL) {
1052 #ifdef DEBUG
1053 		(void) fprintf(stderr, "%s has a new at job = %s\n",
1054 		    u->name, name);
1055 #endif
1056 			add_atevent(u, name, tim, jobtype);
1057 	}
1058 }
1059 
1060 static char line[CTLINESIZE];	/* holds a line from a crontab file */
1061 static int cursor;		/* cursor for the above line */
1062 
1063 static void
1064 readcron(struct usr *u, time_t reftime)
1065 {
1066 	/*
1067 	 * readcron reads in a crontab file for a user (u). The list of
1068 	 * events for user u is built, and u->events is made to point to
1069 	 * this list. Each event is also entered into the main event
1070 	 * list.
1071 	 */
1072 	FILE *cf;	/* cf will be a user's crontab file */
1073 	struct event *e;
1074 	int start;
1075 	unsigned int i;
1076 	char namebuf[PATH_MAX];
1077 	char *pname;
1078 	int lineno = 0;
1079 
1080 	/* read the crontab file */
1081 	cte_init();		/* Init error handling */
1082 	if (cwd != CRON) {
1083 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
1084 		    CRONDIR, u->name) >= sizeof (namebuf)) {
1085 			return;
1086 		}
1087 		pname = namebuf;
1088 	} else {
1089 		pname = u->name;
1090 	}
1091 	if ((cf = fopen(pname, "r")) == NULL) {
1092 		mail(u->name, NOREAD, ERR_UNIXERR);
1093 		return;
1094 	}
1095 	while (fgets(line, CTLINESIZE, cf) != NULL) {
1096 		/* process a line of a crontab file */
1097 		lineno++;
1098 		if (cte_istoomany())
1099 			break;
1100 		cursor = 0;
1101 		while (line[cursor] == ' ' || line[cursor] == '\t')
1102 			cursor++;
1103 		if (line[cursor] == '#' || line[cursor] == '\n')
1104 			continue;
1105 		e = xmalloc(sizeof (struct event));
1106 		e->etype = CRONEVENT;
1107 		if (!(((e->of.ct.minute = next_field(0, 59)) != NULL) &&
1108 		    ((e->of.ct.hour = next_field(0, 23)) != NULL) &&
1109 		    ((e->of.ct.daymon = next_field(1, 31)) != NULL) &&
1110 		    ((e->of.ct.month = next_field(1, 12)) != NULL) &&
1111 		    ((e->of.ct.dayweek = next_field(0, 6)) != NULL))) {
1112 			free(e);
1113 			cte_add(lineno, line);
1114 			continue;
1115 		}
1116 		while (line[cursor] == ' ' || line[cursor] == '\t')
1117 			cursor++;
1118 		if (line[cursor] == '\n' || line[cursor] == '\0')
1119 			continue;
1120 		/* get the command to execute	*/
1121 		start = cursor;
1122 again:
1123 		while ((line[cursor] != '%') &&
1124 		    (line[cursor] != '\n') &&
1125 		    (line[cursor] != '\0') &&
1126 		    (line[cursor] != '\\'))
1127 			cursor++;
1128 		if (line[cursor] == '\\') {
1129 			cursor += 2;
1130 			goto again;
1131 		}
1132 		e->cmd = xmalloc(cursor-start + 1);
1133 		(void) strncpy(e->cmd, line + start, cursor-start);
1134 		e->cmd[cursor-start] = '\0';
1135 		/* see if there is any standard input	*/
1136 		if (line[cursor] == '%') {
1137 			e->of.ct.input = xmalloc(strlen(line)-cursor + 1);
1138 			(void) strcpy(e->of.ct.input, line + cursor + 1);
1139 			for (i = 0; i < strlen(e->of.ct.input); i++) {
1140 				if (e->of.ct.input[i] == '%')
1141 					e->of.ct.input[i] = '\n';
1142 			}
1143 		} else {
1144 			e->of.ct.input = NULL;
1145 		}
1146 		/* have the event point to it's owner	*/
1147 		e->u = u;
1148 		/* insert this event at the front of this user's event list */
1149 		e->link = u->ctevents;
1150 		u->ctevents = e;
1151 		/* set the time for the first occurance of this event	*/
1152 		e->time = next_time(e, reftime);
1153 		/* finally, add this event to the main event list	*/
1154 		el_add(e, e->time, u->ctid);
1155 		cte_valid();
1156 #ifdef DEBUG
1157 		cftime(timebuf, "%C", &e->time);
1158 		(void) fprintf(stderr, "inserting cron event %s at %ld (%s)\n",
1159 		    e->cmd, e->time, timebuf);
1160 #endif
1161 	}
1162 	cte_sendmail(u->name);	/* mail errors if any to user */
1163 	(void) fclose(cf);
1164 }
1165 
1166 /*
1167  * Below are the functions for handling of errors in crontabs. Concept is to
1168  * collect faulty lines and send one email at the end of the crontab
1169  * evaluation. If there are erroneous lines only ((cte_nvalid == 0), evaluation
1170  * of crontab is aborted. Otherwise reading of crontab is continued to the end
1171  * of the file but no further error logging appears.
1172  */
1173 static void
1174 cte_init()
1175 {
1176 	if (cte_text == NULL)
1177 		cte_text = xmalloc(MAILBUFLEN);
1178 	(void) strlcpy(cte_text, cte_intro, MAILBUFLEN);
1179 	cte_lp = cte_text + sizeof (cte_intro) - 1;
1180 	cte_free = MAILBINITFREE;
1181 	cte_nvalid = 0;
1182 }
1183 
1184 static void
1185 cte_add(int lineno, char *ctline)
1186 {
1187 	int len;
1188 	char *p;
1189 
1190 	if (cte_free >= LINELIMIT) {
1191 		(void) sprintf(cte_lp, "%4d: ", lineno);
1192 		(void) strlcat(cte_lp, ctline, LINELIMIT - 1);
1193 		len = strlen(cte_lp);
1194 		if (cte_lp[len - 1] != '\n') {
1195 			cte_lp[len++] = '\n';
1196 			cte_lp[len] = '\0';
1197 		}
1198 		for (p = cte_lp; *p; p++) {
1199 			if (isprint(*p) || *p == '\n' || *p == '\t')
1200 				continue;
1201 			*p = '.';
1202 		}
1203 		cte_lp += len;
1204 		cte_free -= len;
1205 		if (cte_free < LINELIMIT) {
1206 			size_t buflen = MAILBUFLEN - (cte_lp - cte_text);
1207 			(void) strlcpy(cte_lp, cte_trail1, buflen);
1208 			if (cte_nvalid == 0)
1209 				(void) strlcat(cte_lp, cte_trail2, buflen);
1210 		}
1211 	}
1212 }
1213 
1214 static void
1215 cte_valid()
1216 {
1217 	cte_nvalid++;
1218 }
1219 
1220 static int
1221 cte_istoomany()
1222 {
1223 	/*
1224 	 * Return TRUE only if all lines are faulty. So evaluation of
1225 	 * a crontab is not aborted if at least one valid line was found.
1226 	 */
1227 	return (cte_nvalid == 0 && cte_free < LINELIMIT);
1228 }
1229 
1230 static void
1231 cte_sendmail(char *username)
1232 {
1233 	if (cte_free < MAILBINITFREE)
1234 		mail(username, cte_text, ERR_CRONTABENT);
1235 }
1236 
1237 /*
1238  * Send mail with error message to a user
1239  */
1240 static void
1241 mail(char *usrname, char *mesg, int format)
1242 {
1243 	/* mail mails a user a message.	*/
1244 	FILE *pipe;
1245 	char *temp;
1246 	struct passwd	*ruser_ids;
1247 	pid_t fork_val;
1248 	int saveerrno = errno;
1249 	struct utsname	name;
1250 
1251 #ifdef TESTING
1252 	return;
1253 #endif
1254 	(void) uname(&name);
1255 	if ((fork_val = fork()) == (pid_t)-1) {
1256 		msg("cron cannot fork\n");
1257 		return;
1258 	}
1259 	if (fork_val == 0) {
1260 		child_sigreset();
1261 		contract_clear_template();
1262 		if ((ruser_ids = getpwnam(usrname)) == NULL)
1263 			exit(0);
1264 		(void) setuid(ruser_ids->pw_uid);
1265 		temp = xmalloc(strlen(MAIL) + strlen(usrname) + 2);
1266 		(void) sprintf(temp, "%s %s", MAIL, usrname);
1267 		pipe = popen(temp, "w");
1268 		if (pipe != NULL) {
1269 			(void) fprintf(pipe, "To: %s\n", usrname);
1270 			switch (format) {
1271 			case ERR_CRONTABENT:
1272 				(void) fprintf(pipe, CRONTABERR);
1273 				(void) fprintf(pipe, "Your \"crontab\" on %s\n",
1274 				    name.nodename);
1275 				(void) fprintf(pipe, mesg);
1276 				(void) fprintf(pipe,
1277 				    "\nEntries or crontab have been ignored\n");
1278 				break;
1279 			case ERR_UNIXERR:
1280 				(void) fprintf(pipe, "Subject: %s\n\n", mesg);
1281 				(void) fprintf(pipe,
1282 				    "The error on %s was \"%s\"\n",
1283 				    name.nodename, errmsg(saveerrno));
1284 				break;
1285 
1286 			case ERR_CANTEXECCRON:
1287 				(void) fprintf(pipe,
1288 				"Subject: Couldn't run your \"cron\" job\n\n");
1289 				(void) fprintf(pipe,
1290 				    "Your \"cron\" job on %s ", name.nodename);
1291 				(void) fprintf(pipe, "couldn't be run\n");
1292 				(void) fprintf(pipe, "%s\n", mesg);
1293 				(void) fprintf(pipe,
1294 				"The error was \"%s\"\n", errmsg(saveerrno));
1295 				break;
1296 
1297 			case ERR_CANTEXECAT:
1298 				(void) fprintf(pipe,
1299 				"Subject: Couldn't run your \"at\" job\n\n");
1300 				(void) fprintf(pipe, "Your \"at\" job on %s ",
1301 				    name.nodename);
1302 				(void) fprintf(pipe, "couldn't be run\n");
1303 				(void) fprintf(pipe, "%s\n", mesg);
1304 				(void) fprintf(pipe,
1305 				"The error was \"%s\"\n", errmsg(saveerrno));
1306 				break;
1307 
1308 			default:
1309 				break;
1310 			}
1311 			(void) pclose(pipe);
1312 		}
1313 		free(temp);
1314 		exit(0);
1315 	}
1316 
1317 	contract_abandon_latest(fork_val);
1318 
1319 	if (cron_pid == getpid()) {
1320 		miscpid_insert(fork_val);
1321 	}
1322 }
1323 
1324 static char *
1325 next_field(int lower, int upper)
1326 {
1327 	/*
1328 	 * next_field returns a pointer to a string which holds the next
1329 	 * field of a line of a crontab file.
1330 	 *   if (numbers in this field are out of range (lower..upper),
1331 	 *	or there is a syntax error) then
1332 	 *	NULL is returned, and a mail message is sent to the
1333 	 *	user telling him which line the error was in.
1334 	 */
1335 
1336 	char *s;
1337 	int num, num2, start;
1338 
1339 	while ((line[cursor] == ' ') || (line[cursor] == '\t'))
1340 		cursor++;
1341 	start = cursor;
1342 	if (line[cursor] == '\0') {
1343 		return (NULL);
1344 	}
1345 	if (line[cursor] == '*') {
1346 		cursor++;
1347 		if ((line[cursor] != ' ') && (line[cursor] != '\t'))
1348 			return (NULL);
1349 		s = xmalloc(2);
1350 		(void) strcpy(s, "*");
1351 		return (s);
1352 	}
1353 	for (;;) {
1354 		if (!isdigit(line[cursor]))
1355 			return (NULL);
1356 		num = 0;
1357 		do {
1358 			num = num*10 + (line[cursor]-'0');
1359 		} while (isdigit(line[++cursor]));
1360 		if ((num < lower) || (num > upper))
1361 			return (NULL);
1362 		if (line[cursor] == '-') {
1363 			if (!isdigit(line[++cursor]))
1364 				return (NULL);
1365 			num2 = 0;
1366 			do {
1367 				num2 = num2*10 + (line[cursor]-'0');
1368 			} while (isdigit(line[++cursor]));
1369 			if ((num2 < lower) || (num2 > upper))
1370 				return (NULL);
1371 		}
1372 		if ((line[cursor] == ' ') || (line[cursor] == '\t'))
1373 			break;
1374 		if (line[cursor] == '\0')
1375 			return (NULL);
1376 		if (line[cursor++] != ',')
1377 			return (NULL);
1378 	}
1379 	s = xmalloc(cursor-start + 1);
1380 	(void) strncpy(s, line + start, cursor-start);
1381 	s[cursor-start] = '\0';
1382 	return (s);
1383 }
1384 
1385 #define	tm_cmp(t1, t2) (\
1386 	(t1)->tm_year == (t2)->tm_year && \
1387 	(t1)->tm_mon == (t2)->tm_mon && \
1388 	(t1)->tm_mday == (t2)->tm_mday && \
1389 	(t1)->tm_hour == (t2)->tm_hour && \
1390 	(t1)->tm_min == (t2)->tm_min)
1391 
1392 #define	tm_setup(tp, yr, mon, dy, hr, min, dst) \
1393 	(tp)->tm_year = yr; \
1394 	(tp)->tm_mon = mon; \
1395 	(tp)->tm_mday = dy; \
1396 	(tp)->tm_hour = hr; \
1397 	(tp)->tm_min = min; \
1398 	(tp)->tm_isdst = dst; \
1399 	(tp)->tm_sec = 0; \
1400 	(tp)->tm_wday = 0; \
1401 	(tp)->tm_yday = 0;
1402 
1403 /*
1404  * modification for bugid 1104537. the second argument to next_time is
1405  * now the value of time(2) to be used. if this is 0, then use the
1406  * current time. otherwise, the second argument is the time from which to
1407  * calculate things. this is useful to correct situations where you've
1408  * gone backwards in time (I.e. the system's internal clock is correcting
1409  * itself backwards).
1410  */
1411 
1412 static time_t
1413 next_time(struct event *e, time_t tflag)
1414 {
1415 	/*
1416 	 * returns the integer time for the next occurance of event e.
1417 	 * the following fields have ranges as indicated:
1418 	 * PRGM  | min	hour	day of month	mon	day of week
1419 	 * ------|-------------------------------------------------------
1420 	 * cron  | 0-59	0-23	    1-31	1-12	0-6 (0=sunday)
1421 	 * time  | 0-59	0-23	    1-31	0-11	0-6 (0=sunday)
1422 	 * NOTE: this routine is hard to understand.
1423 	 */
1424 
1425 	struct tm *tm, ref_tm, tmp, tmp1, tmp2;
1426 	int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days,
1427 	    d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd,
1428 	    today;
1429 
1430 	time_t t, ref_t, t1, t2, zone_start;
1431 	int fallback;
1432 	extern int days_btwn(int, int, int, int, int, int);
1433 
1434 	if (tflag == 0) {
1435 		t = time(NULL);	/* original way of doing things	*/
1436 	} else {
1437 		t =  tflag;
1438 	}
1439 
1440 	tm = &ref_tm;	/* use a local variable and call localtime_r() */
1441 	ref_t = t;	/* keep a copy of the reference time */
1442 
1443 recalc:
1444 	fallback = 0;
1445 
1446 	(void) localtime_r(&t, tm);
1447 
1448 	if (daylight) {
1449 		tmp = *tm;
1450 		tmp.tm_isdst = (tm->tm_isdst > 0 ? 0 : 1);
1451 		t1 = xmktime(&tmp);
1452 		/*
1453 		 * see if we will have timezone switch over, and clock will
1454 		 * fall back. zone_start will hold the time when it happens
1455 		 * (ie time of PST -> PDT switch over).
1456 		 */
1457 		if (tm->tm_isdst != tmp.tm_isdst &&
1458 		    (t1 - t) == (timezone - altzone) &&
1459 		    tm_cmp(tm, &tmp)) {
1460 			zone_start = get_switching_time(tmp.tm_isdst, t);
1461 			fallback = 1;
1462 		}
1463 	}
1464 
1465 	tm_mon = next_ge(tm->tm_mon + 1, e->of.ct.month) - 1;	/* 0-11 */
1466 	tm_mday = next_ge(tm->tm_mday, e->of.ct.daymon);	/* 1-31 */
1467 	tm_wday = next_ge(tm->tm_wday, e->of.ct.dayweek);	/* 0-6	*/
1468 	today = TRUE;
1469 	if ((strcmp(e->of.ct.daymon, "*") == 0 && tm->tm_wday != tm_wday) ||
1470 	    (strcmp(e->of.ct.dayweek, "*") == 0 && tm->tm_mday != tm_mday) ||
1471 	    (tm->tm_mday != tm_mday && tm->tm_wday != tm_wday) ||
1472 	    (tm->tm_mon != tm_mon)) {
1473 		today = FALSE;
1474 	}
1475 	m = tm->tm_min + (t == ref_t ? 1 : 0);
1476 	if ((tm->tm_hour + 1) <= next_ge(tm->tm_hour, e->of.ct.hour)) {
1477 		m = 0;
1478 	}
1479 	min = next_ge(m%60, e->of.ct.minute);
1480 	carry = (min < m) ? 1 : 0;
1481 	h = tm->tm_hour + carry;
1482 	hr = next_ge(h%24, e->of.ct.hour);
1483 	carry = (hr < h) ? 1 : 0;
1484 
1485 	if (carry == 0 && today) {
1486 		/* this event must occur today */
1487 		tm_setup(&tmp, tm->tm_year, tm->tm_mon, tm->tm_mday,
1488 		    hr, min, tm->tm_isdst);
1489 		tmp1 = tmp;
1490 		if ((t1 = xmktime(&tmp1)) == (time_t)-1) {
1491 			return (0);
1492 		}
1493 		if (daylight && tmp.tm_isdst != tmp1.tm_isdst) {
1494 			/* In case we are falling back */
1495 			if (fallback) {
1496 				/* we may need to run the job once more. */
1497 				t = zone_start;
1498 				goto recalc;
1499 			}
1500 
1501 			/*
1502 			 * In case we are not in falling back period,
1503 			 * calculate the time assuming the DST. If the
1504 			 * date/time is not altered by mktime, it is the
1505 			 * time to execute the job.
1506 			 */
1507 			tmp2 = tmp;
1508 			tmp2.tm_isdst = tmp1.tm_isdst;
1509 			if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
1510 				return (0);
1511 			}
1512 			if (tmp1.tm_isdst == tmp2.tm_isdst &&
1513 			    tm_cmp(&tmp, &tmp2)) {
1514 				/*
1515 				 * We got a valid time.
1516 				 */
1517 				return (t1);
1518 			} else {
1519 				/*
1520 				 * If the date does not match even if
1521 				 * we assume the alternate timezone, then
1522 				 * it must be the invalid time. eg
1523 				 * 2am while switching 1:59am to 3am.
1524 				 * t1 should point the time before the
1525 				 * switching over as we've calculate the
1526 				 * time with assuming alternate zone.
1527 				 */
1528 				if (tmp1.tm_isdst != tmp2.tm_isdst) {
1529 					t = get_switching_time(tmp1.tm_isdst,
1530 					    t1);
1531 				} else {
1532 					/* does this really happen? */
1533 					t = get_switching_time(tmp1.tm_isdst,
1534 					    t1 - abs(timezone - altzone));
1535 				}
1536 				if (t == (time_t)-1)
1537 					return (0);
1538 			}
1539 			goto recalc;
1540 		}
1541 		if (tm_cmp(&tmp, &tmp1)) {
1542 			/* got valid time */
1543 			return (t1);
1544 		} else {
1545 			/*
1546 			 * This should never happen, but just in
1547 			 * case, we fall back to the old code.
1548 			 */
1549 			if (tm->tm_min > min) {
1550 				t += (time_t)(hr-tm->tm_hour-1) * HOUR +
1551 				    (time_t)(60-tm->tm_min + min) * MINUTE;
1552 			} else {
1553 				t += (time_t)(hr-tm->tm_hour) * HOUR +
1554 				    (time_t)(min-tm->tm_min) * MINUTE;
1555 			}
1556 			t1 = t;
1557 			t -= (time_t)tm->tm_sec;
1558 			(void) localtime_r(&t, &tmp);
1559 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
1560 				t -= (timezone - altzone);
1561 			return ((t <= ref_t) ? t1 : t);
1562 		}
1563 	}
1564 
1565 	/*
1566 	 * Job won't run today, however if we have a switch over within
1567 	 * one hour and we will have one hour time drifting back in this
1568 	 * period, we may need to run the job one more time if the job was
1569 	 * set to run on this hour of clock.
1570 	 */
1571 	if (fallback) {
1572 		t = zone_start;
1573 		goto recalc;
1574 	}
1575 
1576 	min = next_ge(0, e->of.ct.minute);
1577 	hr = next_ge(0, e->of.ct.hour);
1578 
1579 	/*
1580 	 * calculate the date of the next occurance of this event, which
1581 	 * will be on a different day than the current
1582 	 */
1583 
1584 	/* check monthly day specification	*/
1585 	d1 = tm->tm_mday + 1;
1586 	day1 = next_ge((d1-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1,
1587 	    e->of.ct.daymon);
1588 	carry1 = (day1 < d1) ? 1 : 0;
1589 
1590 	/* check weekly day specification	*/
1591 	d2 = tm->tm_wday + 1;
1592 	wday = next_ge(d2%7, e->of.ct.dayweek);
1593 	if (wday < d2)
1594 		daysahead = 7 - d2 + wday;
1595 	else
1596 		daysahead = wday - d2;
1597 	day2 = (d1 + daysahead-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1;
1598 	carry2 = (day2 < d1) ? 1 : 0;
1599 
1600 	/*
1601 	 *	based on their respective specifications, day1, and day2 give
1602 	 *	the day of the month for the next occurance of this event.
1603 	 */
1604 	if ((strcmp(e->of.ct.daymon, "*") == 0) &&
1605 	    (strcmp(e->of.ct.dayweek, "*") != 0)) {
1606 		day1 = day2;
1607 		carry1 = carry2;
1608 	}
1609 	if ((strcmp(e->of.ct.daymon, "*") != 0) &&
1610 	    (strcmp(e->of.ct.dayweek, "*") == 0)) {
1611 		day2 = day1;
1612 		carry2 = carry1;
1613 	}
1614 
1615 	yr = tm->tm_year;
1616 	if ((carry1 && carry2) || (tm->tm_mon != tm_mon)) {
1617 		/* event does not occur in this month	*/
1618 		m = tm->tm_mon + 1;
1619 		mon = next_ge(m%12 + 1, e->of.ct.month) - 1;	/* 0..11 */
1620 		carry = (mon < m) ? 1 : 0;
1621 		yr += carry;
1622 		/* recompute day1 and day2	*/
1623 		day1 = next_ge(1, e->of.ct.daymon);
1624 		db = days_btwn(tm->tm_mon, tm->tm_mday, tm->tm_year, mon,
1625 		    1, yr) + 1;
1626 		wd = (tm->tm_wday + db)%7;
1627 		/* wd is the day of the week of the first of month mon	*/
1628 		wday = next_ge(wd, e->of.ct.dayweek);
1629 		if (wday < wd)
1630 			day2 = 1 + 7 - wd + wday;
1631 		else
1632 			day2 = 1 + wday - wd;
1633 		if ((strcmp(e->of.ct.daymon, "*") != 0) &&
1634 		    (strcmp(e->of.ct.dayweek, "*") == 0))
1635 			day2 = day1;
1636 		if ((strcmp(e->of.ct.daymon, "*") == 0) &&
1637 		    (strcmp(e->of.ct.dayweek, "*") != 0))
1638 			day1 = day2;
1639 		day = (day1 < day2) ? day1 : day2;
1640 	} else {			/* event occurs in this month	*/
1641 		mon = tm->tm_mon;
1642 		if (!carry1 && !carry2)
1643 			day = (day1 < day2) ? day1 : day2;
1644 		else if (!carry1)
1645 			day = day1;
1646 		else
1647 			day = day2;
1648 	}
1649 
1650 	/*
1651 	 * now that we have the min, hr, day, mon, yr of the next event,
1652 	 * figure out what time that turns out to be.
1653 	 */
1654 	tm_setup(&tmp, yr, mon, day, hr, min, -1);
1655 	tmp2 = tmp;
1656 	if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
1657 		return (0);
1658 	}
1659 	if (tm_cmp(&tmp, &tmp2)) {
1660 		/*
1661 		 * mktime returns clock for the current time zone. If the
1662 		 * target date was in fallback period, it needs to be adjusted
1663 		 * to the time comes first.
1664 		 * Suppose, we are at Jan and scheduling job at 1:30am10/26/03.
1665 		 * mktime returns the time in PST, but 1:30am in PDT comes
1666 		 * first. So reverse the tm_isdst, and see if we have such
1667 		 * time/date.
1668 		 */
1669 		if (daylight) {
1670 			int dst = tmp2.tm_isdst;
1671 
1672 			tmp2 = tmp;
1673 			tmp2.tm_isdst = (dst > 0 ? 0 : 1);
1674 			if ((t2 = xmktime(&tmp2)) == (time_t)-1) {
1675 				return (0);
1676 			}
1677 			if (tm_cmp(&tmp, &tmp2)) {
1678 				/*
1679 				 * same time/date found in the opposite zone.
1680 				 * check the clock to see which comes early.
1681 				 */
1682 				if (t2 > ref_t && t2 < t1) {
1683 					t1 = t2;
1684 				}
1685 			}
1686 		}
1687 		return (t1);
1688 	} else {
1689 		/*
1690 		 * mktime has set different time/date for the given date.
1691 		 * This means that the next job is scheduled to be run on the
1692 		 * invalid time. There are three possible invalid date/time.
1693 		 * 1. Non existing day of the month. such as April 31th.
1694 		 * 2. Feb 29th in the non-leap year.
1695 		 * 3. Time gap during the DST switch over.
1696 		 */
1697 		d1 = days_in_mon(mon, yr);
1698 		if ((mon != 1 && day > d1) || (mon == 1 && day > 29)) {
1699 			/*
1700 			 * see if we have got a specific date which
1701 			 * is invalid.
1702 			 */
1703 			if (strcmp(e->of.ct.dayweek, "*") == 0 &&
1704 			    mon == (next_ge((mon + 1)%12 + 1,
1705 			    e->of.ct.month) - 1) &&
1706 			    day <= next_ge(1, e->of.ct.daymon)) {
1707 				/* job never run */
1708 				return (0);
1709 			}
1710 			/*
1711 			 * Since the day has gone invalid, we need to go to
1712 			 * next month, and recalcuate the first occurrence.
1713 			 * eg the cron tab such as:
1714 			 * 0 0 1,15,31 1,2,3,4,5 * /usr/bin....
1715 			 * 2/31 is invalid, so the next job is 3/1.
1716 			 */
1717 			tmp2 = tmp;
1718 			tmp2.tm_min = 0;
1719 			tmp2.tm_hour = 0;
1720 			tmp2.tm_mday = 1; /* 1st day of the month */
1721 			if (mon == 11) {
1722 				tmp2.tm_mon = 0;
1723 				tmp2.tm_year = yr + 1;
1724 			} else {
1725 				tmp2.tm_mon = mon + 1;
1726 			}
1727 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
1728 				return (0);
1729 			}
1730 		} else if (mon == 1 && day > d1) {
1731 			/*
1732 			 * ie 29th in the non-leap year. Forwarding the
1733 			 * clock to Feb 29th 00:00 (March 1st), and recalculate
1734 			 * the next time.
1735 			 */
1736 			tmp2 = tmp;
1737 			tmp2.tm_min = 0;
1738 			tmp2.tm_hour = 0;
1739 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
1740 				return (0);
1741 			}
1742 		} else if (daylight) {
1743 			/*
1744 			 * Non existing time, eg 2am PST during summer time
1745 			 * switch.
1746 			 * We need to get the correct isdst which we are
1747 			 * swithing to, by adding time difference to make sure
1748 			 * that t2 is in the zone being switched.
1749 			 */
1750 			t2 = t1;
1751 			t2 += abs(timezone - altzone);
1752 			(void) localtime_r(&t2, &tmp2);
1753 			zone_start = get_switching_time(tmp2.tm_isdst,
1754 			    t1 - abs(timezone - altzone));
1755 			if (zone_start == (time_t)-1) {
1756 				return (0);
1757 			}
1758 			t = zone_start;
1759 		} else {
1760 			/*
1761 			 * This should never happen, but fall back to the
1762 			 * old code.
1763 			 */
1764 			days = days_btwn(tm->tm_mon,
1765 			    tm->tm_mday, tm->tm_year, mon, day, yr);
1766 			t += (time_t)(23-tm->tm_hour)*HOUR
1767 			    + (time_t)(60-tm->tm_min)*MINUTE
1768 			    + (time_t)hr*HOUR + (time_t)min*MINUTE
1769 			    + (time_t)days*DAY;
1770 			t1 = t;
1771 			t -= (time_t)tm->tm_sec;
1772 			(void) localtime_r(&t, &tmp);
1773 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
1774 				t -= (timezone - altzone);
1775 			return (t <= ref_t ? t1 : t);
1776 		}
1777 		goto recalc;
1778 	}
1779 	/*NOTREACHED*/
1780 }
1781 
1782 /*
1783  * This returns TOD in time_t that zone switch will happen, and this
1784  * will be called when clock fallback is about to happen.
1785  * (ie 30minutes before the time of PST -> PDT switch. 2:00 AM PST
1786  * will fall back to 1:00 PDT. So this function will be called only
1787  * for the time between 1:00 AM PST and 2:00 PST(1:00 PST)).
1788  * First goes through the common time differences to see if zone
1789  * switch happens at those minutes later. If not, check every minutes
1790  * until 6 hours ahead see if it happens(We might have 45minutes
1791  * fallback).
1792  */
1793 static time_t
1794 get_switching_time(int to_dst, time_t t_ref)
1795 {
1796 	time_t t, t1;
1797 	struct tm tmp, tmp1;
1798 	int hints[] = { 60, 120, 30, 90, 0}; /* minutes */
1799 	int i;
1800 
1801 	(void) localtime_r(&t_ref, &tmp);
1802 	tmp1 = tmp;
1803 	tmp1.tm_sec = 0;
1804 	tmp1.tm_min = 0;
1805 	if ((t = xmktime(&tmp1)) == (time_t)-1)
1806 		return ((time_t)-1);
1807 
1808 	/* fast path */
1809 	for (i = 0; hints[i] != 0; i++) {
1810 		t1 = t + hints[i] * 60;
1811 		(void) localtime_r(&t1, &tmp1);
1812 		if (tmp1.tm_isdst == to_dst) {
1813 			t1--;
1814 			(void) localtime_r(&t1, &tmp1);
1815 			if (tmp1.tm_isdst != to_dst) {
1816 				return (t1 + 1);
1817 			}
1818 		}
1819 	}
1820 
1821 	/* ugly, but don't know other than this. */
1822 	tmp1 = tmp;
1823 	tmp1.tm_sec = 0;
1824 	if ((t = xmktime(&tmp1)) == (time_t)-1)
1825 		return ((time_t)-1);
1826 	while (t < (t_ref + 6*60*60)) { /* 6 hours should be enough */
1827 		t += 60; /* at least one minute, I assume */
1828 		(void) localtime_r(&t, &tmp);
1829 		if (tmp.tm_isdst == to_dst)
1830 			return (t);
1831 	}
1832 	return ((time_t)-1);
1833 }
1834 
1835 static time_t
1836 xmktime(struct tm *tmp)
1837 {
1838 	time_t ret;
1839 
1840 	if ((ret = mktime(tmp)) == (time_t)-1) {
1841 		if (errno == EOVERFLOW) {
1842 			return ((time_t)-1);
1843 		}
1844 		crabort("internal error: mktime failed",
1845 		    REMOVE_FIFO|CONSOLE_MSG);
1846 	}
1847 	return (ret);
1848 }
1849 
1850 #define	DUMMY	100
1851 
1852 static int
1853 next_ge(int current, char *list)
1854 {
1855 	/*
1856 	 * list is a character field as in a crontab file;
1857 	 * for example: "40, 20, 50-10"
1858 	 * next_ge returns the next number in the list that is
1859 	 * greater than  or equal to current. if no numbers of list
1860 	 * are >= current, the smallest element of list is returned.
1861 	 * NOTE: current must be in the appropriate range.
1862 	 */
1863 
1864 	char *ptr;
1865 	int n, n2, min, min_gt;
1866 
1867 	if (strcmp(list, "*") == 0)
1868 		return (current);
1869 	ptr = list;
1870 	min = DUMMY;
1871 	min_gt = DUMMY;
1872 	for (;;) {
1873 		if ((n = (int)num(&ptr)) == current)
1874 			return (current);
1875 		if (n < min)
1876 			min = n;
1877 		if ((n > current) && (n < min_gt))
1878 			min_gt = n;
1879 		if (*ptr == '-') {
1880 			ptr++;
1881 			if ((n2 = (int)num(&ptr)) > n) {
1882 				if ((current > n) && (current <= n2))
1883 					return (current);
1884 			} else {	/* range that wraps around */
1885 				if (current > n)
1886 					return (current);
1887 				if (current <= n2)
1888 					return (current);
1889 			}
1890 		}
1891 		if (*ptr == '\0')
1892 			break;
1893 		ptr += 1;
1894 	}
1895 	if (min_gt != DUMMY)
1896 		return (min_gt);
1897 	else
1898 		return (min);
1899 }
1900 
1901 static void
1902 free_if_unused(struct usr *u)
1903 {
1904 	struct usr *cur, *prev;
1905 	/*
1906 	 *	To make sure a usr structure is idle we must check that
1907 	 *	there are no at jobs queued for the user; the user does
1908 	 *	not have a crontab, and also that there are no running at
1909 	 *	or cron jobs (since the runinfo structure also has a
1910 	 *	pointer to the usr structure).
1911 	 */
1912 	if (!u->ctexists && u->atevents == NULL &&
1913 	    u->cruncnt == 0 && u->aruncnt == 0) {
1914 #ifdef DEBUG
1915 		(void) fprintf(stderr, "%s removed from usr list\n", u->name);
1916 #endif
1917 		for (cur = uhead, prev = NULL;
1918 		    cur != u;
1919 		    prev = cur, cur = cur->nextusr) {
1920 			if (cur == NULL) {
1921 				return;
1922 			}
1923 		}
1924 
1925 		if (prev == NULL)
1926 			uhead = u->nextusr;
1927 		else
1928 			prev->nextusr = u->nextusr;
1929 		free(u->name);
1930 		free(u->home);
1931 		free(u);
1932 	}
1933 }
1934 
1935 static void
1936 del_atjob(char *name, char *usrname)
1937 {
1938 
1939 	struct	event	*e, *eprev;
1940 	struct	usr	*u;
1941 
1942 	if ((u = find_usr(usrname)) == NULL)
1943 		return;
1944 	e = u->atevents;
1945 	eprev = NULL;
1946 	while (e != NULL) {
1947 		if (strcmp(name, e->cmd) == 0) {
1948 			if (next_event == e)
1949 				next_event = NULL;
1950 			if (eprev == NULL)
1951 				u->atevents = e->link;
1952 			else
1953 				eprev->link = e->link;
1954 			el_remove(e->of.at.eventid, 1);
1955 			free(e->cmd);
1956 			free(e);
1957 			break;
1958 		} else {
1959 			eprev = e;
1960 			e = e->link;
1961 		}
1962 	}
1963 
1964 	free_if_unused(u);
1965 }
1966 
1967 static void
1968 del_ctab(char *name)
1969 {
1970 
1971 	struct	usr *u;
1972 
1973 	if ((u = find_usr(name)) == NULL)
1974 		return;
1975 	rm_ctevents(u);
1976 	el_remove(u->ctid, 0);
1977 	u->ctid = 0;
1978 	u->ctexists = 0;
1979 
1980 	free_if_unused(u);
1981 }
1982 
1983 static void
1984 rm_ctevents(struct usr *u)
1985 {
1986 	struct event *e2, *e3;
1987 
1988 	/*
1989 	 * see if the next event (to be run by cron) is a cronevent
1990 	 * owned by this user.
1991 	 */
1992 
1993 	if ((next_event != NULL) &&
1994 	    (next_event->etype == CRONEVENT) &&
1995 	    (next_event->u == u)) {
1996 		next_event = NULL;
1997 	}
1998 	e2 = u->ctevents;
1999 	while (e2 != NULL) {
2000 		free(e2->cmd);
2001 		free(e2->of.ct.minute);
2002 		free(e2->of.ct.hour);
2003 		free(e2->of.ct.daymon);
2004 		free(e2->of.ct.month);
2005 		free(e2->of.ct.dayweek);
2006 		if (e2->of.ct.input != NULL)
2007 			free(e2->of.ct.input);
2008 		e3 = e2->link;
2009 		free(e2);
2010 		e2 = e3;
2011 	}
2012 	u->ctevents = NULL;
2013 }
2014 
2015 
2016 static struct usr *
2017 find_usr(char *uname)
2018 {
2019 	struct usr *u;
2020 
2021 	u = uhead;
2022 	while (u != NULL) {
2023 		if (strcmp(u->name, uname) == 0)
2024 			return (u);
2025 		u = u->nextusr;
2026 	}
2027 	return (NULL);
2028 }
2029 
2030 /*
2031  * Execute cron command or at/batch job.
2032  * If ever a premature return is added to this function pay attention to
2033  * free at_cmdfile and outfile plus jobname buffers of the runinfo structure.
2034  */
2035 static int
2036 ex(struct event *e)
2037 {
2038 	int r;
2039 	int fd;
2040 	pid_t rfork;
2041 	FILE *atcmdfp;
2042 	char mailvar[4];
2043 	char *at_cmdfile = NULL;
2044 	struct stat buf;
2045 	struct queue *qp;
2046 	struct runinfo *rp;
2047 	struct project proj, *pproj = NULL;
2048 	char mybuf[PROJECT_BUFSZ];
2049 	char mybuf2[PROJECT_BUFSZ];
2050 	char *tmpfile;
2051 	FILE *fptr;
2052 	time_t dhltime;
2053 	projid_t projid;
2054 	int projflag = 0;
2055 
2056 	qp = &qt[e->etype];	/* set pointer to queue defs */
2057 	if (qp->nrun >= qp->njob) {
2058 		msg("%c queue max run limit reached", e->etype + 'a');
2059 		resched(qp->nwait);
2060 		return (0);
2061 	}
2062 	rp = rinfo_get(0); /* allocating a new runinfo struct */
2063 
2064 #ifdef ATLIMIT
2065 	if ((e->u)->uid != 0 && (e->u)->aruncnt >= ATLIMIT) {
2066 		msg("ATLIMIT (%d) reached for uid %d",
2067 		    ATLIMIT, (e->u)->uid);
2068 		rinfo_free(rp);
2069 		resched(qp->nwait);
2070 		return (0);
2071 	}
2072 #endif
2073 #ifdef CRONLIMIT
2074 	if ((e->u)->uid != 0 && (e->u)->cruncnt >= CRONLIMIT) {
2075 		msg("CRONLIMIT (%d) reached for uid %d",
2076 		    CRONLIMIT, (e->u)->uid);
2077 		rinfo_free(rp);
2078 		resched(qp->nwait);
2079 		return (0);
2080 	}
2081 #endif
2082 	if ((e->u)->uid == 0) {	/* set default path */
2083 		/* path settable in defaults file */
2084 		envinit[2] = supath;
2085 	} else {
2086 		envinit[2] = path;
2087 	}
2088 
2089 	/*
2090 	 * the tempnam() function uses malloc(3C) to allocate space for the
2091 	 * constructed file name, and returns a pointer to this area, which
2092 	 * is assigned to rp->outfile. Here rp->outfile is not overwritten.
2093 	 */
2094 
2095 	rp->outfile = tempnam(TMPDIR, PFX);
2096 	rp->jobtype = e->etype;
2097 	if (e->etype == CRONEVENT) {
2098 		rp->jobname = xmalloc(strlen(e->cmd) + 1);
2099 		(void) strcpy(rp->jobname, e->cmd);
2100 		/* "cron" jobs only produce mail if there's output */
2101 		rp->mailwhendone = 0;
2102 	} else {
2103 		at_cmdfile = xmalloc(strlen(ATDIR) + strlen(e->cmd) + 2);
2104 		(void) sprintf(at_cmdfile, "%s/%s", ATDIR, e->cmd);
2105 		if ((atcmdfp = fopen(at_cmdfile, "r")) == NULL) {
2106 			if (errno == ENAMETOOLONG) {
2107 				if (chdir(ATDIR) == 0)
2108 					cron_unlink(e->cmd);
2109 			} else {
2110 				cron_unlink(at_cmdfile);
2111 			}
2112 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECAT);
2113 			free(at_cmdfile);
2114 			rinfo_free(rp);
2115 			return (0);
2116 		}
2117 		rp->jobname = xmalloc(strlen(at_cmdfile) + 1);
2118 		(void) strcpy(rp->jobname, at_cmdfile);
2119 
2120 		/*
2121 		 * Skip over the first two lines.
2122 		 */
2123 		(void) fscanf(atcmdfp, "%*[^\n]\n");
2124 		(void) fscanf(atcmdfp, "%*[^\n]\n");
2125 		if (fscanf(atcmdfp, ": notify by mail: %3s%*[^\n]\n",
2126 		    mailvar) == 1) {
2127 			/*
2128 			 * Check to see if we should always send mail
2129 			 * to the owner.
2130 			 */
2131 			rp->mailwhendone = (strcmp(mailvar, "yes") == 0);
2132 		} else {
2133 			rp->mailwhendone = 0;
2134 		}
2135 
2136 		if (fscanf(atcmdfp, "\n: project: %d\n", &projid) == 1) {
2137 			projflag = 1;
2138 		}
2139 		(void) fclose(atcmdfp);
2140 	}
2141 
2142 	/*
2143 	 * we make sure that the system time
2144 	 * hasn't drifted backwards. if it has, el_add() is now
2145 	 * called, to make sure that the event queue is back in order,
2146 	 * and we set the delayed flag. cron will pick up the request
2147 	 * later on at the proper time.
2148 	 */
2149 	dhltime = time(NULL);
2150 	if ((dhltime - e->time) < 0) {
2151 		msg("clock time drifted backwards!\n");
2152 		if (next_event->etype == CRONEVENT) {
2153 			msg("correcting cron event\n");
2154 			next_event->time = next_time(next_event, dhltime);
2155 			el_add(next_event, next_event->time,
2156 			    (next_event->u)->ctid);
2157 		} else { /* etype == ATEVENT */
2158 			msg("correcting batch event\n");
2159 			el_add(next_event, next_event->time,
2160 			    next_event->of.at.eventid);
2161 		}
2162 		delayed++;
2163 		t_old = time(NULL);
2164 		free(at_cmdfile);
2165 		rinfo_free(rp);
2166 		return (0);
2167 	}
2168 
2169 	if ((rfork = fork()) == (pid_t)-1) {
2170 		reap_child();
2171 		if ((rfork = fork()) == (pid_t)-1) {
2172 			msg("cannot fork");
2173 			free(at_cmdfile);
2174 			rinfo_free(rp);
2175 			resched(60);
2176 			(void) sleep(30);
2177 			return (0);
2178 		}
2179 	}
2180 	if (rfork) {		/* parent process */
2181 		contract_abandon_latest(rfork);
2182 
2183 		++qp->nrun;
2184 		rp->pid = rfork;
2185 		rp->que = e->etype;
2186 		if (e->etype != CRONEVENT)
2187 			(e->u)->aruncnt++;
2188 		else
2189 			(e->u)->cruncnt++;
2190 		rp->rusr = (e->u);
2191 		logit(BCHAR, rp, 0);
2192 		free(at_cmdfile);
2193 
2194 		return (0);
2195 	}
2196 
2197 	child_sigreset();
2198 	contract_clear_template();
2199 
2200 	if (e->etype != CRONEVENT) {
2201 		/* open jobfile as stdin to shell */
2202 		if (stat(at_cmdfile, &buf)) {
2203 			if (errno == ENAMETOOLONG) {
2204 				if (chdir(ATDIR) == 0)
2205 					cron_unlink(e->cmd);
2206 			} else
2207 				cron_unlink(at_cmdfile);
2208 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
2209 			exit(1);
2210 		}
2211 		if (!(buf.st_mode&ISUID)) {
2212 			/*
2213 			 * if setuid bit off, original owner has
2214 			 * given this file to someone else
2215 			 */
2216 			cron_unlink(at_cmdfile);
2217 			exit(1);
2218 		}
2219 		if ((fd = open(at_cmdfile, O_RDONLY)) == -1) {
2220 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
2221 			cron_unlink(at_cmdfile);
2222 			exit(1);
2223 		}
2224 		if (fd != 0) {
2225 			(void) dup2(fd, 0);
2226 			(void) close(fd);
2227 		}
2228 		/*
2229 		 * retrieve the project id of the at job and convert it
2230 		 * to a project name.  fail if it's not a valid project
2231 		 * or if the user isn't a member of the project.
2232 		 */
2233 		if (projflag == 1) {
2234 			if ((pproj = getprojbyid(projid, &proj,
2235 			    (void *)&mybuf, sizeof (mybuf))) == NULL ||
2236 			    !inproj(e->u->name, pproj->pj_name,
2237 			    mybuf2, sizeof (mybuf2))) {
2238 				cron_unlink(at_cmdfile);
2239 				mail((e->u)->name, BADPROJID, ERR_CANTEXECAT);
2240 				exit(1);
2241 			}
2242 		}
2243 	}
2244 
2245 	/*
2246 	 * Put process in a new session, and create a new task.
2247 	 */
2248 	if (setsid() < 0) {
2249 		msg("setsid failed with errno = %d. job failed (%s)"
2250 		    " for user %s", errno, e->cmd, e->u->name);
2251 		if (e->etype != CRONEVENT)
2252 			cron_unlink(at_cmdfile);
2253 		exit(1);
2254 	}
2255 
2256 	/*
2257 	 * set correct user identification and check his account
2258 	 */
2259 	r = set_user_cred(e->u, pproj);
2260 	if (r == VUC_EXPIRED) {
2261 		msg("user (%s) account is expired", e->u->name);
2262 		audit_cron_user_acct_expired(e->u->name);
2263 		clean_out_user(e->u);
2264 		exit(1);
2265 	}
2266 	if (r == VUC_NEW_AUTH) {
2267 		msg("user (%s) password has expired", e->u->name);
2268 		audit_cron_user_acct_expired(e->u->name);
2269 		clean_out_user(e->u);
2270 		exit(1);
2271 	}
2272 	if (r != VUC_OK) {
2273 		msg("bad user (%s)", e->u->name);
2274 		audit_cron_bad_user(e->u->name);
2275 		clean_out_user(e->u);
2276 		exit(1);
2277 	}
2278 	/*
2279 	 * check user and initialize the supplementary group access list.
2280 	 * bugid 1230784: deleted from parent to avoid cron hang. Now
2281 	 * only child handles the call.
2282 	 */
2283 
2284 	if (verify_user_cred(e->u) != VUC_OK ||
2285 	    setgid(e->u->gid) == -1 ||
2286 	    initgroups(e->u->name, e->u->gid) == -1) {
2287 		msg("bad user (%s) or setgid failed (%s)",
2288 		    e->u->name, e->u->name);
2289 		audit_cron_bad_user(e->u->name);
2290 		clean_out_user(e->u);
2291 		exit(1);
2292 	}
2293 
2294 	if (e->etype != CRONEVENT) {
2295 		r = audit_cron_session(e->u->name, NULL,
2296 		    e->u->uid, e->u->gid, at_cmdfile);
2297 		cron_unlink(at_cmdfile);
2298 	} else {
2299 		r = audit_cron_session(e->u->name, CRONDIR,
2300 		    e->u->uid, e->u->gid, NULL);
2301 	}
2302 	if (r != 0) {
2303 		msg("cron audit problem. job failed (%s) for user %s",
2304 		    e->cmd, e->u->name);
2305 		exit(1);
2306 	}
2307 
2308 	audit_cron_new_job(e->cmd, e->etype, (void *)e);
2309 
2310 	if (setuid(e->u->uid) == -1)  {
2311 		msg("setuid failed (%s)", e->u->name);
2312 		clean_out_user(e->u);
2313 		exit(1);
2314 	}
2315 
2316 	if (e->etype == CRONEVENT) {
2317 		/* check for standard input to command	*/
2318 		if (e->of.ct.input != NULL) {
2319 			if ((tmpfile = strdup(TMPINFILE)) == NULL) {
2320 				mail((e->u)->name, MALLOCERR,
2321 				    ERR_CANTEXECCRON);
2322 				exit(1);
2323 			}
2324 			if ((fd = mkstemp(tmpfile)) == -1 ||
2325 			    (fptr = fdopen(fd, "w")) == NULL) {
2326 				mail((e->u)->name, NOSTDIN,
2327 				    ERR_CANTEXECCRON);
2328 				cron_unlink(tmpfile);
2329 				free(tmpfile);
2330 				exit(1);
2331 			}
2332 			if ((fwrite(e->of.ct.input, sizeof (char),
2333 			    strlen(e->of.ct.input), fptr)) !=
2334 			    strlen(e->of.ct.input)) {
2335 				mail((e->u)->name, NOSTDIN, ERR_CANTEXECCRON);
2336 				cron_unlink(tmpfile);
2337 				free(tmpfile);
2338 				(void) close(fd);
2339 				(void) fclose(fptr);
2340 				exit(1);
2341 			}
2342 			if (fseek(fptr, (off_t)0, SEEK_SET) != -1) {
2343 				if (fd != 0) {
2344 					(void) dup2(fd, 0);
2345 					(void) close(fd);
2346 				}
2347 			}
2348 			cron_unlink(tmpfile);
2349 			free(tmpfile);
2350 			(void) fclose(fptr);
2351 		} else if ((fd = open("/dev/null", O_RDONLY)) > 0) {
2352 			(void) dup2(fd, 0);
2353 			(void) close(fd);
2354 		}
2355 	}
2356 
2357 	/* redirect stdout and stderr for the shell	*/
2358 	if ((fd = open(rp->outfile, O_WRONLY|O_CREAT|O_EXCL, OUTMODE)) == 1)
2359 		fd = open("/dev/null", O_WRONLY);
2360 
2361 	if (fd >= 0 && fd != 1)
2362 		(void) dup2(fd, 1);
2363 
2364 	if (fd >= 0 && fd != 2) {
2365 		(void) dup2(fd, 2);
2366 		if (fd != 1)
2367 			(void) close(fd);
2368 	}
2369 
2370 	(void) strlcat(homedir, (e->u)->home, sizeof (homedir));
2371 	(void) strlcat(logname, (e->u)->name, sizeof (logname));
2372 	environ = envinit;
2373 	if (chdir((e->u)->home) == -1) {
2374 		mail((e->u)->name, CANTCDHOME,
2375 		    e->etype == CRONEVENT ? ERR_CANTEXECCRON :
2376 		    ERR_CANTEXECAT);
2377 		exit(1);
2378 	}
2379 #ifdef TESTING
2380 	exit(1);
2381 #endif
2382 	/*
2383 	 * make sure that all file descriptors EXCEPT 0, 1 and 2
2384 	 * will be closed.
2385 	 */
2386 	closefrom(3);
2387 
2388 	if ((e->u)->uid != 0)
2389 		(void) nice(qp->nice);
2390 	if (e->etype == CRONEVENT)
2391 		(void) execl(SHELL, "sh", "-c", e->cmd, 0);
2392 	else		/* type == ATEVENT */
2393 		(void) execl(SHELL, "sh", 0);
2394 	mail((e->u)->name, CANTEXECSH,
2395 	    e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT);
2396 	exit(1);
2397 	/*NOTREACHED*/
2398 }
2399 
2400 static int
2401 idle(long t)
2402 {
2403 	time_t	now;
2404 
2405 	while (t > 0L) {
2406 
2407 		if (msg_wait(t) != 0) {
2408 			/* we need to run next job immediately */
2409 			return (0);
2410 		}
2411 
2412 		reap_child();
2413 
2414 		now = time(NULL);
2415 		if (last_time > now) {
2416 			/* clock has been reset */
2417 			return (1);
2418 		}
2419 
2420 		if (next_event == NULL && !el_empty()) {
2421 			next_event = (struct event *)el_first();
2422 		}
2423 		if (next_event == NULL)
2424 			t = INFINITY;
2425 		else
2426 			t = (long)next_event->time - now;
2427 	}
2428 	return (0);
2429 }
2430 
2431 /*
2432  * This used to be in the idle(), but moved to the separate function.
2433  * This called from various place when cron needs to reap the
2434  * child. It includes the situation that cron hit maxrun, and needs
2435  * to reschedule the job.
2436  */
2437 static void
2438 reap_child()
2439 {
2440 	pid_t	pid;
2441 	int	prc;
2442 	struct	runinfo	*rp;
2443 
2444 	for (;;) {
2445 		pid = waitpid((pid_t)-1, &prc, WNOHANG);
2446 		if (pid <= 0)
2447 			break;
2448 #ifdef DEBUG
2449 		fprintf(stderr,
2450 		    "wait returned %x for process %d\n", prc, pid);
2451 #endif
2452 		if ((rp = rinfo_get(pid)) == NULL) {
2453 			if (miscpid_delete(pid) == 0) {
2454 				/* not found in anywhere */
2455 				msg(PIDERR, pid);
2456 			}
2457 		} else if (rp->que == ZOMB) {
2458 			(void) unlink(rp->outfile);
2459 			rinfo_free(rp);
2460 		} else {
2461 			cleanup(rp, prc);
2462 		}
2463 	}
2464 }
2465 
2466 static void
2467 cleanup(struct runinfo *pr, int rc)
2468 {
2469 	int	nextfork = 1;
2470 	struct	usr	*p;
2471 	struct	stat	buf;
2472 
2473 	logit(ECHAR, pr, rc);
2474 	--qt[pr->que].nrun;
2475 	p = pr->rusr;
2476 	if (pr->que != CRONEVENT)
2477 		--p->aruncnt;
2478 	else
2479 		--p->cruncnt;
2480 
2481 	if (lstat(pr->outfile, &buf) == 0) {
2482 		if (!S_ISLNK(buf.st_mode) &&
2483 		    (buf.st_size > 0 || pr->mailwhendone)) {
2484 			/* mail user stdout and stderr */
2485 			for (;;) {
2486 				if ((pr->pid = fork()) < 0) {
2487 					/*
2488 					 * if fork fails try forever in doubling
2489 					 * retry times, up to 16 seconds
2490 					 */
2491 					(void) sleep(nextfork);
2492 					if (nextfork < 16)
2493 						nextfork += nextfork;
2494 					continue;
2495 				} else if (pr->pid == 0) {
2496 					child_sigreset();
2497 					contract_clear_template();
2498 
2499 					mail_result(p, pr, buf.st_size);
2500 					/* NOTREACHED */
2501 				} else {
2502 					contract_abandon_latest(pr->pid);
2503 					pr->que = ZOMB;
2504 					break;
2505 				}
2506 			}
2507 		} else {
2508 			(void) unlink(pr->outfile);
2509 			rinfo_free(pr);
2510 		}
2511 	} else {
2512 		rinfo_free(pr);
2513 	}
2514 
2515 	free_if_unused(p);
2516 }
2517 
2518 /*
2519  * Mail stdout and stderr of a job to user. Get uid for real user and become
2520  * that person. We do this so that mail won't come from root since this
2521  * could be a security hole. If failure, quit - don't send mail as root.
2522  */
2523 static void
2524 mail_result(struct usr *p, struct runinfo *pr, size_t filesize)
2525 {
2526 	struct	passwd	*ruser_ids;
2527 	FILE	*mailpipe;
2528 	FILE	*st;
2529 	struct utsname	name;
2530 	int	nbytes;
2531 	char	iobuf[BUFSIZ];
2532 	char	*cmd;
2533 
2534 	(void) uname(&name);
2535 	if ((ruser_ids = getpwnam(p->name)) == NULL)
2536 		exit(0);
2537 	(void) setuid(ruser_ids->pw_uid);
2538 
2539 	cmd = xmalloc(strlen(MAIL) + strlen(p->name)+2);
2540 	(void) sprintf(cmd, "%s %s", MAIL, p->name);
2541 	mailpipe = popen(cmd, "w");
2542 	free(cmd);
2543 	if (mailpipe == NULL)
2544 		exit(127);
2545 	(void) fprintf(mailpipe, "To: %s\n", p->name);
2546 	if (pr->jobtype == CRONEVENT) {
2547 		(void) fprintf(mailpipe, CRONOUT);
2548 		(void) fprintf(mailpipe, "Your \"cron\" job on %s\n",
2549 		    name.nodename);
2550 		if (pr->jobname != NULL) {
2551 			(void) fprintf(mailpipe, "%s\n\n", pr->jobname);
2552 		}
2553 	} else {
2554 		(void) fprintf(mailpipe, "Subject: Output from \"at\" job\n\n");
2555 		(void) fprintf(mailpipe, "Your \"at\" job on %s\n",
2556 		    name.nodename);
2557 		if (pr->jobname != NULL) {
2558 			(void) fprintf(mailpipe, "\"%s\"\n\n", pr->jobname);
2559 		}
2560 	}
2561 	/* Tmp. file is fopen'ed w/ "r",  secure open */
2562 	if (filesize > 0 &&
2563 	    (st = fopen(pr->outfile, "r")) != NULL) {
2564 		(void) fprintf(mailpipe,
2565 		    "produced the following output:\n\n");
2566 		while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0)
2567 			(void) fwrite(iobuf, sizeof (char), nbytes, mailpipe);
2568 		(void) fclose(st);
2569 	} else {
2570 		(void) fprintf(mailpipe, "completed.\n");
2571 	}
2572 	(void) pclose(mailpipe);
2573 	exit(0);
2574 }
2575 
2576 static int
2577 msg_wait(long tim)
2578 {
2579 	struct	message	msg;
2580 	int	cnt;
2581 	time_t	reftime;
2582 	struct	pollfd pfd[2];
2583 	int64_t	tl;
2584 	int	timeout;
2585 	static int	pending_msg;
2586 	static time_t	pending_reftime;
2587 
2588 	if (pending_msg) {
2589 		process_msg(&msgbuf, pending_reftime);
2590 		pending_msg = 0;
2591 		return (0);
2592 	}
2593 
2594 	/*
2595 	 * We are opening the signal mask to receive SIGCLD. The notifypipe
2596 	 * is used to avoid race condition between SIGCLD and poll system
2597 	 * call.
2598 	 * If SIGCLD is delivered in poll(), poll will be interrupted, and
2599 	 * we will return to idle() to reap the dead children.
2600 	 * If SIGCLD is delivered between sigprocmask() below and poll(),
2601 	 * there is no way we can detect the SIGCLD because poll() won't
2602 	 * be interrupted. In such case, the dead children can't be wait'ed
2603 	 * until poll returns by timeout or a new job. To avoid this race
2604 	 * condition, child_handler write to the notifypipe, so that
2605 	 * poll() will be able to return with POLLIN which indicates that
2606 	 * we have received SIGCLD.
2607 	 *
2608 	 * Since the notifypipe is used to just let poll return from
2609 	 * system call, the data in the pipe won't be read. Therefore,
2610 	 * any data in the pipe needs to be flushed before opening signal
2611 	 * mask.
2612 	 *
2613 	 * Note that we can probably re-write this code with pselect()
2614 	 * which can handle this situation easily.
2615 	 */
2616 	(void) ioctl(notifypipe[0], I_FLUSH, FLUSHW);
2617 
2618 	pfd[0].fd = msgfd;
2619 	pfd[0].events = POLLIN;
2620 	pfd[1].fd = notifypipe[1];
2621 	pfd[1].events = POLLIN;
2622 
2623 #ifdef CRON_MAXSLEEP
2624 	/*
2625 	 * CRON_MAXSLEEP can be defined to have cron periodically wake
2626 	 * up, so that cron can detect a change of TOD and adjust the
2627 	 * sleep time accordingly.
2628 	 */
2629 	tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim;
2630 #endif
2631 	tl = (tim == INFINITY) ? -1ll : (int64_t)tim * 1000;
2632 
2633 	accept_sigcld = 1;
2634 	(void) sigprocmask(SIG_UNBLOCK, &childmask, NULL);
2635 	do {
2636 		timeout = (tl > INT_MAX ? INT_MAX : (int)tl);
2637 		tl -= timeout;
2638 		cnt = poll(pfd, 2, timeout);
2639 		if (cnt == -1 && errno != EINTR) {
2640 			perror("! poll");
2641 		}
2642 	} while (tl > 0 && cnt == 0);
2643 	(void) sigprocmask(SIG_BLOCK, &childmask, NULL);
2644 	accept_sigcld = 0;
2645 
2646 	/*
2647 	 * poll timeout or interrupted.
2648 	 */
2649 	if (cnt <= 0)
2650 		return (0);
2651 
2652 	/*
2653 	 * Not the timeout or new job, but a SIGCLD has been delivered.
2654 	 */
2655 	if ((pfd[0].revents & POLLIN) == 0)
2656 		return (0);
2657 
2658 	errno = 0;
2659 	if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) {
2660 		if (cnt != -1 || errno != EAGAIN)
2661 			perror("! read");
2662 		return (0);
2663 	}
2664 	reftime = time(NULL);
2665 	if (next_event != NULL && reftime >= next_event->time) {
2666 		/*
2667 		 * we need to run the job before reloading crontab.
2668 		 */
2669 		(void) memcpy(&msgbuf, &msg, sizeof (msg));
2670 		pending_msg = 1;
2671 		pending_reftime = reftime;
2672 		return (1);
2673 	}
2674 	process_msg(&msg, reftime);
2675 	return (0);
2676 }
2677 
2678 /*
2679  * process the message supplied via pipe. This will be called either
2680  * immediately after cron read the message from pipe, or idle time
2681  * if the message was pending due to the job execution.
2682  */
2683 static void
2684 process_msg(struct message *pmsg, time_t reftime)
2685 {
2686 	if (pmsg->etype == NULL)
2687 		return;
2688 
2689 	switch (pmsg->etype) {
2690 	case AT:
2691 		if (pmsg->action == DELETE)
2692 			del_atjob(pmsg->fname, pmsg->logname);
2693 		else
2694 			mod_atjob(pmsg->fname, (time_t)0);
2695 		break;
2696 	case CRON:
2697 		if (pmsg->action == DELETE)
2698 			del_ctab(pmsg->fname);
2699 		else
2700 			mod_ctab(pmsg->fname, reftime);
2701 		break;
2702 	default:
2703 		msg("message received - bad format");
2704 		break;
2705 	}
2706 	if (next_event != NULL) {
2707 		if (next_event->etype == CRONEVENT)
2708 			el_add(next_event, next_event->time,
2709 			    (next_event->u)->ctid);
2710 		else	/* etype == ATEVENT */
2711 			el_add(next_event, next_event->time,
2712 			    next_event->of.at.eventid);
2713 		next_event = NULL;
2714 	}
2715 	(void) fflush(stdout);
2716 	pmsg->etype = NULL;
2717 }
2718 
2719 /*
2720  * Allocate a new or find an existing runinfo structure
2721  */
2722 static struct runinfo *
2723 rinfo_get(pid_t pid)
2724 {
2725 	struct runinfo *rp;
2726 
2727 	if (pid == 0) {		/* allocate a new entry */
2728 		rp = xcalloc(1, sizeof (struct runinfo));
2729 		rp->next = rthead;	/* link the entry into the list */
2730 		rthead = rp;
2731 		return (rp);
2732 	}
2733 	/* search the list for an existing entry */
2734 	for (rp = rthead; rp != NULL; rp = rp->next) {
2735 		if (rp->pid == pid)
2736 			break;
2737 	}
2738 	return (rp);
2739 }
2740 
2741 /*
2742  * Free a runinfo structure and its associated memory
2743  */
2744 static void
2745 rinfo_free(struct runinfo *entry)
2746 {
2747 	struct runinfo **rpp;
2748 	struct runinfo *rp;
2749 
2750 #ifdef DEBUG
2751 	(void) fprintf(stderr, "freeing job %s\n", entry->jobname);
2752 #endif
2753 	for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) {
2754 		if (rp == entry) {
2755 			*rpp = rp->next;	/* unlink the entry */
2756 			free(rp->outfile);
2757 			free(rp->jobname);
2758 			free(rp);
2759 			break;
2760 		}
2761 	}
2762 }
2763 
2764 /* ARGSUSED */
2765 static void
2766 thaw_handler(int sig)
2767 {
2768 	;
2769 }
2770 
2771 
2772 /* ARGSUSED */
2773 static void
2774 cronend(int sig)
2775 {
2776 	crabort("SIGTERM", REMOVE_FIFO);
2777 }
2778 
2779 /*ARGSUSED*/
2780 static void
2781 child_handler(int sig)
2782 {
2783 	/*
2784 	 * Just in case someone changes the signal mask.
2785 	 * we don't want to notify the SIGCLD.
2786 	 */
2787 	if (accept_sigcld) {
2788 		(void) write(notifypipe[0], &sig, 1);
2789 	}
2790 }
2791 
2792 static void
2793 child_sigreset(void)
2794 {
2795 	(void) signal(SIGCLD, SIG_DFL);
2796 	(void) sigprocmask(SIG_SETMASK, &defmask, NULL);
2797 }
2798 
2799 /*
2800  * crabort() - handle exits out of cron
2801  */
2802 static void
2803 crabort(char *mssg, int action)
2804 {
2805 	int	c;
2806 
2807 	if (action & REMOVE_FIFO) {
2808 		/* FIFO vanishes when cron finishes */
2809 		if (unlink(FIFO) < 0)
2810 			perror("cron could not unlink FIFO");
2811 	}
2812 
2813 	if (action & CONSOLE_MSG) {
2814 		/* write error msg to console */
2815 		if ((c = open(CONSOLE, O_WRONLY)) >= 0) {
2816 			(void) write(c, "cron aborted: ", 14);
2817 			(void) write(c, mssg, strlen(mssg));
2818 			(void) write(c, "\n", 1);
2819 			(void) close(c);
2820 		}
2821 	}
2822 
2823 	/* always log the message */
2824 	msg(mssg);
2825 	msg("******* CRON ABORTED ********");
2826 	exit(1);
2827 }
2828 
2829 /*
2830  * msg() - time-stamped error reporting function
2831  */
2832 /*PRINTFLIKE1*/
2833 static void
2834 msg(char *fmt, ...)
2835 {
2836 	va_list args;
2837 	time_t	t;
2838 
2839 	t = time(NULL);
2840 
2841 	(void) fflush(stdout);
2842 
2843 	(void) fprintf(stderr, "! ");
2844 
2845 	va_start(args, fmt);
2846 	(void) vfprintf(stderr, fmt, args);
2847 	va_end(args);
2848 
2849 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
2850 	(void) fprintf(stderr, " %s\n", timebuf);
2851 
2852 	(void) fflush(stderr);
2853 }
2854 
2855 static void
2856 logit(int cc, struct runinfo *rp, int rc)
2857 {
2858 	time_t t;
2859 	int    ret;
2860 
2861 	if (!log)
2862 		return;
2863 
2864 	t = time(NULL);
2865 	if (cc == BCHAR)
2866 		(void) printf("%c  CMD: %s\n", cc, next_event->cmd);
2867 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
2868 	(void) printf("%c  %.8s %u %c %s",
2869 	    cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf);
2870 	if ((ret = TSTAT(rc)) != 0)
2871 		(void) printf(" ts=%d", ret);
2872 	if ((ret = RCODE(rc)) != 0)
2873 		(void) printf(" rc=%d", ret);
2874 	(void) putchar('\n');
2875 	(void) fflush(stdout);
2876 }
2877 
2878 static void
2879 resched(int delay)
2880 {
2881 	time_t	nt;
2882 
2883 	/* run job at a later time */
2884 	nt = next_event->time + delay;
2885 	if (next_event->etype == CRONEVENT) {
2886 		next_event->time = next_time(next_event, (time_t)0);
2887 		if (nt < next_event->time)
2888 			next_event->time = nt;
2889 		el_add(next_event, next_event->time, (next_event->u)->ctid);
2890 		delayed = 1;
2891 		msg("rescheduling a cron job");
2892 		return;
2893 	}
2894 	add_atevent(next_event->u, next_event->cmd, nt, next_event->etype);
2895 	msg("rescheduling at job");
2896 }
2897 
2898 static void
2899 quedefs(int action)
2900 {
2901 	int	i;
2902 	int	j;
2903 	char	qbuf[QBUFSIZ];
2904 	FILE	*fd;
2905 
2906 	/* set up default queue definitions */
2907 	for (i = 0; i < NQUEUE; i++) {
2908 		qt[i].njob = qd.njob;
2909 		qt[i].nice = qd.nice;
2910 		qt[i].nwait = qd.nwait;
2911 	}
2912 	if (action == DEFAULT)
2913 		return;
2914 	if ((fd = fopen(QUEDEFS, "r")) == NULL) {
2915 		msg("cannot open quedefs file");
2916 		msg("using default queue definitions");
2917 		return;
2918 	}
2919 	while (fgets(qbuf, QBUFSIZ, fd) != NULL) {
2920 		if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.')
2921 			continue;
2922 		parsqdef(&qbuf[2]);
2923 		qt[j].njob = qq.njob;
2924 		qt[j].nice = qq.nice;
2925 		qt[j].nwait = qq.nwait;
2926 	}
2927 	(void) fclose(fd);
2928 }
2929 
2930 static void
2931 parsqdef(char *name)
2932 {
2933 	int i;
2934 
2935 	qq = qd;
2936 	while (*name) {
2937 		i = 0;
2938 		while (isdigit(*name)) {
2939 			i *= 10;
2940 			i += *name++ - '0';
2941 		}
2942 		switch (*name++) {
2943 		case JOBF:
2944 			qq.njob = i;
2945 			break;
2946 		case NICEF:
2947 			qq.nice = i;
2948 			break;
2949 		case WAITF:
2950 			qq.nwait = i;
2951 			break;
2952 		}
2953 	}
2954 }
2955 
2956 /*
2957  * defaults - read defaults from /etc/default/cron
2958  */
2959 static void
2960 defaults()
2961 {
2962 	int  flags;
2963 	char *deflog;
2964 	char *hz, *tz;
2965 
2966 	/*
2967 	 * get HZ value for environment
2968 	 */
2969 	if ((hz = getenv("HZ")) == (char *)NULL)
2970 		(void) sprintf(hzname, "HZ=%d", HZ);
2971 	else
2972 		(void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz);
2973 	/*
2974 	 * get TZ value for environment
2975 	 */
2976 	(void) snprintf(tzone, sizeof (tzone), "TZ=%s",
2977 	    ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ);
2978 
2979 	if (defopen(DEFFILE) == 0) {
2980 		/* ignore case */
2981 		flags = defcntl(DC_GETFLAGS, 0);
2982 		TURNOFF(flags, DC_CASE);
2983 		(void) defcntl(DC_SETFLAGS, flags);
2984 
2985 		if (((deflog = defread("CRONLOG=")) == NULL) ||
2986 		    (*deflog == 'N') || (*deflog == 'n'))
2987 			log = 0;
2988 		else
2989 			log = 1;
2990 		/* fix for 1087611 - allow paths to be set in defaults file */
2991 		if ((Def_path = defread("PATH=")) != NULL) {
2992 			(void) strlcat(path, Def_path, LINE_MAX);
2993 		} else {
2994 			(void) strlcpy(path, NONROOTPATH, LINE_MAX);
2995 		}
2996 		if ((Def_supath = defread("SUPATH=")) != NULL) {
2997 			(void) strlcat(supath, Def_supath, LINE_MAX);
2998 		} else {
2999 			(void) strlcpy(supath, ROOTPATH, LINE_MAX);
3000 		}
3001 		(void) defopen(NULL);
3002 	}
3003 }
3004 
3005 /*
3006  * Determine if a user entry for a job is still ok.  The method used here
3007  * is a lot (about 75x) faster than using setgrent() / getgrent()
3008  * endgrent().  It should be safe because we use the sysconf to determine
3009  * the max, and it tolerates the max being 0.
3010  */
3011 
3012 static int
3013 verify_user_cred(struct usr *u)
3014 {
3015 	struct passwd *pw;
3016 	size_t numUsrGrps = 0;
3017 	size_t numOrigGrps = 0;
3018 	size_t i;
3019 	int retval;
3020 
3021 	/*
3022 	 * Maximum number of groups a user may be in concurrently.  This
3023 	 * is a value which we obtain at runtime through a sysconf()
3024 	 * call.
3025 	 */
3026 
3027 	static size_t nGroupsMax = (size_t)-1;
3028 
3029 	/*
3030 	 * Arrays for cron user's group list, constructed at startup to
3031 	 * be nGroupsMax elements long, used for verifying user
3032 	 * credentials prior to execution.
3033 	 */
3034 
3035 	static gid_t *UsrGrps;
3036 	static gid_t *OrigGrps;
3037 
3038 	if ((pw = getpwnam(u->name)) == NULL)
3039 		return (VUC_BADUSER);
3040 	if (u->home != NULL) {
3041 		if (strcmp(u->home, pw->pw_dir) != 0) {
3042 			free(u->home);
3043 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
3044 			(void) strcpy(u->home, pw->pw_dir);
3045 		}
3046 	} else {
3047 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
3048 		(void) strcpy(u->home, pw->pw_dir);
3049 	}
3050 	if (u->uid != pw->pw_uid)
3051 		u->uid = pw->pw_uid;
3052 	if (u->gid != pw->pw_gid)
3053 		u->gid  = pw->pw_gid;
3054 
3055 	/*
3056 	 * Create the group id lists needed for job credential
3057 	 * verification.
3058 	 */
3059 
3060 	if (nGroupsMax == (size_t)-1) {
3061 		if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) {
3062 			UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t));
3063 			OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t));
3064 		}
3065 
3066 #ifdef DEBUG
3067 		(void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax);
3068 #endif
3069 	}
3070 
3071 #ifdef DEBUG
3072 	(void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name,
3073 	    pw->pw_uid);
3074 	(void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, "
3075 	    "u->gid = %d\n", pw->pw_gid, u->gid);
3076 #endif
3077 
3078 	retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP;
3079 
3080 	if (nGroupsMax > 0) {
3081 		numOrigGrps = getgroups(nGroupsMax, OrigGrps);
3082 
3083 		(void) initgroups(pw->pw_name, pw->pw_gid);
3084 		numUsrGrps = getgroups(nGroupsMax, UsrGrps);
3085 
3086 		for (i = 0; i < numUsrGrps; i++) {
3087 			if (UsrGrps[i] == u->gid) {
3088 				retval = VUC_OK;
3089 				break;
3090 			}
3091 		}
3092 
3093 		if (OrigGrps) {
3094 			(void) setgroups(numOrigGrps, OrigGrps);
3095 		}
3096 	}
3097 
3098 #ifdef DEBUG
3099 	(void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval);
3100 #endif
3101 
3102 	return (retval);
3103 }
3104 
3105 static int
3106 set_user_cred(const struct usr *u, struct project *pproj)
3107 {
3108 	static char *progname = "cron";
3109 	int r = 0, rval = 0;
3110 
3111 	if ((r = pam_start(progname, u->name, &pam_conv, &pamh))
3112 	    != PAM_SUCCESS) {
3113 #ifdef DEBUG
3114 		msg("pam_start returns %d\n", r);
3115 #endif
3116 		rval = VUC_BADUSER;
3117 		goto set_eser_cred_exit;
3118 	}
3119 
3120 	r = pam_acct_mgmt(pamh, 0);
3121 #ifdef DEBUG
3122 	msg("pam_acc_mgmt returns %d\n", r);
3123 #endif
3124 	if (r == PAM_ACCT_EXPIRED) {
3125 		rval = VUC_EXPIRED;
3126 		goto set_eser_cred_exit;
3127 	}
3128 	if (r == PAM_NEW_AUTHTOK_REQD) {
3129 		rval = VUC_NEW_AUTH;
3130 		goto set_eser_cred_exit;
3131 	}
3132 	if (r != PAM_SUCCESS) {
3133 		rval = VUC_BADUSER;
3134 		goto set_eser_cred_exit;
3135 	}
3136 
3137 	if (pproj != NULL) {
3138 		size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name);
3139 		char *buf = alloca(sz);
3140 
3141 		(void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name);
3142 		(void) pam_set_item(pamh, PAM_RESOURCE, buf);
3143 	}
3144 
3145 	r = pam_setcred(pamh, PAM_ESTABLISH_CRED);
3146 	if (r != PAM_SUCCESS)
3147 		rval = VUC_BADUSER;
3148 
3149 set_eser_cred_exit:
3150 	(void) pam_end(pamh, r);
3151 	return (rval);
3152 }
3153 
3154 static void
3155 clean_out_user(struct usr *u)
3156 {
3157 	if (next_event->u == u) {
3158 		next_event = NULL;
3159 	}
3160 
3161 	clean_out_ctab(u);
3162 	clean_out_atjobs(u);
3163 	free_if_unused(u);
3164 }
3165 
3166 static void
3167 clean_out_atjobs(struct usr *u)
3168 {
3169 	struct event *ev, *pv;
3170 
3171 	for (pv = NULL, ev = u->atevents;
3172 	    ev != NULL;
3173 	    pv = ev, ev = ev->link, free(pv)) {
3174 		el_remove(ev->of.at.eventid, 1);
3175 		if (cwd == AT)
3176 			cron_unlink(ev->cmd);
3177 		else {
3178 			char buf[PATH_MAX];
3179 			if (strlen(ATDIR) + strlen(ev->cmd) + 2
3180 			    < PATH_MAX) {
3181 				(void) sprintf(buf, "%s/%s", ATDIR, ev->cmd);
3182 				cron_unlink(buf);
3183 			}
3184 		}
3185 		free(ev->cmd);
3186 	}
3187 
3188 	u->atevents = NULL;
3189 }
3190 
3191 static void
3192 clean_out_ctab(struct usr *u)
3193 {
3194 	rm_ctevents(u);
3195 	el_remove(u->ctid, 0);
3196 	u->ctid = 0;
3197 	u->ctexists = 0;
3198 }
3199 
3200 static void
3201 cron_unlink(char *name)
3202 {
3203 	int r;
3204 
3205 	r = unlink(name);
3206 	if (r == 0 || (r == -1 && errno == ENOENT)) {
3207 		(void) audit_cron_delete_anc_file(name, NULL);
3208 	}
3209 }
3210 
3211 static void
3212 create_anc_ctab(struct event *e)
3213 {
3214 	if (audit_cron_create_anc_file(e->u->name,
3215 	    (cwd == CRON) ? NULL:CRONDIR,
3216 	    e->u->name, e->u->uid) == -1) {
3217 		process_anc_files(CRON_ANC_DELETE);
3218 		crabort("cannot create ancillary files for crontabs",
3219 		    REMOVE_FIFO|CONSOLE_MSG);
3220 	}
3221 }
3222 
3223 static void
3224 delete_anc_ctab(struct event *e)
3225 {
3226 	(void) audit_cron_delete_anc_file(e->u->name,
3227 	    (cwd == CRON) ? NULL:CRONDIR);
3228 }
3229 
3230 static void
3231 create_anc_atjob(struct event *e)
3232 {
3233 	if (!e->of.at.exists)
3234 		return;
3235 
3236 	if (audit_cron_create_anc_file(e->cmd,
3237 	    (cwd == AT) ? NULL:ATDIR,
3238 	    e->u->name, e->u->uid) == -1) {
3239 		process_anc_files(CRON_ANC_DELETE);
3240 		crabort("cannot create ancillary files for atjobs",
3241 		    REMOVE_FIFO|CONSOLE_MSG);
3242 	}
3243 }
3244 
3245 static void
3246 delete_anc_atjob(struct event *e)
3247 {
3248 	if (!e->of.at.exists)
3249 		return;
3250 
3251 	(void) audit_cron_delete_anc_file(e->cmd,
3252 	    (cwd == AT) ? NULL:ATDIR);
3253 }
3254 
3255 
3256 static void
3257 process_anc_files(int del)
3258 {
3259 	struct usr	*u = uhead;
3260 	struct event	*e;
3261 
3262 	if (!audit_cron_mode())
3263 		return;
3264 
3265 	for (;;) {
3266 		if (u->ctexists && u->ctevents != NULL) {
3267 			e = u->ctevents;
3268 			for (;;) {
3269 				if (del)
3270 					delete_anc_ctab(e);
3271 				else
3272 					create_anc_ctab(e);
3273 				if ((e = e->link) == NULL)
3274 					break;
3275 			}
3276 		}
3277 
3278 		if (u->atevents != NULL) {
3279 			e = u->atevents;
3280 			for (;;) {
3281 				if (del)
3282 					delete_anc_atjob(e);
3283 				else
3284 					create_anc_atjob(e);
3285 				if ((e = e->link) == NULL)
3286 					break;
3287 			}
3288 		}
3289 
3290 		if ((u = u->nextusr)  == NULL)
3291 			break;
3292 	}
3293 }
3294 
3295 /*ARGSUSED*/
3296 static int
3297 cron_conv(int num_msg, struct pam_message **msgs,
3298     struct pam_response **response, void *appdata_ptr)
3299 {
3300 	struct pam_message	**m = msgs;
3301 	int i;
3302 
3303 	for (i = 0; i < num_msg; i++) {
3304 		switch (m[i]->msg_style) {
3305 		case PAM_ERROR_MSG:
3306 		case PAM_TEXT_INFO:
3307 			if (m[i]->msg != NULL) {
3308 				(void) msg("%s\n", m[i]->msg);
3309 			}
3310 			break;
3311 
3312 		default:
3313 			break;
3314 		}
3315 	}
3316 	return (0);
3317 }
3318 
3319 /*
3320  * Cron creates process for other than job. Mail process is the
3321  * one which rinfo does not cover. Therefore, miscpid will keep
3322  * track of the pids executed from cron. Otherwise, we will see
3323  * "unexpected pid returned.." messages appear in the log file.
3324  */
3325 static void
3326 miscpid_insert(pid_t pid)
3327 {
3328 	struct miscpid *mp;
3329 
3330 	mp = xmalloc(sizeof (*mp));
3331 	mp->pid = pid;
3332 	mp->next = miscpid_head;
3333 	miscpid_head = mp;
3334 }
3335 
3336 static int
3337 miscpid_delete(pid_t pid)
3338 {
3339 	struct miscpid *mp, *omp;
3340 	int found = 0;
3341 
3342 	omp = NULL;
3343 	for (mp = miscpid_head; mp != NULL; mp = mp->next) {
3344 		if (mp->pid == pid) {
3345 			found = 1;
3346 			break;
3347 		}
3348 		omp = mp;
3349 	}
3350 	if (found) {
3351 		if (omp != NULL)
3352 			omp->next = mp->next;
3353 		else
3354 			miscpid_head = NULL;
3355 		free(mp);
3356 	}
3357 	return (found);
3358 }
3359 
3360 /*
3361  * Establish contract terms such that all children are in abandoned
3362  * process contracts.
3363  */
3364 static void
3365 contract_set_template(void)
3366 {
3367 	int fd;
3368 
3369 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
3370 		crabort("cannot open process contract template",
3371 		    REMOVE_FIFO | CONSOLE_MSG);
3372 
3373 	if (ct_pr_tmpl_set_param(fd, 0) ||
3374 	    ct_tmpl_set_informative(fd, 0) ||
3375 	    ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR))
3376 		crabort("cannot establish contract template terms",
3377 		    REMOVE_FIFO | CONSOLE_MSG);
3378 
3379 	if (ct_tmpl_activate(fd))
3380 		crabort("cannot activate contract template",
3381 		    REMOVE_FIFO | CONSOLE_MSG);
3382 
3383 	(void) close(fd);
3384 }
3385 
3386 /*
3387  * Clear active process contract template.
3388  */
3389 static void
3390 contract_clear_template(void)
3391 {
3392 	int fd;
3393 
3394 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
3395 		crabort("cannot open process contract template",
3396 		    REMOVE_FIFO | CONSOLE_MSG);
3397 
3398 	if (ct_tmpl_clear(fd))
3399 		crabort("cannot clear contract template",
3400 		    REMOVE_FIFO | CONSOLE_MSG);
3401 
3402 	(void) close(fd);
3403 }
3404 
3405 /*
3406  * Abandon latest process contract unconditionally.  If we have leaked [some
3407  * critical amount], exit such that the kernel reaps our contracts.
3408  */
3409 static void
3410 contract_abandon_latest(pid_t pid)
3411 {
3412 	int r;
3413 	ctid_t id;
3414 	static uint_t cts_lost;
3415 
3416 	if (cts_lost > MAX_LOST_CONTRACTS)
3417 		crabort("repeated failure to abandon contracts",
3418 		    REMOVE_FIFO | CONSOLE_MSG);
3419 
3420 	if (r = contract_latest(&id)) {
3421 		msg("could not obtain latest contract for "
3422 		    "PID %ld: %s", pid, strerror(r));
3423 		cts_lost++;
3424 		return;
3425 	}
3426 
3427 	if (r = contract_abandon_id(id)) {
3428 		msg("could not abandon latest contract %ld: %s", id,
3429 		    strerror(r));
3430 		cts_lost++;
3431 		return;
3432 	}
3433 }
3434