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