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