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