xref: /titanic_50/usr/src/cmd/cron/cron.c (revision a43153bf6cee5e5307b15ed4f77dafc5c56f333d)
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 (strcmp(u->home, pw->pw_dir) != 0) {
902 			free(u->home);
903 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
904 			(void) strcpy(u->home, pw->pw_dir);
905 		}
906 		u->ctexists = TRUE;
907 		if (u->ctid == 0) {
908 #ifdef DEBUG
909 			(void) fprintf(stderr, "%s now has a crontab\n",
910 			    u->name);
911 #endif
912 			/* user didnt have a crontab last time */
913 			u->ctid = ecid++;
914 			u->ctevents = NULL;
915 			readcron(u, reftime);
916 			return;
917 		}
918 #ifdef DEBUG
919 		(void) fprintf(stderr, "%s has revised his crontab\n", u->name);
920 #endif
921 		rm_ctevents(u);
922 		el_remove(u->ctid, 0);
923 		readcron(u, reftime);
924 	}
925 }
926 
927 /* ARGSUSED */
928 static void
929 mod_atjob(char *name, time_t reftime)
930 {
931 	char	*ptr;
932 	time_t	tim;
933 	struct	passwd	*pw;
934 	struct	stat	buf;
935 	struct	usr	*u;
936 	char	namebuf[PATH_MAX];
937 	char	*pname;
938 	int	jobtype;
939 
940 	ptr = name;
941 	if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
942 		return;
943 	ptr++;
944 	if (!isalpha(*ptr))
945 		return;
946 	jobtype = *ptr - 'a';
947 
948 	/* check for audit ancillary file */
949 	if (audit_cron_is_anc_name(name))
950 		return;
951 
952 	if (cwd != AT) {
953 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name)
954 		    >= sizeof (namebuf)) {
955 			return;
956 		}
957 		pname = namebuf;
958 	} else {
959 		pname = name;
960 	}
961 	if (stat(pname, &buf) || jobtype >= NQUEUE) {
962 		cron_unlink(pname);
963 		return;
964 	}
965 	if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) {
966 		cron_unlink(pname);
967 		return;
968 	}
969 	if ((pw = getpwuid(buf.st_uid)) == NULL) {
970 		cron_unlink(pname);
971 		return;
972 	}
973 	/*
974 	 * a warning message is given by the at command so there is no
975 	 * need to give one here......use this code if you only want
976 	 * users with a login shell of /usr/bin/sh to use cron
977 	 */
978 #ifdef BOURNESHELLONLY
979 	if ((strcmp(pw->pw_shell, "") != 0) &&
980 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
981 		mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT);
982 		cron_unlink(pname);
983 		return;
984 	}
985 #endif
986 	if ((u = find_usr(pw->pw_name)) == NULL) {
987 #ifdef DEBUG
988 		(void) fprintf(stderr, "new user (%s) with an at job = %s\n",
989 		    pw->pw_name, name);
990 #endif
991 		u = create_ulist(pw->pw_name, ATEVENT);
992 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
993 		(void) strcpy(u->home, pw->pw_dir);
994 		u->uid = pw->pw_uid;
995 		u->gid = pw->pw_gid;
996 		add_atevent(u, name, tim, jobtype);
997 	} else {
998 		u->uid = pw->pw_uid;
999 		u->gid = pw->pw_gid;
1000 		free(u->home);
1001 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
1002 		(void) strcpy(u->home, pw->pw_dir);
1003 		update_atevent(u, name, tim, jobtype);
1004 	}
1005 }
1006 
1007 static void
1008 add_atevent(struct usr *u, char *job, time_t tim, int jobtype)
1009 {
1010 	struct event *e;
1011 
1012 	e = xmalloc(sizeof (struct event));
1013 	e->etype = jobtype;
1014 	e->cmd = xmalloc(strlen(job) + 1);
1015 	(void) strcpy(e->cmd, job);
1016 	e->u = u;
1017 	e->link = u->atevents;
1018 	u->atevents = e;
1019 	e->of.at.exists = TRUE;
1020 	e->of.at.eventid = ecid++;
1021 	if (tim < init_time)	/* old job */
1022 		e->time = init_time;
1023 	else
1024 		e->time = tim;
1025 #ifdef DEBUG
1026 	(void) fprintf(stderr, "add_atevent: user=%s, job=%s, time=%ld\n",
1027 	    u->name, e->cmd, e->time);
1028 #endif
1029 	el_add(e, e->time, e->of.at.eventid);
1030 }
1031 
1032 void
1033 update_atevent(struct usr *u, char *name, time_t tim, int jobtype)
1034 {
1035 	struct event *e;
1036 
1037 	e = u->atevents;
1038 	while (e != NULL) {
1039 		if (strcmp(e->cmd, name) == 0) {
1040 			e->of.at.exists = TRUE;
1041 			break;
1042 		} else {
1043 			e = e->link;
1044 		}
1045 	}
1046 	if (e == NULL) {
1047 #ifdef DEBUG
1048 		(void) fprintf(stderr, "%s has a new at job = %s\n",
1049 		    u->name, name);
1050 #endif
1051 			add_atevent(u, name, tim, jobtype);
1052 	}
1053 }
1054 
1055 static char line[CTLINESIZE];	/* holds a line from a crontab file */
1056 static int cursor;		/* cursor for the above line */
1057 
1058 static void
1059 readcron(struct usr *u, time_t reftime)
1060 {
1061 	/*
1062 	 * readcron reads in a crontab file for a user (u). The list of
1063 	 * events for user u is built, and u->events is made to point to
1064 	 * this list. Each event is also entered into the main event
1065 	 * list.
1066 	 */
1067 	FILE *cf;	/* cf will be a user's crontab file */
1068 	struct event *e;
1069 	int start;
1070 	unsigned int i;
1071 	char namebuf[PATH_MAX];
1072 	char *pname;
1073 	int lineno = 0;
1074 
1075 	/* read the crontab file */
1076 	cte_init();		/* Init error handling */
1077 	if (cwd != CRON) {
1078 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
1079 		    CRONDIR, u->name) >= sizeof (namebuf)) {
1080 			return;
1081 		}
1082 		pname = namebuf;
1083 	} else {
1084 		pname = u->name;
1085 	}
1086 	if ((cf = fopen(pname, "r")) == NULL) {
1087 		mail(u->name, NOREAD, ERR_UNIXERR);
1088 		return;
1089 	}
1090 	while (fgets(line, CTLINESIZE, cf) != NULL) {
1091 		/* process a line of a crontab file */
1092 		lineno++;
1093 		if (cte_istoomany())
1094 			break;
1095 		cursor = 0;
1096 		while (line[cursor] == ' ' || line[cursor] == '\t')
1097 			cursor++;
1098 		if (line[cursor] == '#' || line[cursor] == '\n')
1099 			continue;
1100 		e = xmalloc(sizeof (struct event));
1101 		e->etype = CRONEVENT;
1102 		if (!(((e->of.ct.minute = next_field(0, 59)) != NULL) &&
1103 		    ((e->of.ct.hour = next_field(0, 23)) != NULL) &&
1104 		    ((e->of.ct.daymon = next_field(1, 31)) != NULL) &&
1105 		    ((e->of.ct.month = next_field(1, 12)) != NULL) &&
1106 		    ((e->of.ct.dayweek = next_field(0, 6)) != NULL))) {
1107 			free(e);
1108 			cte_add(lineno, line);
1109 			continue;
1110 		}
1111 		while (line[cursor] == ' ' || line[cursor] == '\t')
1112 			cursor++;
1113 		if (line[cursor] == '\n' || line[cursor] == '\0')
1114 			continue;
1115 		/* get the command to execute	*/
1116 		start = cursor;
1117 again:
1118 		while ((line[cursor] != '%') &&
1119 		    (line[cursor] != '\n') &&
1120 		    (line[cursor] != '\0') &&
1121 		    (line[cursor] != '\\'))
1122 			cursor++;
1123 		if (line[cursor] == '\\') {
1124 			cursor += 2;
1125 			goto again;
1126 		}
1127 		e->cmd = xmalloc(cursor-start + 1);
1128 		(void) strncpy(e->cmd, line + start, cursor-start);
1129 		e->cmd[cursor-start] = '\0';
1130 		/* see if there is any standard input	*/
1131 		if (line[cursor] == '%') {
1132 			e->of.ct.input = xmalloc(strlen(line)-cursor + 1);
1133 			(void) strcpy(e->of.ct.input, line + cursor + 1);
1134 			for (i = 0; i < strlen(e->of.ct.input); i++) {
1135 				if (e->of.ct.input[i] == '%')
1136 					e->of.ct.input[i] = '\n';
1137 			}
1138 		} else {
1139 			e->of.ct.input = NULL;
1140 		}
1141 		/* have the event point to it's owner	*/
1142 		e->u = u;
1143 		/* insert this event at the front of this user's event list */
1144 		e->link = u->ctevents;
1145 		u->ctevents = e;
1146 		/* set the time for the first occurance of this event	*/
1147 		e->time = next_time(e, reftime);
1148 		/* finally, add this event to the main event list	*/
1149 		el_add(e, e->time, u->ctid);
1150 		cte_valid();
1151 #ifdef DEBUG
1152 		cftime(timebuf, "%C", &e->time);
1153 		(void) fprintf(stderr, "inserting cron event %s at %ld (%s)\n",
1154 		    e->cmd, e->time, timebuf);
1155 #endif
1156 	}
1157 	cte_sendmail(u->name);	/* mail errors if any to user */
1158 	(void) fclose(cf);
1159 }
1160 
1161 /*
1162  * Below are the functions for handling of errors in crontabs. Concept is to
1163  * collect faulty lines and send one email at the end of the crontab
1164  * evaluation. If there are erroneous lines only ((cte_nvalid == 0), evaluation
1165  * of crontab is aborted. Otherwise reading of crontab is continued to the end
1166  * of the file but no further error logging appears.
1167  */
1168 static void
1169 cte_init()
1170 {
1171 	if (cte_text == NULL)
1172 		cte_text = xmalloc(MAILBUFLEN);
1173 	(void) strlcpy(cte_text, cte_intro, MAILBUFLEN);
1174 	cte_lp = cte_text + sizeof (cte_intro) - 1;
1175 	cte_free = MAILBINITFREE;
1176 	cte_nvalid = 0;
1177 }
1178 
1179 static void
1180 cte_add(int lineno, char *ctline)
1181 {
1182 	int len;
1183 	char *p;
1184 
1185 	if (cte_free >= LINELIMIT) {
1186 		(void) sprintf(cte_lp, "%4d: ", lineno);
1187 		(void) strlcat(cte_lp, ctline, LINELIMIT - 1);
1188 		len = strlen(cte_lp);
1189 		if (cte_lp[len - 1] != '\n') {
1190 			cte_lp[len++] = '\n';
1191 			cte_lp[len] = '\0';
1192 		}
1193 		for (p = cte_lp; *p; p++) {
1194 			if (isprint(*p) || *p == '\n' || *p == '\t')
1195 				continue;
1196 			*p = '.';
1197 		}
1198 		cte_lp += len;
1199 		cte_free -= len;
1200 		if (cte_free < LINELIMIT) {
1201 			size_t buflen = MAILBUFLEN - (cte_lp - cte_text);
1202 			(void) strlcpy(cte_lp, cte_trail1, buflen);
1203 			if (cte_nvalid == 0)
1204 				(void) strlcat(cte_lp, cte_trail2, buflen);
1205 		}
1206 	}
1207 }
1208 
1209 static void
1210 cte_valid()
1211 {
1212 	cte_nvalid++;
1213 }
1214 
1215 static int
1216 cte_istoomany()
1217 {
1218 	/*
1219 	 * Return TRUE only if all lines are faulty. So evaluation of
1220 	 * a crontab is not aborted if at least one valid line was found.
1221 	 */
1222 	return (cte_nvalid == 0 && cte_free < LINELIMIT);
1223 }
1224 
1225 static void
1226 cte_sendmail(char *username)
1227 {
1228 	if (cte_free < MAILBINITFREE)
1229 		mail(username, cte_text, ERR_CRONTABENT);
1230 }
1231 
1232 /*
1233  * Send mail with error message to a user
1234  */
1235 static void
1236 mail(char *usrname, char *mesg, int format)
1237 {
1238 	/* mail mails a user a message.	*/
1239 	FILE *pipe;
1240 	char *temp;
1241 	struct passwd	*ruser_ids;
1242 	pid_t fork_val;
1243 	int saveerrno = errno;
1244 	struct utsname	name;
1245 
1246 #ifdef TESTING
1247 	return;
1248 #endif
1249 	(void) uname(&name);
1250 	if ((fork_val = fork()) == (pid_t)-1) {
1251 		msg("cron cannot fork\n");
1252 		return;
1253 	}
1254 	if (fork_val == 0) {
1255 		child_sigreset();
1256 		contract_clear_template();
1257 		if ((ruser_ids = getpwnam(usrname)) == NULL)
1258 			exit(0);
1259 		(void) setuid(ruser_ids->pw_uid);
1260 		temp = xmalloc(strlen(MAIL) + strlen(usrname) + 2);
1261 		(void) sprintf(temp, "%s %s", MAIL, usrname);
1262 		pipe = popen(temp, "w");
1263 		if (pipe != NULL) {
1264 			(void) fprintf(pipe, "To: %s\n", usrname);
1265 			switch (format) {
1266 			case ERR_CRONTABENT:
1267 				(void) fprintf(pipe, CRONTABERR);
1268 				(void) fprintf(pipe, "Your \"crontab\" on %s\n",
1269 				    name.nodename);
1270 				(void) fprintf(pipe, mesg);
1271 				(void) fprintf(pipe,
1272 				    "\nEntries or crontab have been ignored\n");
1273 				break;
1274 			case ERR_UNIXERR:
1275 				(void) fprintf(pipe, "Subject: %s\n\n", mesg);
1276 				(void) fprintf(pipe,
1277 				    "The error on %s was \"%s\"\n",
1278 				    name.nodename, errmsg(saveerrno));
1279 				break;
1280 
1281 			case ERR_CANTEXECCRON:
1282 				(void) fprintf(pipe,
1283 				"Subject: Couldn't run your \"cron\" job\n\n");
1284 				(void) fprintf(pipe,
1285 				    "Your \"cron\" job on %s ", name.nodename);
1286 				(void) fprintf(pipe, "couldn't be run\n");
1287 				(void) fprintf(pipe, "%s\n", mesg);
1288 				(void) fprintf(pipe,
1289 				"The error was \"%s\"\n", errmsg(saveerrno));
1290 				break;
1291 
1292 			case ERR_CANTEXECAT:
1293 				(void) fprintf(pipe,
1294 				"Subject: Couldn't run your \"at\" job\n\n");
1295 				(void) fprintf(pipe, "Your \"at\" job on %s ",
1296 				    name.nodename);
1297 				(void) fprintf(pipe, "couldn't be run\n");
1298 				(void) fprintf(pipe, "%s\n", mesg);
1299 				(void) fprintf(pipe,
1300 				"The error was \"%s\"\n", errmsg(saveerrno));
1301 				break;
1302 
1303 			default:
1304 				break;
1305 			}
1306 			(void) pclose(pipe);
1307 		}
1308 		free(temp);
1309 		exit(0);
1310 	}
1311 
1312 	contract_abandon_latest(fork_val);
1313 
1314 	if (cron_pid == getpid()) {
1315 		miscpid_insert(fork_val);
1316 	}
1317 }
1318 
1319 static char *
1320 next_field(int lower, int upper)
1321 {
1322 	/*
1323 	 * next_field returns a pointer to a string which holds the next
1324 	 * field of a line of a crontab file.
1325 	 *   if (numbers in this field are out of range (lower..upper),
1326 	 *	or there is a syntax error) then
1327 	 *	NULL is returned, and a mail message is sent to the
1328 	 *	user telling him which line the error was in.
1329 	 */
1330 
1331 	char *s;
1332 	int num, num2, start;
1333 
1334 	while ((line[cursor] == ' ') || (line[cursor] == '\t'))
1335 		cursor++;
1336 	start = cursor;
1337 	if (line[cursor] == '\0') {
1338 		return (NULL);
1339 	}
1340 	if (line[cursor] == '*') {
1341 		cursor++;
1342 		if ((line[cursor] != ' ') && (line[cursor] != '\t'))
1343 			return (NULL);
1344 		s = xmalloc(2);
1345 		(void) strcpy(s, "*");
1346 		return (s);
1347 	}
1348 	for (;;) {
1349 		if (!isdigit(line[cursor]))
1350 			return (NULL);
1351 		num = 0;
1352 		do {
1353 			num = num*10 + (line[cursor]-'0');
1354 		} while (isdigit(line[++cursor]));
1355 		if ((num < lower) || (num > upper))
1356 			return (NULL);
1357 		if (line[cursor] == '-') {
1358 			if (!isdigit(line[++cursor]))
1359 				return (NULL);
1360 			num2 = 0;
1361 			do {
1362 				num2 = num2*10 + (line[cursor]-'0');
1363 			} while (isdigit(line[++cursor]));
1364 			if ((num2 < lower) || (num2 > upper))
1365 				return (NULL);
1366 		}
1367 		if ((line[cursor] == ' ') || (line[cursor] == '\t'))
1368 			break;
1369 		if (line[cursor] == '\0')
1370 			return (NULL);
1371 		if (line[cursor++] != ',')
1372 			return (NULL);
1373 	}
1374 	s = xmalloc(cursor-start + 1);
1375 	(void) strncpy(s, line + start, cursor-start);
1376 	s[cursor-start] = '\0';
1377 	return (s);
1378 }
1379 
1380 #define	tm_cmp(t1, t2) (\
1381 	(t1)->tm_year == (t2)->tm_year && \
1382 	(t1)->tm_mon == (t2)->tm_mon && \
1383 	(t1)->tm_mday == (t2)->tm_mday && \
1384 	(t1)->tm_hour == (t2)->tm_hour && \
1385 	(t1)->tm_min == (t2)->tm_min)
1386 
1387 #define	tm_setup(tp, yr, mon, dy, hr, min, dst) \
1388 	(tp)->tm_year = yr; \
1389 	(tp)->tm_mon = mon; \
1390 	(tp)->tm_mday = dy; \
1391 	(tp)->tm_hour = hr; \
1392 	(tp)->tm_min = min; \
1393 	(tp)->tm_isdst = dst; \
1394 	(tp)->tm_sec = 0; \
1395 	(tp)->tm_wday = 0; \
1396 	(tp)->tm_yday = 0;
1397 
1398 /*
1399  * modification for bugid 1104537. the second argument to next_time is
1400  * now the value of time(2) to be used. if this is 0, then use the
1401  * current time. otherwise, the second argument is the time from which to
1402  * calculate things. this is useful to correct situations where you've
1403  * gone backwards in time (I.e. the system's internal clock is correcting
1404  * itself backwards).
1405  */
1406 
1407 static time_t
1408 next_time(struct event *e, time_t tflag)
1409 {
1410 	/*
1411 	 * returns the integer time for the next occurance of event e.
1412 	 * the following fields have ranges as indicated:
1413 	 * PRGM  | min	hour	day of month	mon	day of week
1414 	 * ------|-------------------------------------------------------
1415 	 * cron  | 0-59	0-23	    1-31	1-12	0-6 (0=sunday)
1416 	 * time  | 0-59	0-23	    1-31	0-11	0-6 (0=sunday)
1417 	 * NOTE: this routine is hard to understand.
1418 	 */
1419 
1420 	struct tm *tm, ref_tm, tmp, tmp1, tmp2;
1421 	int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days,
1422 	    d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd,
1423 	    today;
1424 
1425 	time_t t, ref_t, t1, t2, zone_start;
1426 	int fallback;
1427 	extern int days_btwn(int, int, int, int, int, int);
1428 
1429 	if (tflag == 0) {
1430 		t = time(NULL);	/* original way of doing things	*/
1431 	} else {
1432 		t =  tflag;
1433 	}
1434 
1435 	tm = &ref_tm;	/* use a local variable and call localtime_r() */
1436 	ref_t = t;	/* keep a copy of the reference time */
1437 
1438 recalc:
1439 	fallback = 0;
1440 
1441 	(void) localtime_r(&t, tm);
1442 
1443 	if (daylight) {
1444 		tmp = *tm;
1445 		tmp.tm_isdst = (tm->tm_isdst > 0 ? 0 : 1);
1446 		t1 = xmktime(&tmp);
1447 		/*
1448 		 * see if we will have timezone switch over, and clock will
1449 		 * fall back. zone_start will hold the time when it happens
1450 		 * (ie time of PST -> PDT switch over).
1451 		 */
1452 		if (tm->tm_isdst != tmp.tm_isdst &&
1453 		    (t1 - t) == (timezone - altzone) &&
1454 		    tm_cmp(tm, &tmp)) {
1455 			zone_start = get_switching_time(tmp.tm_isdst, t);
1456 			fallback = 1;
1457 		}
1458 	}
1459 
1460 	tm_mon = next_ge(tm->tm_mon + 1, e->of.ct.month) - 1;	/* 0-11 */
1461 	tm_mday = next_ge(tm->tm_mday, e->of.ct.daymon);	/* 1-31 */
1462 	tm_wday = next_ge(tm->tm_wday, e->of.ct.dayweek);	/* 0-6	*/
1463 	today = TRUE;
1464 	if ((strcmp(e->of.ct.daymon, "*") == 0 && tm->tm_wday != tm_wday) ||
1465 	    (strcmp(e->of.ct.dayweek, "*") == 0 && tm->tm_mday != tm_mday) ||
1466 	    (tm->tm_mday != tm_mday && tm->tm_wday != tm_wday) ||
1467 	    (tm->tm_mon != tm_mon)) {
1468 		today = FALSE;
1469 	}
1470 	m = tm->tm_min + (t == ref_t ? 1 : 0);
1471 	if ((tm->tm_hour + 1) <= next_ge(tm->tm_hour, e->of.ct.hour)) {
1472 		m = 0;
1473 	}
1474 	min = next_ge(m%60, e->of.ct.minute);
1475 	carry = (min < m) ? 1 : 0;
1476 	h = tm->tm_hour + carry;
1477 	hr = next_ge(h%24, e->of.ct.hour);
1478 	carry = (hr < h) ? 1 : 0;
1479 
1480 	if (carry == 0 && today) {
1481 		/* this event must occur today */
1482 		tm_setup(&tmp, tm->tm_year, tm->tm_mon, tm->tm_mday,
1483 		    hr, min, tm->tm_isdst);
1484 		tmp1 = tmp;
1485 		if ((t1 = xmktime(&tmp1)) == (time_t)-1) {
1486 			return (0);
1487 		}
1488 		if (daylight && tmp.tm_isdst != tmp1.tm_isdst) {
1489 			/* In case we are falling back */
1490 			if (fallback) {
1491 				/* we may need to run the job once more. */
1492 				t = zone_start;
1493 				goto recalc;
1494 			}
1495 
1496 			/*
1497 			 * In case we are not in falling back period,
1498 			 * calculate the time assuming the DST. If the
1499 			 * date/time is not altered by mktime, it is the
1500 			 * time to execute the job.
1501 			 */
1502 			tmp2 = tmp;
1503 			tmp2.tm_isdst = tmp1.tm_isdst;
1504 			if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
1505 				return (0);
1506 			}
1507 			if (tmp1.tm_isdst == tmp2.tm_isdst &&
1508 			    tm_cmp(&tmp, &tmp2)) {
1509 				/*
1510 				 * We got a valid time.
1511 				 */
1512 				return (t1);
1513 			} else {
1514 				/*
1515 				 * If the date does not match even if
1516 				 * we assume the alternate timezone, then
1517 				 * it must be the invalid time. eg
1518 				 * 2am while switching 1:59am to 3am.
1519 				 * t1 should point the time before the
1520 				 * switching over as we've calculate the
1521 				 * time with assuming alternate zone.
1522 				 */
1523 				if (tmp1.tm_isdst != tmp2.tm_isdst) {
1524 					t = get_switching_time(tmp1.tm_isdst,
1525 					    t1);
1526 				} else {
1527 					/* does this really happen? */
1528 					t = get_switching_time(tmp1.tm_isdst,
1529 					    t1 - abs(timezone - altzone));
1530 				}
1531 				if (t == (time_t)-1)
1532 					return (0);
1533 			}
1534 			goto recalc;
1535 		}
1536 		if (tm_cmp(&tmp, &tmp1)) {
1537 			/* got valid time */
1538 			return (t1);
1539 		} else {
1540 			/*
1541 			 * This should never happen, but just in
1542 			 * case, we fall back to the old code.
1543 			 */
1544 			if (tm->tm_min > min) {
1545 				t += (time_t)(hr-tm->tm_hour-1) * HOUR +
1546 				    (time_t)(60-tm->tm_min + min) * MINUTE;
1547 			} else {
1548 				t += (time_t)(hr-tm->tm_hour) * HOUR +
1549 				    (time_t)(min-tm->tm_min) * MINUTE;
1550 			}
1551 			t1 = t;
1552 			t -= (time_t)tm->tm_sec;
1553 			(void) localtime_r(&t, &tmp);
1554 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
1555 				t -= (timezone - altzone);
1556 			return ((t <= ref_t) ? t1 : t);
1557 		}
1558 	}
1559 
1560 	/*
1561 	 * Job won't run today, however if we have a switch over within
1562 	 * one hour and we will have one hour time drifting back in this
1563 	 * period, we may need to run the job one more time if the job was
1564 	 * set to run on this hour of clock.
1565 	 */
1566 	if (fallback) {
1567 		t = zone_start;
1568 		goto recalc;
1569 	}
1570 
1571 	min = next_ge(0, e->of.ct.minute);
1572 	hr = next_ge(0, e->of.ct.hour);
1573 
1574 	/*
1575 	 * calculate the date of the next occurance of this event, which
1576 	 * will be on a different day than the current
1577 	 */
1578 
1579 	/* check monthly day specification	*/
1580 	d1 = tm->tm_mday + 1;
1581 	day1 = next_ge((d1-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1,
1582 	    e->of.ct.daymon);
1583 	carry1 = (day1 < d1) ? 1 : 0;
1584 
1585 	/* check weekly day specification	*/
1586 	d2 = tm->tm_wday + 1;
1587 	wday = next_ge(d2%7, e->of.ct.dayweek);
1588 	if (wday < d2)
1589 		daysahead = 7 - d2 + wday;
1590 	else
1591 		daysahead = wday - d2;
1592 	day2 = (d1 + daysahead-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1;
1593 	carry2 = (day2 < d1) ? 1 : 0;
1594 
1595 	/*
1596 	 *	based on their respective specifications, day1, and day2 give
1597 	 *	the day of the month for the next occurance of this event.
1598 	 */
1599 	if ((strcmp(e->of.ct.daymon, "*") == 0) &&
1600 	    (strcmp(e->of.ct.dayweek, "*") != 0)) {
1601 		day1 = day2;
1602 		carry1 = carry2;
1603 	}
1604 	if ((strcmp(e->of.ct.daymon, "*") != 0) &&
1605 	    (strcmp(e->of.ct.dayweek, "*") == 0)) {
1606 		day2 = day1;
1607 		carry2 = carry1;
1608 	}
1609 
1610 	yr = tm->tm_year;
1611 	if ((carry1 && carry2) || (tm->tm_mon != tm_mon)) {
1612 		/* event does not occur in this month	*/
1613 		m = tm->tm_mon + 1;
1614 		mon = next_ge(m%12 + 1, e->of.ct.month) - 1;	/* 0..11 */
1615 		carry = (mon < m) ? 1 : 0;
1616 		yr += carry;
1617 		/* recompute day1 and day2	*/
1618 		day1 = next_ge(1, e->of.ct.daymon);
1619 		db = days_btwn(tm->tm_mon, tm->tm_mday, tm->tm_year, mon,
1620 		    1, yr) + 1;
1621 		wd = (tm->tm_wday + db)%7;
1622 		/* wd is the day of the week of the first of month mon	*/
1623 		wday = next_ge(wd, e->of.ct.dayweek);
1624 		if (wday < wd)
1625 			day2 = 1 + 7 - wd + wday;
1626 		else
1627 			day2 = 1 + wday - wd;
1628 		if ((strcmp(e->of.ct.daymon, "*") != 0) &&
1629 		    (strcmp(e->of.ct.dayweek, "*") == 0))
1630 			day2 = day1;
1631 		if ((strcmp(e->of.ct.daymon, "*") == 0) &&
1632 		    (strcmp(e->of.ct.dayweek, "*") != 0))
1633 			day1 = day2;
1634 		day = (day1 < day2) ? day1 : day2;
1635 	} else {			/* event occurs in this month	*/
1636 		mon = tm->tm_mon;
1637 		if (!carry1 && !carry2)
1638 			day = (day1 < day2) ? day1 : day2;
1639 		else if (!carry1)
1640 			day = day1;
1641 		else
1642 			day = day2;
1643 	}
1644 
1645 	/*
1646 	 * now that we have the min, hr, day, mon, yr of the next event,
1647 	 * figure out what time that turns out to be.
1648 	 */
1649 	tm_setup(&tmp, yr, mon, day, hr, min, -1);
1650 	tmp2 = tmp;
1651 	if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
1652 		return (0);
1653 	}
1654 	if (tm_cmp(&tmp, &tmp2)) {
1655 		/*
1656 		 * mktime returns clock for the current time zone. If the
1657 		 * target date was in fallback period, it needs to be adjusted
1658 		 * to the time comes first.
1659 		 * Suppose, we are at Jan and scheduling job at 1:30am10/26/03.
1660 		 * mktime returns the time in PST, but 1:30am in PDT comes
1661 		 * first. So reverse the tm_isdst, and see if we have such
1662 		 * time/date.
1663 		 */
1664 		if (daylight) {
1665 			int dst = tmp2.tm_isdst;
1666 
1667 			tmp2 = tmp;
1668 			tmp2.tm_isdst = (dst > 0 ? 0 : 1);
1669 			if ((t2 = xmktime(&tmp2)) == (time_t)-1) {
1670 				return (0);
1671 			}
1672 			if (tm_cmp(&tmp, &tmp2)) {
1673 				/*
1674 				 * same time/date found in the opposite zone.
1675 				 * check the clock to see which comes early.
1676 				 */
1677 				if (t2 > ref_t && t2 < t1) {
1678 					t1 = t2;
1679 				}
1680 			}
1681 		}
1682 		return (t1);
1683 	} else {
1684 		/*
1685 		 * mktime has set different time/date for the given date.
1686 		 * This means that the next job is scheduled to be run on the
1687 		 * invalid time. There are three possible invalid date/time.
1688 		 * 1. Non existing day of the month. such as April 31th.
1689 		 * 2. Feb 29th in the non-leap year.
1690 		 * 3. Time gap during the DST switch over.
1691 		 */
1692 		d1 = days_in_mon(mon, yr);
1693 		if ((mon != 1 && day > d1) || (mon == 1 && day > 29)) {
1694 			/*
1695 			 * see if we have got a specific date which
1696 			 * is invalid.
1697 			 */
1698 			if (strcmp(e->of.ct.dayweek, "*") == 0 &&
1699 			    mon == (next_ge((mon + 1)%12 + 1,
1700 			    e->of.ct.month) - 1) &&
1701 			    day <= next_ge(1, e->of.ct.daymon)) {
1702 				/* job never run */
1703 				return (0);
1704 			}
1705 			/*
1706 			 * Since the day has gone invalid, we need to go to
1707 			 * next month, and recalcuate the first occurrence.
1708 			 * eg the cron tab such as:
1709 			 * 0 0 1,15,31 1,2,3,4,5 * /usr/bin....
1710 			 * 2/31 is invalid, so the next job is 3/1.
1711 			 */
1712 			tmp2 = tmp;
1713 			tmp2.tm_min = 0;
1714 			tmp2.tm_hour = 0;
1715 			tmp2.tm_mday = 1; /* 1st day of the month */
1716 			if (mon == 11) {
1717 				tmp2.tm_mon = 0;
1718 				tmp2.tm_year = yr + 1;
1719 			} else {
1720 				tmp2.tm_mon = mon + 1;
1721 			}
1722 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
1723 				return (0);
1724 			}
1725 		} else if (mon == 1 && day > d1) {
1726 			/*
1727 			 * ie 29th in the non-leap year. Forwarding the
1728 			 * clock to Feb 29th 00:00 (March 1st), and recalculate
1729 			 * the next time.
1730 			 */
1731 			tmp2 = tmp;
1732 			tmp2.tm_min = 0;
1733 			tmp2.tm_hour = 0;
1734 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
1735 				return (0);
1736 			}
1737 		} else if (daylight) {
1738 			/*
1739 			 * Non existing time, eg 2am PST during summer time
1740 			 * switch.
1741 			 * We need to get the correct isdst which we are
1742 			 * swithing to, by adding time difference to make sure
1743 			 * that t2 is in the zone being switched.
1744 			 */
1745 			t2 = t1;
1746 			t2 += abs(timezone - altzone);
1747 			(void) localtime_r(&t2, &tmp2);
1748 			zone_start = get_switching_time(tmp2.tm_isdst,
1749 			    t1 - abs(timezone - altzone));
1750 			if (zone_start == (time_t)-1) {
1751 				return (0);
1752 			}
1753 			t = zone_start;
1754 		} else {
1755 			/*
1756 			 * This should never happen, but fall back to the
1757 			 * old code.
1758 			 */
1759 			days = days_btwn(tm->tm_mon,
1760 			    tm->tm_mday, tm->tm_year, mon, day, yr);
1761 			t += (time_t)(23-tm->tm_hour)*HOUR
1762 			    + (time_t)(60-tm->tm_min)*MINUTE
1763 			    + (time_t)hr*HOUR + (time_t)min*MINUTE
1764 			    + (time_t)days*DAY;
1765 			t1 = t;
1766 			t -= (time_t)tm->tm_sec;
1767 			(void) localtime_r(&t, &tmp);
1768 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
1769 				t -= (timezone - altzone);
1770 			return (t <= ref_t ? t1 : t);
1771 		}
1772 		goto recalc;
1773 	}
1774 	/*NOTREACHED*/
1775 }
1776 
1777 /*
1778  * This returns TOD in time_t that zone switch will happen, and this
1779  * will be called when clock fallback is about to happen.
1780  * (ie 30minutes before the time of PST -> PDT switch. 2:00 AM PST
1781  * will fall back to 1:00 PDT. So this function will be called only
1782  * for the time between 1:00 AM PST and 2:00 PST(1:00 PST)).
1783  * First goes through the common time differences to see if zone
1784  * switch happens at those minutes later. If not, check every minutes
1785  * until 6 hours ahead see if it happens(We might have 45minutes
1786  * fallback).
1787  */
1788 static time_t
1789 get_switching_time(int to_dst, time_t t_ref)
1790 {
1791 	time_t t, t1;
1792 	struct tm tmp, tmp1;
1793 	int hints[] = { 60, 120, 30, 90, 0}; /* minutes */
1794 	int i;
1795 
1796 	(void) localtime_r(&t_ref, &tmp);
1797 	tmp1 = tmp;
1798 	tmp1.tm_sec = 0;
1799 	tmp1.tm_min = 0;
1800 	if ((t = xmktime(&tmp1)) == (time_t)-1)
1801 		return ((time_t)-1);
1802 
1803 	/* fast path */
1804 	for (i = 0; hints[i] != 0; i++) {
1805 		t1 = t + hints[i] * 60;
1806 		(void) localtime_r(&t1, &tmp1);
1807 		if (tmp1.tm_isdst == to_dst) {
1808 			t1--;
1809 			(void) localtime_r(&t1, &tmp1);
1810 			if (tmp1.tm_isdst != to_dst) {
1811 				return (t1 + 1);
1812 			}
1813 		}
1814 	}
1815 
1816 	/* ugly, but don't know other than this. */
1817 	tmp1 = tmp;
1818 	tmp1.tm_sec = 0;
1819 	if ((t = xmktime(&tmp1)) == (time_t)-1)
1820 		return ((time_t)-1);
1821 	while (t < (t_ref + 6*60*60)) { /* 6 hours should be enough */
1822 		t += 60; /* at least one minute, I assume */
1823 		(void) localtime_r(&t, &tmp);
1824 		if (tmp.tm_isdst == to_dst)
1825 			return (t);
1826 	}
1827 	return ((time_t)-1);
1828 }
1829 
1830 static time_t
1831 xmktime(struct tm *tmp)
1832 {
1833 	time_t ret;
1834 
1835 	if ((ret = mktime(tmp)) == (time_t)-1) {
1836 		if (errno == EOVERFLOW) {
1837 			return ((time_t)-1);
1838 		}
1839 		crabort("internal error: mktime failed",
1840 		    REMOVE_FIFO|CONSOLE_MSG);
1841 	}
1842 	return (ret);
1843 }
1844 
1845 #define	DUMMY	100
1846 
1847 static int
1848 next_ge(int current, char *list)
1849 {
1850 	/*
1851 	 * list is a character field as in a crontab file;
1852 	 * for example: "40, 20, 50-10"
1853 	 * next_ge returns the next number in the list that is
1854 	 * greater than  or equal to current. if no numbers of list
1855 	 * are >= current, the smallest element of list is returned.
1856 	 * NOTE: current must be in the appropriate range.
1857 	 */
1858 
1859 	char *ptr;
1860 	int n, n2, min, min_gt;
1861 
1862 	if (strcmp(list, "*") == 0)
1863 		return (current);
1864 	ptr = list;
1865 	min = DUMMY;
1866 	min_gt = DUMMY;
1867 	for (;;) {
1868 		if ((n = (int)num(&ptr)) == current)
1869 			return (current);
1870 		if (n < min)
1871 			min = n;
1872 		if ((n > current) && (n < min_gt))
1873 			min_gt = n;
1874 		if (*ptr == '-') {
1875 			ptr++;
1876 			if ((n2 = (int)num(&ptr)) > n) {
1877 				if ((current > n) && (current <= n2))
1878 					return (current);
1879 			} else {	/* range that wraps around */
1880 				if (current > n)
1881 					return (current);
1882 				if (current <= n2)
1883 					return (current);
1884 			}
1885 		}
1886 		if (*ptr == '\0')
1887 			break;
1888 		ptr += 1;
1889 	}
1890 	if (min_gt != DUMMY)
1891 		return (min_gt);
1892 	else
1893 		return (min);
1894 }
1895 
1896 static void
1897 free_if_unused(struct usr *u)
1898 {
1899 	struct usr *cur, *prev;
1900 	/*
1901 	 *	To make sure a usr structure is idle we must check that
1902 	 *	there are no at jobs queued for the user; the user does
1903 	 *	not have a crontab, and also that there are no running at
1904 	 *	or cron jobs (since the runinfo structure also has a
1905 	 *	pointer to the usr structure).
1906 	 */
1907 	if (!u->ctexists && u->atevents == NULL &&
1908 	    u->cruncnt == 0 && u->aruncnt == 0) {
1909 #ifdef DEBUG
1910 		(void) fprintf(stderr, "%s removed from usr list\n", u->name);
1911 #endif
1912 		for (cur = uhead, prev = NULL;
1913 		    cur != u;
1914 		    prev = cur, cur = cur->nextusr) {
1915 			if (cur == NULL) {
1916 				return;
1917 			}
1918 		}
1919 
1920 		if (prev == NULL)
1921 			uhead = u->nextusr;
1922 		else
1923 			prev->nextusr = u->nextusr;
1924 		free(u->name);
1925 		free(u->home);
1926 		free(u);
1927 	}
1928 }
1929 
1930 static void
1931 del_atjob(char *name, char *usrname)
1932 {
1933 
1934 	struct	event	*e, *eprev;
1935 	struct	usr	*u;
1936 
1937 	if ((u = find_usr(usrname)) == NULL)
1938 		return;
1939 	e = u->atevents;
1940 	eprev = NULL;
1941 	while (e != NULL) {
1942 		if (strcmp(name, e->cmd) == 0) {
1943 			if (next_event == e)
1944 				next_event = NULL;
1945 			if (eprev == NULL)
1946 				u->atevents = e->link;
1947 			else
1948 				eprev->link = e->link;
1949 			el_remove(e->of.at.eventid, 1);
1950 			free(e->cmd);
1951 			free(e);
1952 			break;
1953 		} else {
1954 			eprev = e;
1955 			e = e->link;
1956 		}
1957 	}
1958 
1959 	free_if_unused(u);
1960 }
1961 
1962 static void
1963 del_ctab(char *name)
1964 {
1965 
1966 	struct	usr *u;
1967 
1968 	if ((u = find_usr(name)) == NULL)
1969 		return;
1970 	rm_ctevents(u);
1971 	el_remove(u->ctid, 0);
1972 	u->ctid = 0;
1973 	u->ctexists = 0;
1974 
1975 	free_if_unused(u);
1976 }
1977 
1978 static void
1979 rm_ctevents(struct usr *u)
1980 {
1981 	struct event *e2, *e3;
1982 
1983 	/*
1984 	 * see if the next event (to be run by cron) is a cronevent
1985 	 * owned by this user.
1986 	 */
1987 
1988 	if ((next_event != NULL) &&
1989 	    (next_event->etype == CRONEVENT) &&
1990 	    (next_event->u == u)) {
1991 		next_event = NULL;
1992 	}
1993 	e2 = u->ctevents;
1994 	while (e2 != NULL) {
1995 		free(e2->cmd);
1996 		free(e2->of.ct.minute);
1997 		free(e2->of.ct.hour);
1998 		free(e2->of.ct.daymon);
1999 		free(e2->of.ct.month);
2000 		free(e2->of.ct.dayweek);
2001 		if (e2->of.ct.input != NULL)
2002 			free(e2->of.ct.input);
2003 		e3 = e2->link;
2004 		free(e2);
2005 		e2 = e3;
2006 	}
2007 	u->ctevents = NULL;
2008 }
2009 
2010 
2011 static struct usr *
2012 find_usr(char *uname)
2013 {
2014 	struct usr *u;
2015 
2016 	u = uhead;
2017 	while (u != NULL) {
2018 		if (strcmp(u->name, uname) == 0)
2019 			return (u);
2020 		u = u->nextusr;
2021 	}
2022 	return (NULL);
2023 }
2024 
2025 /*
2026  * Execute cron command or at/batch job.
2027  * If ever a premature return is added to this function pay attention to
2028  * free at_cmdfile and outfile plus jobname buffers of the runinfo structure.
2029  */
2030 static int
2031 ex(struct event *e)
2032 {
2033 	int r;
2034 	int fd;
2035 	pid_t rfork;
2036 	FILE *atcmdfp;
2037 	char mailvar[4];
2038 	char *at_cmdfile = NULL;
2039 	struct stat buf;
2040 	struct queue *qp;
2041 	struct runinfo *rp;
2042 	struct project proj, *pproj = NULL;
2043 	char mybuf[PROJECT_BUFSZ];
2044 	char mybuf2[PROJECT_BUFSZ];
2045 	char *tmpfile;
2046 	FILE *fptr;
2047 	time_t dhltime;
2048 	projid_t projid;
2049 	int projflag = 0;
2050 
2051 	qp = &qt[e->etype];	/* set pointer to queue defs */
2052 	if (qp->nrun >= qp->njob) {
2053 		msg("%c queue max run limit reached", e->etype + 'a');
2054 		resched(qp->nwait);
2055 		return (0);
2056 	}
2057 	rp = rinfo_get(0); /* allocating a new runinfo struct */
2058 
2059 #ifdef ATLIMIT
2060 	if ((e->u)->uid != 0 && (e->u)->aruncnt >= ATLIMIT) {
2061 		msg("ATLIMIT (%d) reached for uid %d",
2062 		    ATLIMIT, (e->u)->uid);
2063 		rinfo_free(rp);
2064 		resched(qp->nwait);
2065 		return (0);
2066 	}
2067 #endif
2068 #ifdef CRONLIMIT
2069 	if ((e->u)->uid != 0 && (e->u)->cruncnt >= CRONLIMIT) {
2070 		msg("CRONLIMIT (%d) reached for uid %d",
2071 		    CRONLIMIT, (e->u)->uid);
2072 		rinfo_free(rp);
2073 		resched(qp->nwait);
2074 		return (0);
2075 	}
2076 #endif
2077 	if ((e->u)->uid == 0) {	/* set default path */
2078 		/* path settable in defaults file */
2079 		envinit[2] = supath;
2080 	} else {
2081 		envinit[2] = path;
2082 	}
2083 
2084 	/*
2085 	 * the tempnam() function uses malloc(3C) to allocate space for the
2086 	 * constructed file name, and returns a pointer to this area, which
2087 	 * is assigned to rp->outfile. Here rp->outfile is not overwritten.
2088 	 */
2089 
2090 	rp->outfile = tempnam(TMPDIR, PFX);
2091 	rp->jobtype = e->etype;
2092 	if (e->etype == CRONEVENT) {
2093 		rp->jobname = xmalloc(strlen(e->cmd) + 1);
2094 		(void) strcpy(rp->jobname, e->cmd);
2095 		/* "cron" jobs only produce mail if there's output */
2096 		rp->mailwhendone = 0;
2097 	} else {
2098 		at_cmdfile = xmalloc(strlen(ATDIR) + strlen(e->cmd) + 2);
2099 		(void) sprintf(at_cmdfile, "%s/%s", ATDIR, e->cmd);
2100 		if ((atcmdfp = fopen(at_cmdfile, "r")) == NULL) {
2101 			if (errno == ENAMETOOLONG) {
2102 				if (chdir(ATDIR) == 0)
2103 					cron_unlink(e->cmd);
2104 			} else {
2105 				cron_unlink(at_cmdfile);
2106 			}
2107 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECAT);
2108 			free(at_cmdfile);
2109 			rinfo_free(rp);
2110 			return (0);
2111 		}
2112 		rp->jobname = xmalloc(strlen(at_cmdfile) + 1);
2113 		(void) strcpy(rp->jobname, at_cmdfile);
2114 
2115 		/*
2116 		 * Skip over the first two lines.
2117 		 */
2118 		(void) fscanf(atcmdfp, "%*[^\n]\n");
2119 		(void) fscanf(atcmdfp, "%*[^\n]\n");
2120 		if (fscanf(atcmdfp, ": notify by mail: %3s%*[^\n]\n",
2121 		    mailvar) == 1) {
2122 			/*
2123 			 * Check to see if we should always send mail
2124 			 * to the owner.
2125 			 */
2126 			rp->mailwhendone = (strcmp(mailvar, "yes") == 0);
2127 		} else {
2128 			rp->mailwhendone = 0;
2129 		}
2130 
2131 		if (fscanf(atcmdfp, "\n: project: %d\n", &projid) == 1) {
2132 			projflag = 1;
2133 		}
2134 		(void) fclose(atcmdfp);
2135 	}
2136 
2137 	/*
2138 	 * we make sure that the system time
2139 	 * hasn't drifted backwards. if it has, el_add() is now
2140 	 * called, to make sure that the event queue is back in order,
2141 	 * and we set the delayed flag. cron will pick up the request
2142 	 * later on at the proper time.
2143 	 */
2144 	dhltime = time(NULL);
2145 	if ((dhltime - e->time) < 0) {
2146 		msg("clock time drifted backwards!\n");
2147 		if (next_event->etype == CRONEVENT) {
2148 			msg("correcting cron event\n");
2149 			next_event->time = next_time(next_event, dhltime);
2150 			el_add(next_event, next_event->time,
2151 			    (next_event->u)->ctid);
2152 		} else { /* etype == ATEVENT */
2153 			msg("correcting batch event\n");
2154 			el_add(next_event, next_event->time,
2155 			    next_event->of.at.eventid);
2156 		}
2157 		delayed++;
2158 		t_old = time(NULL);
2159 		free(at_cmdfile);
2160 		rinfo_free(rp);
2161 		return (0);
2162 	}
2163 
2164 	if ((rfork = fork()) == (pid_t)-1) {
2165 		reap_child();
2166 		if ((rfork = fork()) == (pid_t)-1) {
2167 			msg("cannot fork");
2168 			free(at_cmdfile);
2169 			rinfo_free(rp);
2170 			resched(60);
2171 			(void) sleep(30);
2172 			return (0);
2173 		}
2174 	}
2175 	if (rfork) {		/* parent process */
2176 		contract_abandon_latest(rfork);
2177 
2178 		++qp->nrun;
2179 		rp->pid = rfork;
2180 		rp->que = e->etype;
2181 		if (e->etype != CRONEVENT)
2182 			(e->u)->aruncnt++;
2183 		else
2184 			(e->u)->cruncnt++;
2185 		rp->rusr = (e->u);
2186 		logit(BCHAR, rp, 0);
2187 		free(at_cmdfile);
2188 
2189 		return (0);
2190 	}
2191 
2192 	child_sigreset();
2193 	contract_clear_template();
2194 
2195 	if (e->etype != CRONEVENT) {
2196 		/* open jobfile as stdin to shell */
2197 		if (stat(at_cmdfile, &buf)) {
2198 			if (errno == ENAMETOOLONG) {
2199 				if (chdir(ATDIR) == 0)
2200 					cron_unlink(e->cmd);
2201 			} else
2202 				cron_unlink(at_cmdfile);
2203 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
2204 			exit(1);
2205 		}
2206 		if (!(buf.st_mode&ISUID)) {
2207 			/*
2208 			 * if setuid bit off, original owner has
2209 			 * given this file to someone else
2210 			 */
2211 			cron_unlink(at_cmdfile);
2212 			exit(1);
2213 		}
2214 		if ((fd = open(at_cmdfile, O_RDONLY)) == -1) {
2215 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
2216 			cron_unlink(at_cmdfile);
2217 			exit(1);
2218 		}
2219 		if (fd != 0) {
2220 			(void) dup2(fd, 0);
2221 			(void) close(fd);
2222 		}
2223 		/*
2224 		 * retrieve the project id of the at job and convert it
2225 		 * to a project name.  fail if it's not a valid project
2226 		 * or if the user isn't a member of the project.
2227 		 */
2228 		if (projflag == 1) {
2229 			if ((pproj = getprojbyid(projid, &proj,
2230 			    (void *)&mybuf, sizeof (mybuf))) == NULL ||
2231 			    !inproj(e->u->name, pproj->pj_name,
2232 			    mybuf2, sizeof (mybuf2))) {
2233 				cron_unlink(at_cmdfile);
2234 				mail((e->u)->name, BADPROJID, ERR_CANTEXECAT);
2235 				exit(1);
2236 			}
2237 		}
2238 	}
2239 
2240 	/*
2241 	 * Put process in a new session, and create a new task.
2242 	 */
2243 	if (setsid() < 0) {
2244 		msg("setsid failed with errno = %d. job failed (%s)"
2245 		    " for user %s", errno, e->cmd, e->u->name);
2246 		if (e->etype != CRONEVENT)
2247 			cron_unlink(at_cmdfile);
2248 		exit(1);
2249 	}
2250 
2251 	/*
2252 	 * set correct user identification and check his account
2253 	 */
2254 	r = set_user_cred(e->u, pproj);
2255 	if (r == VUC_EXPIRED) {
2256 		msg("user (%s) account is expired", e->u->name);
2257 		audit_cron_user_acct_expired(e->u->name);
2258 		clean_out_user(e->u);
2259 		exit(1);
2260 	}
2261 	if (r == VUC_NEW_AUTH) {
2262 		msg("user (%s) password has expired", e->u->name);
2263 		audit_cron_user_acct_expired(e->u->name);
2264 		clean_out_user(e->u);
2265 		exit(1);
2266 	}
2267 	if (r != VUC_OK) {
2268 		msg("bad user (%s)", e->u->name);
2269 		audit_cron_bad_user(e->u->name);
2270 		clean_out_user(e->u);
2271 		exit(1);
2272 	}
2273 	/*
2274 	 * check user and initialize the supplementary group access list.
2275 	 * bugid 1230784: deleted from parent to avoid cron hang. Now
2276 	 * only child handles the call.
2277 	 */
2278 
2279 	if (verify_user_cred(e->u) != VUC_OK ||
2280 	    setgid(e->u->gid) == -1 ||
2281 	    initgroups(e->u->name, e->u->gid) == -1) {
2282 		msg("bad user (%s) or setgid failed (%s)",
2283 		    e->u->name, e->u->name);
2284 		audit_cron_bad_user(e->u->name);
2285 		clean_out_user(e->u);
2286 		exit(1);
2287 	}
2288 
2289 	if (e->etype != CRONEVENT) {
2290 		r = audit_cron_session(e->u->name, NULL,
2291 		    e->u->uid, e->u->gid, at_cmdfile);
2292 		cron_unlink(at_cmdfile);
2293 	} else {
2294 		r = audit_cron_session(e->u->name, CRONDIR,
2295 		    e->u->uid, e->u->gid, NULL);
2296 	}
2297 	if (r != 0) {
2298 		msg("cron audit problem. job failed (%s) for user %s",
2299 		    e->cmd, e->u->name);
2300 		exit(1);
2301 	}
2302 
2303 	audit_cron_new_job(e->cmd, e->etype, (void *)e);
2304 
2305 	if (setuid(e->u->uid) == -1)  {
2306 		msg("setuid failed (%s)", e->u->name);
2307 		clean_out_user(e->u);
2308 		exit(1);
2309 	}
2310 
2311 	if (e->etype == CRONEVENT) {
2312 		/* check for standard input to command	*/
2313 		if (e->of.ct.input != NULL) {
2314 			if ((tmpfile = strdup(TMPINFILE)) == NULL) {
2315 				mail((e->u)->name, MALLOCERR,
2316 				    ERR_CANTEXECCRON);
2317 				exit(1);
2318 			}
2319 			if ((fd = mkstemp(tmpfile)) == -1 ||
2320 			    (fptr = fdopen(fd, "w")) == NULL) {
2321 				mail((e->u)->name, NOSTDIN,
2322 				    ERR_CANTEXECCRON);
2323 				cron_unlink(tmpfile);
2324 				free(tmpfile);
2325 				exit(1);
2326 			}
2327 			if ((fwrite(e->of.ct.input, sizeof (char),
2328 			    strlen(e->of.ct.input), fptr)) !=
2329 			    strlen(e->of.ct.input)) {
2330 				mail((e->u)->name, NOSTDIN, ERR_CANTEXECCRON);
2331 				cron_unlink(tmpfile);
2332 				free(tmpfile);
2333 				(void) close(fd);
2334 				(void) fclose(fptr);
2335 				exit(1);
2336 			}
2337 			if (fseek(fptr, (off_t)0, SEEK_SET) != -1) {
2338 				if (fd != 0) {
2339 					(void) dup2(fd, 0);
2340 					(void) close(fd);
2341 				}
2342 			}
2343 			cron_unlink(tmpfile);
2344 			free(tmpfile);
2345 			(void) fclose(fptr);
2346 		} else if ((fd = open("/dev/null", O_RDONLY)) > 0) {
2347 			(void) dup2(fd, 0);
2348 			(void) close(fd);
2349 		}
2350 	}
2351 
2352 	/* redirect stdout and stderr for the shell	*/
2353 	if ((fd = open(rp->outfile, O_WRONLY|O_CREAT|O_EXCL, OUTMODE)) == 1)
2354 		fd = open("/dev/null", O_WRONLY);
2355 
2356 	if (fd >= 0 && fd != 1)
2357 		(void) dup2(fd, 1);
2358 
2359 	if (fd >= 0 && fd != 2) {
2360 		(void) dup2(fd, 2);
2361 		if (fd != 1)
2362 			(void) close(fd);
2363 	}
2364 
2365 	(void) strlcat(homedir, (e->u)->home, sizeof (homedir));
2366 	(void) strlcat(logname, (e->u)->name, sizeof (logname));
2367 	environ = envinit;
2368 	if (chdir((e->u)->home) == -1) {
2369 		mail((e->u)->name, CANTCDHOME,
2370 		    e->etype == CRONEVENT ? ERR_CANTEXECCRON :
2371 		    ERR_CANTEXECAT);
2372 		exit(1);
2373 	}
2374 #ifdef TESTING
2375 	exit(1);
2376 #endif
2377 	/*
2378 	 * make sure that all file descriptors EXCEPT 0, 1 and 2
2379 	 * will be closed.
2380 	 */
2381 	closefrom(3);
2382 
2383 	if ((e->u)->uid != 0)
2384 		(void) nice(qp->nice);
2385 	if (e->etype == CRONEVENT)
2386 		(void) execl(SHELL, "sh", "-c", e->cmd, 0);
2387 	else		/* type == ATEVENT */
2388 		(void) execl(SHELL, "sh", 0);
2389 	mail((e->u)->name, CANTEXECSH,
2390 	    e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT);
2391 	exit(1);
2392 	/*NOTREACHED*/
2393 }
2394 
2395 static int
2396 idle(long t)
2397 {
2398 	time_t	now;
2399 
2400 	while (t > 0L) {
2401 
2402 		if (msg_wait(t) != 0) {
2403 			/* we need to run next job immediately */
2404 			return (0);
2405 		}
2406 
2407 		reap_child();
2408 
2409 		now = time(NULL);
2410 		if (last_time > now) {
2411 			/* clock has been reset */
2412 			return (1);
2413 		}
2414 
2415 		if (next_event == NULL && !el_empty()) {
2416 			next_event = (struct event *)el_first();
2417 		}
2418 		if (next_event == NULL)
2419 			t = INFINITY;
2420 		else
2421 			t = (long)next_event->time - now;
2422 	}
2423 	return (0);
2424 }
2425 
2426 /*
2427  * This used to be in the idle(), but moved to the separate function.
2428  * This called from various place when cron needs to reap the
2429  * child. It includes the situation that cron hit maxrun, and needs
2430  * to reschedule the job.
2431  */
2432 static void
2433 reap_child()
2434 {
2435 	pid_t	pid;
2436 	int	prc;
2437 	struct	runinfo	*rp;
2438 
2439 	for (;;) {
2440 		pid = waitpid((pid_t)-1, &prc, WNOHANG);
2441 		if (pid <= 0)
2442 			break;
2443 #ifdef DEBUG
2444 		fprintf(stderr,
2445 		    "wait returned %x for process %d\n", prc, pid);
2446 #endif
2447 		if ((rp = rinfo_get(pid)) == NULL) {
2448 			if (miscpid_delete(pid) == 0) {
2449 				/* not found in anywhere */
2450 				msg(PIDERR, pid);
2451 			}
2452 		} else if (rp->que == ZOMB) {
2453 			(void) unlink(rp->outfile);
2454 			rinfo_free(rp);
2455 		} else {
2456 			cleanup(rp, prc);
2457 		}
2458 	}
2459 }
2460 
2461 static void
2462 cleanup(struct runinfo *pr, int rc)
2463 {
2464 	int	nextfork = 1;
2465 	struct	usr	*p;
2466 	struct	stat	buf;
2467 
2468 	logit(ECHAR, pr, rc);
2469 	--qt[pr->que].nrun;
2470 	p = pr->rusr;
2471 	if (pr->que != CRONEVENT)
2472 		--p->aruncnt;
2473 	else
2474 		--p->cruncnt;
2475 
2476 	if (lstat(pr->outfile, &buf) == 0) {
2477 		if (!S_ISLNK(buf.st_mode) &&
2478 		    (buf.st_size > 0 || pr->mailwhendone)) {
2479 			/* mail user stdout and stderr */
2480 			for (;;) {
2481 				if ((pr->pid = fork()) < 0) {
2482 					/*
2483 					 * if fork fails try forever in doubling
2484 					 * retry times, up to 16 seconds
2485 					 */
2486 					(void) sleep(nextfork);
2487 					if (nextfork < 16)
2488 						nextfork += nextfork;
2489 					continue;
2490 				} else if (pr->pid == 0) {
2491 					child_sigreset();
2492 					contract_clear_template();
2493 
2494 					mail_result(p, pr, buf.st_size);
2495 					/* NOTREACHED */
2496 				} else {
2497 					contract_abandon_latest(pr->pid);
2498 					pr->que = ZOMB;
2499 					break;
2500 				}
2501 			}
2502 		} else {
2503 			(void) unlink(pr->outfile);
2504 			rinfo_free(pr);
2505 		}
2506 	} else {
2507 		rinfo_free(pr);
2508 	}
2509 
2510 	free_if_unused(p);
2511 }
2512 
2513 /*
2514  * Mail stdout and stderr of a job to user. Get uid for real user and become
2515  * that person. We do this so that mail won't come from root since this
2516  * could be a security hole. If failure, quit - don't send mail as root.
2517  */
2518 static void
2519 mail_result(struct usr *p, struct runinfo *pr, size_t filesize)
2520 {
2521 	struct	passwd	*ruser_ids;
2522 	FILE	*mailpipe;
2523 	FILE	*st;
2524 	struct utsname	name;
2525 	int	nbytes;
2526 	char	iobuf[BUFSIZ];
2527 	char	*cmd;
2528 
2529 	(void) uname(&name);
2530 	if ((ruser_ids = getpwnam(p->name)) == NULL)
2531 		exit(0);
2532 	(void) setuid(ruser_ids->pw_uid);
2533 
2534 	cmd = xmalloc(strlen(MAIL) + strlen(p->name)+2);
2535 	(void) sprintf(cmd, "%s %s", MAIL, p->name);
2536 	mailpipe = popen(cmd, "w");
2537 	free(cmd);
2538 	if (mailpipe == NULL)
2539 		exit(127);
2540 	(void) fprintf(mailpipe, "To: %s\n", p->name);
2541 	if (pr->jobtype == CRONEVENT) {
2542 		(void) fprintf(mailpipe, CRONOUT);
2543 		(void) fprintf(mailpipe, "Your \"cron\" job on %s\n",
2544 		    name.nodename);
2545 		if (pr->jobname != NULL) {
2546 			(void) fprintf(mailpipe, "%s\n\n", pr->jobname);
2547 		}
2548 	} else {
2549 		(void) fprintf(mailpipe, "Subject: Output from \"at\" job\n\n");
2550 		(void) fprintf(mailpipe, "Your \"at\" job on %s\n",
2551 		    name.nodename);
2552 		if (pr->jobname != NULL) {
2553 			(void) fprintf(mailpipe, "\"%s\"\n\n", pr->jobname);
2554 		}
2555 	}
2556 	/* Tmp. file is fopen'ed w/ "r",  secure open */
2557 	if (filesize > 0 &&
2558 	    (st = fopen(pr->outfile, "r")) != NULL) {
2559 		(void) fprintf(mailpipe,
2560 		    "produced the following output:\n\n");
2561 		while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0)
2562 			(void) fwrite(iobuf, sizeof (char), nbytes, mailpipe);
2563 		(void) fclose(st);
2564 	} else {
2565 		(void) fprintf(mailpipe, "completed.\n");
2566 	}
2567 	(void) pclose(mailpipe);
2568 	exit(0);
2569 }
2570 
2571 static int
2572 msg_wait(long tim)
2573 {
2574 	struct	message	msg;
2575 	int	cnt;
2576 	time_t	reftime;
2577 	struct	pollfd pfd[2];
2578 	int64_t	tl;
2579 	int	timeout;
2580 	static int	pending_msg;
2581 	static time_t	pending_reftime;
2582 
2583 	if (pending_msg) {
2584 		process_msg(&msgbuf, pending_reftime);
2585 		pending_msg = 0;
2586 		return (0);
2587 	}
2588 
2589 	/*
2590 	 * We are opening the signal mask to receive SIGCLD. The notifypipe
2591 	 * is used to avoid race condition between SIGCLD and poll system
2592 	 * call.
2593 	 * If SIGCLD is delivered in poll(), poll will be interrupted, and
2594 	 * we will return to idle() to reap the dead children.
2595 	 * If SIGCLD is delivered between sigprocmask() below and poll(),
2596 	 * there is no way we can detect the SIGCLD because poll() won't
2597 	 * be interrupted. In such case, the dead children can't be wait'ed
2598 	 * until poll returns by timeout or a new job. To avoid this race
2599 	 * condition, child_handler write to the notifypipe, so that
2600 	 * poll() will be able to return with POLLIN which indicates that
2601 	 * we have received SIGCLD.
2602 	 *
2603 	 * Since the notifypipe is used to just let poll return from
2604 	 * system call, the data in the pipe won't be read. Therefore,
2605 	 * any data in the pipe needs to be flushed before opening signal
2606 	 * mask.
2607 	 *
2608 	 * Note that we can probably re-write this code with pselect()
2609 	 * which can handle this situation easily.
2610 	 */
2611 	(void) ioctl(notifypipe[0], I_FLUSH, FLUSHW);
2612 
2613 	pfd[0].fd = msgfd;
2614 	pfd[0].events = POLLIN;
2615 	pfd[1].fd = notifypipe[1];
2616 	pfd[1].events = POLLIN;
2617 
2618 #ifdef CRON_MAXSLEEP
2619 	/*
2620 	 * CRON_MAXSLEEP can be defined to have cron periodically wake
2621 	 * up, so that cron can detect a change of TOD and adjust the
2622 	 * sleep time accordingly.
2623 	 */
2624 	tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim;
2625 #endif
2626 	tl = (tim == INFINITY) ? -1ll : (int64_t)tim * 1000;
2627 
2628 	accept_sigcld = 1;
2629 	(void) sigprocmask(SIG_UNBLOCK, &childmask, NULL);
2630 	do {
2631 		timeout = (tl > INT_MAX ? INT_MAX : (int)tl);
2632 		tl -= timeout;
2633 		cnt = poll(pfd, 2, timeout);
2634 		if (cnt == -1 && errno != EINTR) {
2635 			perror("! poll");
2636 		}
2637 	} while (tl > 0 && cnt == 0);
2638 	(void) sigprocmask(SIG_BLOCK, &childmask, NULL);
2639 	accept_sigcld = 0;
2640 
2641 	/*
2642 	 * poll timeout or interrupted.
2643 	 */
2644 	if (cnt <= 0)
2645 		return (0);
2646 
2647 	/*
2648 	 * Not the timeout or new job, but a SIGCLD has been delivered.
2649 	 */
2650 	if ((pfd[0].revents & POLLIN) == 0)
2651 		return (0);
2652 
2653 	errno = 0;
2654 	if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) {
2655 		if (cnt != -1 || errno != EAGAIN)
2656 			perror("! read");
2657 		return (0);
2658 	}
2659 	reftime = time(NULL);
2660 	if (next_event != NULL && reftime >= next_event->time) {
2661 		/*
2662 		 * we need to run the job before reloading crontab.
2663 		 */
2664 		(void) memcpy(&msgbuf, &msg, sizeof (msg));
2665 		pending_msg = 1;
2666 		pending_reftime = reftime;
2667 		return (1);
2668 	}
2669 	process_msg(&msg, reftime);
2670 	return (0);
2671 }
2672 
2673 /*
2674  * process the message supplied via pipe. This will be called either
2675  * immediately after cron read the message from pipe, or idle time
2676  * if the message was pending due to the job execution.
2677  */
2678 static void
2679 process_msg(struct message *pmsg, time_t reftime)
2680 {
2681 	if (pmsg->etype == NULL)
2682 		return;
2683 
2684 	switch (pmsg->etype) {
2685 	case AT:
2686 		if (pmsg->action == DELETE)
2687 			del_atjob(pmsg->fname, pmsg->logname);
2688 		else
2689 			mod_atjob(pmsg->fname, (time_t)0);
2690 		break;
2691 	case CRON:
2692 		if (pmsg->action == DELETE)
2693 			del_ctab(pmsg->fname);
2694 		else
2695 			mod_ctab(pmsg->fname, reftime);
2696 		break;
2697 	default:
2698 		msg("message received - bad format");
2699 		break;
2700 	}
2701 	if (next_event != NULL) {
2702 		if (next_event->etype == CRONEVENT)
2703 			el_add(next_event, next_event->time,
2704 			    (next_event->u)->ctid);
2705 		else	/* etype == ATEVENT */
2706 			el_add(next_event, next_event->time,
2707 			    next_event->of.at.eventid);
2708 		next_event = NULL;
2709 	}
2710 	(void) fflush(stdout);
2711 	pmsg->etype = NULL;
2712 }
2713 
2714 /*
2715  * Allocate a new or find an existing runinfo structure
2716  */
2717 static struct runinfo *
2718 rinfo_get(pid_t pid)
2719 {
2720 	struct runinfo *rp;
2721 
2722 	if (pid == 0) {		/* allocate a new entry */
2723 		rp = xcalloc(1, sizeof (struct runinfo));
2724 		rp->next = rthead;	/* link the entry into the list */
2725 		rthead = rp;
2726 		return (rp);
2727 	}
2728 	/* search the list for an existing entry */
2729 	for (rp = rthead; rp != NULL; rp = rp->next) {
2730 		if (rp->pid == pid)
2731 			break;
2732 	}
2733 	return (rp);
2734 }
2735 
2736 /*
2737  * Free a runinfo structure and its associated memory
2738  */
2739 static void
2740 rinfo_free(struct runinfo *entry)
2741 {
2742 	struct runinfo **rpp;
2743 	struct runinfo *rp;
2744 
2745 #ifdef DEBUG
2746 	(void) fprintf(stderr, "freeing job %s\n", entry->jobname);
2747 #endif
2748 	for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) {
2749 		if (rp == entry) {
2750 			*rpp = rp->next;	/* unlink the entry */
2751 			free(rp->outfile);
2752 			free(rp->jobname);
2753 			free(rp);
2754 			break;
2755 		}
2756 	}
2757 }
2758 
2759 /* ARGSUSED */
2760 static void
2761 thaw_handler(int sig)
2762 {
2763 	;
2764 }
2765 
2766 
2767 /* ARGSUSED */
2768 static void
2769 cronend(int sig)
2770 {
2771 	crabort("SIGTERM", REMOVE_FIFO);
2772 }
2773 
2774 /*ARGSUSED*/
2775 static void
2776 child_handler(int sig)
2777 {
2778 	/*
2779 	 * Just in case someone changes the signal mask.
2780 	 * we don't want to notify the SIGCLD.
2781 	 */
2782 	if (accept_sigcld) {
2783 		(void) write(notifypipe[0], &sig, 1);
2784 	}
2785 }
2786 
2787 static void
2788 child_sigreset(void)
2789 {
2790 	(void) signal(SIGCLD, SIG_DFL);
2791 	(void) sigprocmask(SIG_SETMASK, &defmask, NULL);
2792 }
2793 
2794 /*
2795  * crabort() - handle exits out of cron
2796  */
2797 static void
2798 crabort(char *mssg, int action)
2799 {
2800 	int	c;
2801 
2802 	if (action & REMOVE_FIFO) {
2803 		/* FIFO vanishes when cron finishes */
2804 		if (unlink(FIFO) < 0)
2805 			perror("cron could not unlink FIFO");
2806 	}
2807 
2808 	if (action & CONSOLE_MSG) {
2809 		/* write error msg to console */
2810 		if ((c = open(CONSOLE, O_WRONLY)) >= 0) {
2811 			(void) write(c, "cron aborted: ", 14);
2812 			(void) write(c, mssg, strlen(mssg));
2813 			(void) write(c, "\n", 1);
2814 			(void) close(c);
2815 		}
2816 	}
2817 
2818 	/* always log the message */
2819 	msg(mssg);
2820 	msg("******* CRON ABORTED ********");
2821 	exit(1);
2822 }
2823 
2824 /*
2825  * msg() - time-stamped error reporting function
2826  */
2827 /*PRINTFLIKE1*/
2828 static void
2829 msg(char *fmt, ...)
2830 {
2831 	va_list args;
2832 	time_t	t;
2833 
2834 	t = time(NULL);
2835 
2836 	(void) fflush(stdout);
2837 
2838 	(void) fprintf(stderr, "! ");
2839 
2840 	va_start(args, fmt);
2841 	(void) vfprintf(stderr, fmt, args);
2842 	va_end(args);
2843 
2844 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
2845 	(void) fprintf(stderr, " %s\n", timebuf);
2846 
2847 	(void) fflush(stderr);
2848 }
2849 
2850 static void
2851 logit(int cc, struct runinfo *rp, int rc)
2852 {
2853 	time_t t;
2854 	int    ret;
2855 
2856 	if (!log)
2857 		return;
2858 
2859 	t = time(NULL);
2860 	if (cc == BCHAR)
2861 		(void) printf("%c  CMD: %s\n", cc, next_event->cmd);
2862 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
2863 	(void) printf("%c  %.8s %u %c %s",
2864 	    cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf);
2865 	if ((ret = TSTAT(rc)) != 0)
2866 		(void) printf(" ts=%d", ret);
2867 	if ((ret = RCODE(rc)) != 0)
2868 		(void) printf(" rc=%d", ret);
2869 	(void) putchar('\n');
2870 	(void) fflush(stdout);
2871 }
2872 
2873 static void
2874 resched(int delay)
2875 {
2876 	time_t	nt;
2877 
2878 	/* run job at a later time */
2879 	nt = next_event->time + delay;
2880 	if (next_event->etype == CRONEVENT) {
2881 		next_event->time = next_time(next_event, (time_t)0);
2882 		if (nt < next_event->time)
2883 			next_event->time = nt;
2884 		el_add(next_event, next_event->time, (next_event->u)->ctid);
2885 		delayed = 1;
2886 		msg("rescheduling a cron job");
2887 		return;
2888 	}
2889 	add_atevent(next_event->u, next_event->cmd, nt, next_event->etype);
2890 	msg("rescheduling at job");
2891 }
2892 
2893 static void
2894 quedefs(int action)
2895 {
2896 	int	i;
2897 	int	j;
2898 	char	qbuf[QBUFSIZ];
2899 	FILE	*fd;
2900 
2901 	/* set up default queue definitions */
2902 	for (i = 0; i < NQUEUE; i++) {
2903 		qt[i].njob = qd.njob;
2904 		qt[i].nice = qd.nice;
2905 		qt[i].nwait = qd.nwait;
2906 	}
2907 	if (action == DEFAULT)
2908 		return;
2909 	if ((fd = fopen(QUEDEFS, "r")) == NULL) {
2910 		msg("cannot open quedefs file");
2911 		msg("using default queue definitions");
2912 		return;
2913 	}
2914 	while (fgets(qbuf, QBUFSIZ, fd) != NULL) {
2915 		if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.')
2916 			continue;
2917 		parsqdef(&qbuf[2]);
2918 		qt[j].njob = qq.njob;
2919 		qt[j].nice = qq.nice;
2920 		qt[j].nwait = qq.nwait;
2921 	}
2922 	(void) fclose(fd);
2923 }
2924 
2925 static void
2926 parsqdef(char *name)
2927 {
2928 	int i;
2929 
2930 	qq = qd;
2931 	while (*name) {
2932 		i = 0;
2933 		while (isdigit(*name)) {
2934 			i *= 10;
2935 			i += *name++ - '0';
2936 		}
2937 		switch (*name++) {
2938 		case JOBF:
2939 			qq.njob = i;
2940 			break;
2941 		case NICEF:
2942 			qq.nice = i;
2943 			break;
2944 		case WAITF:
2945 			qq.nwait = i;
2946 			break;
2947 		}
2948 	}
2949 }
2950 
2951 /*
2952  * defaults - read defaults from /etc/default/cron
2953  */
2954 static void
2955 defaults()
2956 {
2957 	int  flags;
2958 	char *deflog;
2959 	char *hz, *tz;
2960 
2961 	/*
2962 	 * get HZ value for environment
2963 	 */
2964 	if ((hz = getenv("HZ")) == (char *)NULL)
2965 		(void) sprintf(hzname, "HZ=%d", HZ);
2966 	else
2967 		(void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz);
2968 	/*
2969 	 * get TZ value for environment
2970 	 */
2971 	(void) snprintf(tzone, sizeof (tzone), "TZ=%s",
2972 	    ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ);
2973 
2974 	if (defopen(DEFFILE) == 0) {
2975 		/* ignore case */
2976 		flags = defcntl(DC_GETFLAGS, 0);
2977 		TURNOFF(flags, DC_CASE);
2978 		(void) defcntl(DC_SETFLAGS, flags);
2979 
2980 		if (((deflog = defread("CRONLOG=")) == NULL) ||
2981 		    (*deflog == 'N') || (*deflog == 'n'))
2982 			log = 0;
2983 		else
2984 			log = 1;
2985 		/* fix for 1087611 - allow paths to be set in defaults file */
2986 		if ((Def_path = defread("PATH=")) != NULL) {
2987 			(void) strlcat(path, Def_path, LINE_MAX);
2988 		} else {
2989 			(void) strlcpy(path, NONROOTPATH, LINE_MAX);
2990 		}
2991 		if ((Def_supath = defread("SUPATH=")) != NULL) {
2992 			(void) strlcat(supath, Def_supath, LINE_MAX);
2993 		} else {
2994 			(void) strlcpy(supath, ROOTPATH, LINE_MAX);
2995 		}
2996 		(void) defopen(NULL);
2997 	}
2998 }
2999 
3000 /*
3001  * Determine if a user entry for a job is still ok.  The method used here
3002  * is a lot (about 75x) faster than using setgrent() / getgrent()
3003  * endgrent().  It should be safe because we use the sysconf to determine
3004  * the max, and it tolerates the max being 0.
3005  */
3006 
3007 static int
3008 verify_user_cred(struct usr *u)
3009 {
3010 	struct passwd *pw;
3011 	size_t numUsrGrps = 0;
3012 	size_t numOrigGrps = 0;
3013 	size_t i;
3014 	int retval;
3015 
3016 	/*
3017 	 * Maximum number of groups a user may be in concurrently.  This
3018 	 * is a value which we obtain at runtime through a sysconf()
3019 	 * call.
3020 	 */
3021 
3022 	static size_t nGroupsMax = (size_t)-1;
3023 
3024 	/*
3025 	 * Arrays for cron user's group list, constructed at startup to
3026 	 * be nGroupsMax elements long, used for verifying user
3027 	 * credentials prior to execution.
3028 	 */
3029 
3030 	static gid_t *UsrGrps;
3031 	static gid_t *OrigGrps;
3032 
3033 	if ((pw = getpwnam(u->name)) == NULL)
3034 		return (VUC_BADUSER);
3035 	if (u->home != NULL) {
3036 		if (strcmp(u->home, pw->pw_dir) != 0) {
3037 			free(u->home);
3038 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
3039 			(void) strcpy(u->home, pw->pw_dir);
3040 		}
3041 	} else {
3042 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
3043 		(void) strcpy(u->home, pw->pw_dir);
3044 	}
3045 	if (u->uid != pw->pw_uid)
3046 		u->uid = pw->pw_uid;
3047 	if (u->gid != pw->pw_gid)
3048 		u->gid  = pw->pw_gid;
3049 
3050 	/*
3051 	 * Create the group id lists needed for job credential
3052 	 * verification.
3053 	 */
3054 
3055 	if (nGroupsMax == (size_t)-1) {
3056 		if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) {
3057 			UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t));
3058 			OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t));
3059 		}
3060 
3061 #ifdef DEBUG
3062 		(void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax);
3063 #endif
3064 	}
3065 
3066 #ifdef DEBUG
3067 	(void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name,
3068 	    pw->pw_uid);
3069 	(void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, "
3070 	    "u->gid = %d\n", pw->pw_gid, u->gid);
3071 #endif
3072 
3073 	retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP;
3074 
3075 	if (nGroupsMax > 0) {
3076 		numOrigGrps = getgroups(nGroupsMax, OrigGrps);
3077 
3078 		(void) initgroups(pw->pw_name, pw->pw_gid);
3079 		numUsrGrps = getgroups(nGroupsMax, UsrGrps);
3080 
3081 		for (i = 0; i < numUsrGrps; i++) {
3082 			if (UsrGrps[i] == u->gid) {
3083 				retval = VUC_OK;
3084 				break;
3085 			}
3086 		}
3087 
3088 		if (OrigGrps) {
3089 			(void) setgroups(numOrigGrps, OrigGrps);
3090 		}
3091 	}
3092 
3093 #ifdef DEBUG
3094 	(void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval);
3095 #endif
3096 
3097 	return (retval);
3098 }
3099 
3100 static int
3101 set_user_cred(const struct usr *u, struct project *pproj)
3102 {
3103 	static char *progname = "cron";
3104 	int r = 0, rval = 0;
3105 
3106 	if ((r = pam_start(progname, u->name, &pam_conv, &pamh))
3107 	    != PAM_SUCCESS) {
3108 #ifdef DEBUG
3109 		msg("pam_start returns %d\n", r);
3110 #endif
3111 		rval = VUC_BADUSER;
3112 		goto set_eser_cred_exit;
3113 	}
3114 
3115 	r = pam_acct_mgmt(pamh, 0);
3116 #ifdef DEBUG
3117 	msg("pam_acc_mgmt returns %d\n", r);
3118 #endif
3119 	if (r == PAM_ACCT_EXPIRED) {
3120 		rval = VUC_EXPIRED;
3121 		goto set_eser_cred_exit;
3122 	}
3123 	if (r == PAM_NEW_AUTHTOK_REQD) {
3124 		rval = VUC_NEW_AUTH;
3125 		goto set_eser_cred_exit;
3126 	}
3127 	if (r != PAM_SUCCESS) {
3128 		rval = VUC_BADUSER;
3129 		goto set_eser_cred_exit;
3130 	}
3131 
3132 	if (pproj != NULL) {
3133 		size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name);
3134 		char *buf = alloca(sz);
3135 
3136 		(void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name);
3137 		(void) pam_set_item(pamh, PAM_RESOURCE, buf);
3138 	}
3139 
3140 	r = pam_setcred(pamh, PAM_ESTABLISH_CRED);
3141 	if (r != PAM_SUCCESS)
3142 		rval = VUC_BADUSER;
3143 
3144 set_eser_cred_exit:
3145 	(void) pam_end(pamh, r);
3146 	return (rval);
3147 }
3148 
3149 static void
3150 clean_out_user(struct usr *u)
3151 {
3152 	if (next_event->u == u) {
3153 		next_event = NULL;
3154 	}
3155 
3156 	clean_out_ctab(u);
3157 	clean_out_atjobs(u);
3158 	free_if_unused(u);
3159 }
3160 
3161 static void
3162 clean_out_atjobs(struct usr *u)
3163 {
3164 	struct event *ev, *pv;
3165 
3166 	for (pv = NULL, ev = u->atevents;
3167 	    ev != NULL;
3168 	    pv = ev, ev = ev->link, free(pv)) {
3169 		el_remove(ev->of.at.eventid, 1);
3170 		if (cwd == AT)
3171 			cron_unlink(ev->cmd);
3172 		else {
3173 			char buf[PATH_MAX];
3174 			if (strlen(ATDIR) + strlen(ev->cmd) + 2
3175 			    < PATH_MAX) {
3176 				(void) sprintf(buf, "%s/%s", ATDIR, ev->cmd);
3177 				cron_unlink(buf);
3178 			}
3179 		}
3180 		free(ev->cmd);
3181 	}
3182 
3183 	u->atevents = NULL;
3184 }
3185 
3186 static void
3187 clean_out_ctab(struct usr *u)
3188 {
3189 	rm_ctevents(u);
3190 	el_remove(u->ctid, 0);
3191 	u->ctid = 0;
3192 	u->ctexists = 0;
3193 }
3194 
3195 static void
3196 cron_unlink(char *name)
3197 {
3198 	int r;
3199 
3200 	r = unlink(name);
3201 	if (r == 0 || (r == -1 && errno == ENOENT)) {
3202 		(void) audit_cron_delete_anc_file(name, NULL);
3203 	}
3204 }
3205 
3206 static void
3207 create_anc_ctab(struct event *e)
3208 {
3209 	if (audit_cron_create_anc_file(e->u->name,
3210 	    (cwd == CRON) ? NULL:CRONDIR,
3211 	    e->u->name, e->u->uid) == -1) {
3212 		process_anc_files(CRON_ANC_DELETE);
3213 		crabort("cannot create ancillary files for crontabs",
3214 		    REMOVE_FIFO|CONSOLE_MSG);
3215 	}
3216 }
3217 
3218 static void
3219 delete_anc_ctab(struct event *e)
3220 {
3221 	(void) audit_cron_delete_anc_file(e->u->name,
3222 	    (cwd == CRON) ? NULL:CRONDIR);
3223 }
3224 
3225 static void
3226 create_anc_atjob(struct event *e)
3227 {
3228 	if (!e->of.at.exists)
3229 		return;
3230 
3231 	if (audit_cron_create_anc_file(e->cmd,
3232 	    (cwd == AT) ? NULL:ATDIR,
3233 	    e->u->name, e->u->uid) == -1) {
3234 		process_anc_files(CRON_ANC_DELETE);
3235 		crabort("cannot create ancillary files for atjobs",
3236 		    REMOVE_FIFO|CONSOLE_MSG);
3237 	}
3238 }
3239 
3240 static void
3241 delete_anc_atjob(struct event *e)
3242 {
3243 	if (!e->of.at.exists)
3244 		return;
3245 
3246 	(void) audit_cron_delete_anc_file(e->cmd,
3247 	    (cwd == AT) ? NULL:ATDIR);
3248 }
3249 
3250 
3251 static void
3252 process_anc_files(int del)
3253 {
3254 	struct usr	*u = uhead;
3255 	struct event	*e;
3256 
3257 	if (!audit_cron_mode())
3258 		return;
3259 
3260 	for (;;) {
3261 		if (u->ctexists && u->ctevents != NULL) {
3262 			e = u->ctevents;
3263 			for (;;) {
3264 				if (del)
3265 					delete_anc_ctab(e);
3266 				else
3267 					create_anc_ctab(e);
3268 				if ((e = e->link) == NULL)
3269 					break;
3270 			}
3271 		}
3272 
3273 		if (u->atevents != NULL) {
3274 			e = u->atevents;
3275 			for (;;) {
3276 				if (del)
3277 					delete_anc_atjob(e);
3278 				else
3279 					create_anc_atjob(e);
3280 				if ((e = e->link) == NULL)
3281 					break;
3282 			}
3283 		}
3284 
3285 		if ((u = u->nextusr)  == NULL)
3286 			break;
3287 	}
3288 }
3289 
3290 /*ARGSUSED*/
3291 static int
3292 cron_conv(int num_msg, struct pam_message **msgs,
3293     struct pam_response **response, void *appdata_ptr)
3294 {
3295 	struct pam_message	**m = msgs;
3296 	int i;
3297 
3298 	for (i = 0; i < num_msg; i++) {
3299 		switch (m[i]->msg_style) {
3300 		case PAM_ERROR_MSG:
3301 		case PAM_TEXT_INFO:
3302 			if (m[i]->msg != NULL) {
3303 				(void) msg("%s\n", m[i]->msg);
3304 			}
3305 			break;
3306 
3307 		default:
3308 			break;
3309 		}
3310 	}
3311 	return (0);
3312 }
3313 
3314 /*
3315  * Cron creates process for other than job. Mail process is the
3316  * one which rinfo does not cover. Therefore, miscpid will keep
3317  * track of the pids executed from cron. Otherwise, we will see
3318  * "unexpected pid returned.." messages appear in the log file.
3319  */
3320 static void
3321 miscpid_insert(pid_t pid)
3322 {
3323 	struct miscpid *mp;
3324 
3325 	mp = xmalloc(sizeof (*mp));
3326 	mp->pid = pid;
3327 	mp->next = miscpid_head;
3328 	miscpid_head = mp;
3329 }
3330 
3331 static int
3332 miscpid_delete(pid_t pid)
3333 {
3334 	struct miscpid *mp, *omp;
3335 	int found = 0;
3336 
3337 	omp = NULL;
3338 	for (mp = miscpid_head; mp != NULL; mp = mp->next) {
3339 		if (mp->pid == pid) {
3340 			found = 1;
3341 			break;
3342 		}
3343 		omp = mp;
3344 	}
3345 	if (found) {
3346 		if (omp != NULL)
3347 			omp->next = mp->next;
3348 		else
3349 			miscpid_head = NULL;
3350 		free(mp);
3351 	}
3352 	return (found);
3353 }
3354 
3355 /*
3356  * Establish contract terms such that all children are in abandoned
3357  * process contracts.
3358  */
3359 static void
3360 contract_set_template(void)
3361 {
3362 	int fd;
3363 
3364 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
3365 		crabort("cannot open process contract template",
3366 		    REMOVE_FIFO | CONSOLE_MSG);
3367 
3368 	if (ct_pr_tmpl_set_param(fd, 0) ||
3369 	    ct_tmpl_set_informative(fd, 0) ||
3370 	    ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR))
3371 		crabort("cannot establish contract template terms",
3372 		    REMOVE_FIFO | CONSOLE_MSG);
3373 
3374 	if (ct_tmpl_activate(fd))
3375 		crabort("cannot activate contract template",
3376 		    REMOVE_FIFO | CONSOLE_MSG);
3377 
3378 	(void) close(fd);
3379 }
3380 
3381 /*
3382  * Clear active process contract template.
3383  */
3384 static void
3385 contract_clear_template(void)
3386 {
3387 	int fd;
3388 
3389 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
3390 		crabort("cannot open process contract template",
3391 		    REMOVE_FIFO | CONSOLE_MSG);
3392 
3393 	if (ct_tmpl_clear(fd))
3394 		crabort("cannot clear contract template",
3395 		    REMOVE_FIFO | CONSOLE_MSG);
3396 
3397 	(void) close(fd);
3398 }
3399 
3400 /*
3401  * Abandon latest process contract unconditionally.  If we have leaked [some
3402  * critical amount], exit such that the kernel reaps our contracts.
3403  */
3404 static void
3405 contract_abandon_latest(pid_t pid)
3406 {
3407 	int r;
3408 	ctid_t id;
3409 	static uint_t cts_lost;
3410 
3411 	if (cts_lost > MAX_LOST_CONTRACTS)
3412 		crabort("repeated failure to abandon contracts",
3413 		    REMOVE_FIFO | CONSOLE_MSG);
3414 
3415 	if (r = contract_latest(&id)) {
3416 		msg("could not obtain latest contract for "
3417 		    "PID %ld: %s", pid, strerror(r));
3418 		cts_lost++;
3419 		return;
3420 	}
3421 
3422 	if (r = contract_abandon_id(id)) {
3423 		msg("could not abandon latest contract %ld: %s", id,
3424 		    strerror(r));
3425 		cts_lost++;
3426 		return;
3427 	}
3428 }
3429