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