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