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