xref: /illumos-gate/usr/src/cmd/cron/cron.c (revision b02947bf393d39f68e6c7fa8ccb98688f7f9c407)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2013 Joshua M. Clulow <josh@sysmgr.org>
26  *
27  * Copyright (c) 2014 Gary Mills
28  * Copyright (c) 2016 by Delphix. All rights reserved.
29  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
30  * Copyright 2022 Sebastian Wiedenroth
31  */
32 
33 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
34 /*	  All Rights Reserved	*/
35 
36 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
37 /*	  All Rights Reserved	*/
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 			uint32_t max_random_delay;	/* max. random delay */
199 		} ct;
200 		struct { /* for at events */
201 			short exists;	/* for revising at events	*/
202 			int eventid;	/* for el_remove-ing at events	*/
203 		} at;
204 	} of;
205 };
206 
207 struct usr {
208 	char *name;	/* name of user (e.g. "root")	*/
209 	char *home;	/* home directory for user	*/
210 	uid_t uid;	/* user id	*/
211 	gid_t gid;	/* group id	*/
212 	int aruncnt;	/* counter for running jobs per uid */
213 	int cruncnt;	/* counter for running cron jobs per uid */
214 	int ctid;	/* for el_remove-ing crontab events */
215 	short ctexists;	/* for revising crontab events	*/
216 	struct event *ctevents;	/* list of this usr's crontab events */
217 	struct event *atevents;	/* list of this usr's at events */
218 	struct usr *nextusr;
219 };	/* ptr to next user	*/
220 
221 static struct	queue
222 {
223 	int njob;	/* limit */
224 	int nice;	/* nice for execution */
225 	int nwait;	/* wait time to next execution attempt */
226 	int nrun;	/* number running */
227 }
228 	qd = {100, 2, 60},		/* default values for queue defs */
229 	qt[NQUEUE];
230 static struct	queue	qq;
231 
232 static struct runinfo
233 {
234 	pid_t	pid;
235 	short	que;
236 	struct  usr *rusr;	/* pointer to usr struct */
237 	char	*outfile;	/* file where stdout & stderr are trapped */
238 	short	jobtype;	/* what type of event: 0=cron, 1=at */
239 	char	*jobname;	/* command for "cron", jobname for "at" */
240 	int	mailwhendone;	/* 1 = send mail even if no ouptut */
241 	struct runinfo *next;
242 }	*rthead;
243 
244 static struct miscpid {
245 	pid_t		pid;
246 	struct miscpid	*next;
247 }	*miscpid_head;
248 
249 static pid_t cron_pid;	/* own pid */
250 static char didfork = 0; /* flag to see if I'm process group leader */
251 static int msgfd;	/* file descriptor for fifo queue */
252 static int ecid = 1;	/* event class id for el_remove(); MUST be set to 1 */
253 static int delayed;	/* is job being rescheduled or did it run first time */
254 static int cwd;		/* current working directory */
255 static struct event *next_event;	/* the next event to execute	*/
256 static struct usr *uhead;		/* ptr to the list of users	*/
257 
258 /* Variables for error handling at reading crontabs. */
259 static char cte_intro[] = "Line(s) with errors:\n\n";
260 static char cte_trail1[] = "\nMax number of errors encountered.";
261 static char cte_trail2[] = " Evaluation of crontab aborted.\n";
262 static int cte_free = MAILBINITFREE;	/* Free buffer space */
263 static char *cte_text = NULL;		/* Text buffer pointer */
264 static char *cte_lp;			/* Next free line in cte_text */
265 static int cte_nvalid;			/* Valid lines found */
266 
267 /* user's default environment for the shell */
268 #define	ROOTPATH	"PATH=/usr/sbin:/usr/bin"
269 #define	NONROOTPATH	"PATH=/usr/bin:"
270 
271 static char *Def_supath	= NULL;
272 static char *Def_path		= NULL;
273 static char path[LINE_MAX]	= "PATH=";
274 static char supath[LINE_MAX]	= "PATH=";
275 static char homedir[LINE_MAX]	= ENV_HOME;
276 static char logname[LINE_MAX]	= "LOGNAME=";
277 static char tzone[LINE_MAX]	= ENV_TZ;
278 static char *envinit[] = {
279 	homedir,
280 	logname,
281 	ROOTPATH,
282 	"SHELL=/usr/bin/sh",
283 	tzone,
284 	NULL
285 };
286 
287 extern char **environ;
288 
289 #define	DEFTZ		"GMT"
290 static	int	log = 0;
291 static	char	hzname[10];
292 
293 static void cronend(int);
294 static void thaw_handler(int);
295 static void child_handler(int);
296 static void child_sigreset(void);
297 
298 static void mod_ctab(char *, time_t);
299 static void mod_atjob(char *, time_t);
300 static void add_atevent(struct usr *, char *, time_t, int);
301 static void rm_ctevents(struct usr *);
302 static void cleanup(struct runinfo *rn, int r);
303 static void crabort(char *, int);
304 static void msg(char *fmt, ...);
305 static void ignore_msg(char *, char *, struct event *);
306 static void logit(int, struct runinfo *, int);
307 static void parsqdef(char *);
308 static void defaults();
309 static void initialize(int);
310 static void quedefs(int);
311 static int idle(long);
312 static struct usr *find_usr(char *);
313 static int ex(struct event *e);
314 static void read_dirs(int);
315 static void mail(char *, char *, 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(8) 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(8) 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()) != 0) {
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, "%+", &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, "%+", &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(8) 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(8) 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 their crontab\n",
979 		    u->name);
980 #endif
981 		rm_ctevents(u);
982 		el_remove(u->ctid, 0);
983 		readcron(u, reftime);
984 	}
985 }
986 
987 static void
988 mod_atjob(char *name, time_t reftime __unused)
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 	uint32_t max_random_delay = 0;
1136 	int lineno = 0;
1137 	const char *errstr;
1138 
1139 	/* read the crontab file */
1140 	cte_init();		/* Init error handling */
1141 	if (cwd != CRON) {
1142 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
1143 		    CRONDIR, u->name) >= sizeof (namebuf)) {
1144 			return;
1145 		}
1146 		pname = namebuf;
1147 	} else {
1148 		pname = u->name;
1149 	}
1150 	if ((cf = fopen(pname, "r")) == NULL) {
1151 		mail(u->name, NOREAD, ERR_UNIXERR);
1152 		return;
1153 	}
1154 	while (fgets(line, CTLINESIZE, cf) != NULL) {
1155 		char *tmp;
1156 		/* process a line of a crontab file */
1157 		lineno++;
1158 		if (cte_istoomany())
1159 			break;
1160 		cursor = 0;
1161 		while (line[cursor] == ' ' || line[cursor] == '\t')
1162 			cursor++;
1163 		if (line[cursor] == '#' || line[cursor] == '\n')
1164 			continue;
1165 
1166 		if (strncmp(&line[cursor], ENV_TZ,
1167 		    strlen(ENV_TZ)) == 0) {
1168 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
1169 				*tmp = '\0';
1170 			}
1171 
1172 			if (!isvalid_tz(&line[cursor + strlen(ENV_TZ)], NULL,
1173 			    _VTZ_ALL)) {
1174 				cte_add(lineno, line);
1175 				break;
1176 			}
1177 			if (tz == NULL || strcmp(&line[cursor], get_obj(tz))) {
1178 				rel_shared(tz);
1179 				tz = create_shared_str(&line[cursor]);
1180 			}
1181 			continue;
1182 		}
1183 
1184 		if (strncmp(&line[cursor], ENV_HOME,
1185 		    strlen(ENV_HOME)) == 0) {
1186 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
1187 				*tmp = '\0';
1188 			}
1189 			if (home == NULL ||
1190 			    strcmp(&line[cursor], get_obj(home))) {
1191 				rel_shared(home);
1192 				home = create_shared_str(
1193 				    &line[cursor + strlen(ENV_HOME)]);
1194 			}
1195 			continue;
1196 		}
1197 
1198 		if (strncmp(&line[cursor], ENV_SHELL,
1199 		    strlen(ENV_SHELL)) == 0) {
1200 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
1201 				*tmp = '\0';
1202 			}
1203 			if (shell == NULL ||
1204 			    strcmp(&line[cursor], get_obj(shell))) {
1205 				rel_shared(shell);
1206 				shell = create_shared_str(&line[cursor]);
1207 			}
1208 			continue;
1209 		}
1210 
1211 		if (strncmp(&line[cursor], ENV_RANDOM_DELAY,
1212 		    strlen(ENV_RANDOM_DELAY)) == 0) {
1213 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
1214 				*tmp = '\0';
1215 			}
1216 
1217 			max_random_delay = strtonum(
1218 			    &line[cursor + strlen(ENV_RANDOM_DELAY)], 0,
1219 			    UINT32_MAX / 60, &errstr);
1220 			if (errstr != NULL) {
1221 				cte_add(lineno, line);
1222 				break;
1223 			}
1224 
1225 			continue;
1226 		}
1227 
1228 		e = xmalloc(sizeof (struct event));
1229 		e->etype = CRONEVENT;
1230 
1231 		if (next_field(0, 59, line, &cursor,
1232 		    &e->of.ct.minute) != CFOK ||
1233 		    next_field(0, 23, line, &cursor, &e->of.ct.hour) != CFOK ||
1234 		    next_field(1, 31, line, &cursor,
1235 		    &e->of.ct.daymon) != CFOK ||
1236 		    next_field(1, 12, line, &cursor, &e->of.ct.month) != CFOK ||
1237 		    next_field(0, 6, line, &cursor,
1238 		    &e->of.ct.dayweek) != CFOK) {
1239 #ifdef DEBUG
1240 			(void) fprintf(stderr, "Error: %d %s", lineno, line);
1241 #endif
1242 			free(e);
1243 			cte_add(lineno, line);
1244 			continue;
1245 		}
1246 		while (line[cursor] == ' ' || line[cursor] == '\t')
1247 			cursor++;
1248 		if (line[cursor] == '\n' || line[cursor] == '\0')
1249 			continue;
1250 		/* get the command to execute	*/
1251 		start = cursor;
1252 again:
1253 		while ((line[cursor] != '%') &&
1254 		    (line[cursor] != '\n') &&
1255 		    (line[cursor] != '\0') &&
1256 		    (line[cursor] != '\\'))
1257 			cursor++;
1258 		if (line[cursor] == '\\') {
1259 			cursor += 2;
1260 			goto again;
1261 		}
1262 		e->cmd = xmalloc(cursor-start + 1);
1263 		(void) strncpy(e->cmd, line + start, cursor-start);
1264 		e->cmd[cursor-start] = '\0';
1265 		/* see if there is any standard input	*/
1266 		if (line[cursor] == '%') {
1267 			e->of.ct.input = xmalloc(strlen(line)-cursor + 1);
1268 			(void) strcpy(e->of.ct.input, line + cursor + 1);
1269 			for (i = 0; i < strlen(e->of.ct.input); i++) {
1270 				if (e->of.ct.input[i] == '%')
1271 					e->of.ct.input[i] = '\n';
1272 			}
1273 		} else {
1274 			e->of.ct.input = NULL;
1275 		}
1276 		/* set the timezone of this entry */
1277 		e->of.ct.tz = dup_shared(tz);
1278 		/* set the shell of this entry */
1279 		e->of.ct.shell = dup_shared(shell);
1280 		/* set the home of this entry */
1281 		e->of.ct.home = dup_shared(home);
1282 		/* set the maximum random delay */
1283 		e->of.ct.max_random_delay = max_random_delay;
1284 		/* have the event point to it's owner	*/
1285 		e->u = u;
1286 		/* insert this event at the front of this user's event list */
1287 		e->link = u->ctevents;
1288 		u->ctevents = e;
1289 		/* set the time for the first occurance of this event	*/
1290 		e->time = next_time(e, reftime);
1291 		/* finally, add this event to the main event list	*/
1292 		switch (el_add(e, e->time, u->ctid)) {
1293 		case -1:
1294 			ignore_msg("readcron", "cron", e);
1295 			break;
1296 		case -2: /* event time lower than init time */
1297 			reset_needed = 1;
1298 			break;
1299 		}
1300 		cte_valid();
1301 #ifdef DEBUG
1302 		cftime(timebuf, "%+", &e->time);
1303 		(void) fprintf(stderr, "inserting cron event %s at %ld (%s)\n",
1304 		    e->cmd, e->time, timebuf);
1305 #endif
1306 	}
1307 	cte_sendmail(u->name);	/* mail errors if any to user */
1308 	(void) fclose(cf);
1309 	rel_shared(tz);
1310 	rel_shared(shell);
1311 	rel_shared(home);
1312 }
1313 
1314 /*
1315  * Below are the functions for handling of errors in crontabs. Concept is to
1316  * collect faulty lines and send one email at the end of the crontab
1317  * evaluation. If there are erroneous lines only ((cte_nvalid == 0), evaluation
1318  * of crontab is aborted. Otherwise reading of crontab is continued to the end
1319  * of the file but no further error logging appears.
1320  */
1321 static void
1322 cte_init()
1323 {
1324 	if (cte_text == NULL)
1325 		cte_text = xmalloc(MAILBUFLEN);
1326 	(void) strlcpy(cte_text, cte_intro, MAILBUFLEN);
1327 	cte_lp = cte_text + sizeof (cte_intro) - 1;
1328 	cte_free = MAILBINITFREE;
1329 	cte_nvalid = 0;
1330 }
1331 
1332 static void
1333 cte_add(int lineno, char *ctline)
1334 {
1335 	int len;
1336 	char *p;
1337 
1338 	if (cte_free >= LINELIMIT) {
1339 		(void) sprintf(cte_lp, "%4d: ", lineno);
1340 		(void) strlcat(cte_lp, ctline, LINELIMIT - 1);
1341 		len = strlen(cte_lp);
1342 		if (cte_lp[len - 1] != '\n') {
1343 			cte_lp[len++] = '\n';
1344 			cte_lp[len] = '\0';
1345 		}
1346 		for (p = cte_lp; *p; p++) {
1347 			if (isprint(*p) || *p == '\n' || *p == '\t')
1348 				continue;
1349 			*p = '.';
1350 		}
1351 		cte_lp += len;
1352 		cte_free -= len;
1353 		if (cte_free < LINELIMIT) {
1354 			size_t buflen = MAILBUFLEN - (cte_lp - cte_text);
1355 			(void) strlcpy(cte_lp, cte_trail1, buflen);
1356 			if (cte_nvalid == 0)
1357 				(void) strlcat(cte_lp, cte_trail2, buflen);
1358 		}
1359 	}
1360 }
1361 
1362 static void
1363 cte_valid()
1364 {
1365 	cte_nvalid++;
1366 }
1367 
1368 static int
1369 cte_istoomany()
1370 {
1371 	/*
1372 	 * Return TRUE only if all lines are faulty. So evaluation of
1373 	 * a crontab is not aborted if at least one valid line was found.
1374 	 */
1375 	return (cte_nvalid == 0 && cte_free < LINELIMIT);
1376 }
1377 
1378 static void
1379 cte_sendmail(char *username)
1380 {
1381 	if (cte_free < MAILBINITFREE)
1382 		mail(username, cte_text, ERR_CRONTABENT);
1383 }
1384 
1385 /*
1386  * Send mail with error message to a user
1387  */
1388 static void
1389 mail(char *usrname, char *mesg, int format)
1390 {
1391 	/* mail mails a user a message.	*/
1392 	FILE *pipe;
1393 	char *temp;
1394 	struct passwd	*ruser_ids;
1395 	pid_t fork_val;
1396 	int saveerrno = errno;
1397 	struct utsname	name;
1398 
1399 #ifdef TESTING
1400 	return;
1401 #endif
1402 	(void) uname(&name);
1403 	if ((fork_val = fork()) == (pid_t)-1) {
1404 		msg("cron cannot fork\n");
1405 		return;
1406 	}
1407 	if (fork_val == 0) {
1408 		child_sigreset();
1409 		contract_clear_template();
1410 		if ((ruser_ids = getpwnam(usrname)) == NULL)
1411 			exit(0);
1412 		(void) setuid(ruser_ids->pw_uid);
1413 		temp = xmalloc(strlen(MAIL) + strlen(usrname) + 2);
1414 		(void) sprintf(temp, "%s %s", MAIL, usrname);
1415 		pipe = popen(temp, "w");
1416 		if (pipe != NULL) {
1417 			(void) fprintf(pipe, "To: %s\n", usrname);
1418 			switch (format) {
1419 			case ERR_CRONTABENT:
1420 				(void) fprintf(pipe, CRONTABERR);
1421 				(void) fprintf(pipe, "Your \"crontab\" on %s\n",
1422 				    name.nodename);
1423 				(void) fprintf(pipe, mesg);
1424 				(void) fprintf(pipe,
1425 				    "\nEntries or crontab have been ignored\n");
1426 				break;
1427 			case ERR_UNIXERR:
1428 				(void) fprintf(pipe, "Subject: %s\n\n", mesg);
1429 				(void) fprintf(pipe,
1430 				    "The error on %s was \"%s\"\n",
1431 				    name.nodename, errmsg(saveerrno));
1432 				break;
1433 
1434 			case ERR_CANTEXECCRON:
1435 				(void) fprintf(pipe,
1436 				"Subject: Couldn't run your \"cron\" job\n\n");
1437 				(void) fprintf(pipe,
1438 				    "Your \"cron\" job on %s ", name.nodename);
1439 				(void) fprintf(pipe, "couldn't be run\n");
1440 				(void) fprintf(pipe, "%s\n", mesg);
1441 				(void) fprintf(pipe,
1442 				"The error was \"%s\"\n", errmsg(saveerrno));
1443 				break;
1444 
1445 			case ERR_CANTEXECAT:
1446 				(void) fprintf(pipe,
1447 				"Subject: Couldn't run your \"at\" job\n\n");
1448 				(void) fprintf(pipe, "Your \"at\" job on %s ",
1449 				    name.nodename);
1450 				(void) fprintf(pipe, "couldn't be run\n");
1451 				(void) fprintf(pipe, "%s\n", mesg);
1452 				(void) fprintf(pipe,
1453 				"The error was \"%s\"\n", errmsg(saveerrno));
1454 				break;
1455 
1456 			default:
1457 				break;
1458 			}
1459 			(void) pclose(pipe);
1460 		}
1461 		free(temp);
1462 		exit(0);
1463 	}
1464 
1465 	contract_abandon_latest(fork_val);
1466 
1467 	if (cron_pid == getpid()) {
1468 		miscpid_insert(fork_val);
1469 	}
1470 }
1471 
1472 #define	tm_cmp(t1, t2) (\
1473 	(t1)->tm_year == (t2)->tm_year && \
1474 	(t1)->tm_mon == (t2)->tm_mon && \
1475 	(t1)->tm_mday == (t2)->tm_mday && \
1476 	(t1)->tm_hour == (t2)->tm_hour && \
1477 	(t1)->tm_min == (t2)->tm_min)
1478 
1479 #define	tm_setup(tp, yr, mon, dy, hr, min, dst) \
1480 	(tp)->tm_year = yr; \
1481 	(tp)->tm_mon = mon; \
1482 	(tp)->tm_mday = dy; \
1483 	(tp)->tm_hour = hr; \
1484 	(tp)->tm_min = min; \
1485 	(tp)->tm_isdst = dst; \
1486 	(tp)->tm_sec = 0; \
1487 	(tp)->tm_wday = 0; \
1488 	(tp)->tm_yday = 0;
1489 
1490 /*
1491  * modification for bugid 1104537. the second argument to next_time is
1492  * now the value of time(2) to be used. if this is 0, then use the
1493  * current time. otherwise, the second argument is the time from which to
1494  * calculate things. this is useful to correct situations where you've
1495  * gone backwards in time (I.e. the system's internal clock is correcting
1496  * itself backwards).
1497  */
1498 
1499 
1500 
1501 static time_t
1502 tz_next_time(struct event *e, time_t tflag)
1503 {
1504 	/*
1505 	 * returns the integer time for the next occurance of event e.
1506 	 * the following fields have ranges as indicated:
1507 	 * PRGM  | min	hour	day of month	mon	day of week
1508 	 * ------|-------------------------------------------------------
1509 	 * cron  | 0-59	0-23	    1-31	1-12	0-6 (0=sunday)
1510 	 * time  | 0-59	0-23	    1-31	0-11	0-6 (0=sunday)
1511 	 * NOTE: this routine is hard to understand.
1512 	 */
1513 
1514 	struct tm *tm, ref_tm, tmp, tmp1, tmp2;
1515 	int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days;
1516 	int d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd;
1517 	int today;
1518 	time_t t, ref_t, t1, t2, zone_start;
1519 	int fallback;
1520 	extern int days_btwn(int, int, int, int, int, int);
1521 
1522 	if (tflag == 0) {
1523 		t = time(NULL);	/* original way of doing things	*/
1524 	} else {
1525 		t =  tflag;
1526 	}
1527 
1528 	tm = &ref_tm;	/* use a local variable and call localtime_r() */
1529 	ref_t = t;	/* keep a copy of the reference time */
1530 
1531 recalc:
1532 	fallback = 0;
1533 
1534 	(void) localtime_r(&t, tm);
1535 
1536 	if (daylight) {
1537 		tmp = *tm;
1538 		tmp.tm_isdst = (tm->tm_isdst > 0 ? 0 : 1);
1539 		t1 = xmktime(&tmp);
1540 		/*
1541 		 * see if we will have timezone switch over, and clock will
1542 		 * fall back. zone_start will hold the time when it happens
1543 		 * (ie time of PST -> PDT switch over).
1544 		 */
1545 		if (tm->tm_isdst != tmp.tm_isdst &&
1546 		    (t1 - t) == (timezone - altzone) &&
1547 		    tm_cmp(tm, &tmp)) {
1548 			zone_start = get_switching_time(tmp.tm_isdst, t);
1549 			fallback = 1;
1550 		}
1551 	}
1552 
1553 	tm_mon = next_ge(tm->tm_mon + 1, e->of.ct.month) - 1;	/* 0-11 */
1554 	tm_mday = next_ge(tm->tm_mday, e->of.ct.daymon);	/* 1-31 */
1555 	tm_wday = next_ge(tm->tm_wday, e->of.ct.dayweek);	/* 0-6	*/
1556 	today = TRUE;
1557 	if ((strcmp(e->of.ct.daymon, "*") == 0 && tm->tm_wday != tm_wday) ||
1558 	    (strcmp(e->of.ct.dayweek, "*") == 0 && tm->tm_mday != tm_mday) ||
1559 	    (tm->tm_mday != tm_mday && tm->tm_wday != tm_wday) ||
1560 	    (tm->tm_mon != tm_mon)) {
1561 		today = FALSE;
1562 	}
1563 	m = tm->tm_min + (t == ref_t ? 1 : 0);
1564 	if ((tm->tm_hour + 1) <= next_ge(tm->tm_hour, e->of.ct.hour)) {
1565 		m = 0;
1566 	}
1567 	min = next_ge(m%60, e->of.ct.minute);
1568 	carry = (min < m) ? 1 : 0;
1569 	h = tm->tm_hour + carry;
1570 	hr = next_ge(h%24, e->of.ct.hour);
1571 	carry = (hr < h) ? 1 : 0;
1572 
1573 	if (carry == 0 && today) {
1574 		/* this event must occur today */
1575 		tm_setup(&tmp, tm->tm_year, tm->tm_mon, tm->tm_mday,
1576 		    hr, min, tm->tm_isdst);
1577 		tmp1 = tmp;
1578 		if ((t1 = xmktime(&tmp1)) == (time_t)-1) {
1579 			return (0);
1580 		}
1581 		if (daylight && tmp.tm_isdst != tmp1.tm_isdst) {
1582 			/* In case we are falling back */
1583 			if (fallback) {
1584 				/* we may need to run the job once more. */
1585 				t = zone_start;
1586 				goto recalc;
1587 			}
1588 
1589 			/*
1590 			 * In case we are not in falling back period,
1591 			 * calculate the time assuming the DST. If the
1592 			 * date/time is not altered by mktime, it is the
1593 			 * time to execute the job.
1594 			 */
1595 			tmp2 = tmp;
1596 			tmp2.tm_isdst = tmp1.tm_isdst;
1597 			if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
1598 				return (0);
1599 			}
1600 			if (tmp1.tm_isdst == tmp2.tm_isdst &&
1601 			    tm_cmp(&tmp, &tmp2)) {
1602 				/*
1603 				 * We got a valid time.
1604 				 */
1605 				return (t1);
1606 			} else {
1607 				/*
1608 				 * If the date does not match even if
1609 				 * we assume the alternate timezone, then
1610 				 * it must be the invalid time. eg
1611 				 * 2am while switching 1:59am to 3am.
1612 				 * t1 should point the time before the
1613 				 * switching over as we've calculate the
1614 				 * time with assuming alternate zone.
1615 				 */
1616 				if (tmp1.tm_isdst != tmp2.tm_isdst) {
1617 					t = get_switching_time(tmp1.tm_isdst,
1618 					    t1);
1619 				} else {
1620 					/* does this really happen? */
1621 					t = get_switching_time(tmp1.tm_isdst,
1622 					    t1 - abs(timezone - altzone));
1623 				}
1624 				if (t == (time_t)-1) {
1625 					return (0);
1626 				}
1627 			}
1628 			goto recalc;
1629 		}
1630 		if (tm_cmp(&tmp, &tmp1)) {
1631 			/* got valid time */
1632 			return (t1);
1633 		} else {
1634 			/*
1635 			 * This should never happen, but just in
1636 			 * case, we fall back to the old code.
1637 			 */
1638 			if (tm->tm_min > min) {
1639 				t += (time_t)(hr-tm->tm_hour-1) * HOUR +
1640 				    (time_t)(60-tm->tm_min + min) * MINUTE;
1641 			} else {
1642 				t += (time_t)(hr-tm->tm_hour) * HOUR +
1643 				    (time_t)(min-tm->tm_min) * MINUTE;
1644 			}
1645 			t1 = t;
1646 			t -= (time_t)tm->tm_sec;
1647 			(void) localtime_r(&t, &tmp);
1648 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
1649 				t -= (timezone - altzone);
1650 			return ((t <= ref_t) ? t1 : t);
1651 		}
1652 	}
1653 
1654 	/*
1655 	 * Job won't run today, however if we have a switch over within
1656 	 * one hour and we will have one hour time drifting back in this
1657 	 * period, we may need to run the job one more time if the job was
1658 	 * set to run on this hour of clock.
1659 	 */
1660 	if (fallback) {
1661 		t = zone_start;
1662 		goto recalc;
1663 	}
1664 
1665 	min = next_ge(0, e->of.ct.minute);
1666 	hr = next_ge(0, e->of.ct.hour);
1667 
1668 	/*
1669 	 * calculate the date of the next occurance of this event, which
1670 	 * will be on a different day than the current
1671 	 */
1672 
1673 	/* check monthly day specification	*/
1674 	d1 = tm->tm_mday + 1;
1675 	day1 = next_ge((d1-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1,
1676 	    e->of.ct.daymon);
1677 	carry1 = (day1 < d1) ? 1 : 0;
1678 
1679 	/* check weekly day specification	*/
1680 	d2 = tm->tm_wday + 1;
1681 	wday = next_ge(d2%7, e->of.ct.dayweek);
1682 	if (wday < d2)
1683 		daysahead = 7 - d2 + wday;
1684 	else
1685 		daysahead = wday - d2;
1686 	day2 = (d1 + daysahead-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1;
1687 	carry2 = (day2 < d1) ? 1 : 0;
1688 
1689 	/*
1690 	 *	based on their respective specifications, day1, and day2 give
1691 	 *	the day of the month for the next occurance of this event.
1692 	 */
1693 	if ((strcmp(e->of.ct.daymon, "*") == 0) &&
1694 	    (strcmp(e->of.ct.dayweek, "*") != 0)) {
1695 		day1 = day2;
1696 		carry1 = carry2;
1697 	}
1698 	if ((strcmp(e->of.ct.daymon, "*") != 0) &&
1699 	    (strcmp(e->of.ct.dayweek, "*") == 0)) {
1700 		day2 = day1;
1701 		carry2 = carry1;
1702 	}
1703 
1704 	yr = tm->tm_year;
1705 	if ((carry1 && carry2) || (tm->tm_mon != tm_mon)) {
1706 		/* event does not occur in this month	*/
1707 		m = tm->tm_mon + 1;
1708 		mon = next_ge(m%12 + 1, e->of.ct.month) - 1;	/* 0..11 */
1709 		carry = (mon < m) ? 1 : 0;
1710 		yr += carry;
1711 		/* recompute day1 and day2	*/
1712 		day1 = next_ge(1, e->of.ct.daymon);
1713 		db = days_btwn(tm->tm_mon, tm->tm_mday, tm->tm_year, mon,
1714 		    1, yr) + 1;
1715 		wd = (tm->tm_wday + db)%7;
1716 		/* wd is the day of the week of the first of month mon	*/
1717 		wday = next_ge(wd, e->of.ct.dayweek);
1718 		if (wday < wd)
1719 			day2 = 1 + 7 - wd + wday;
1720 		else
1721 			day2 = 1 + wday - wd;
1722 		if ((strcmp(e->of.ct.daymon, "*") != 0) &&
1723 		    (strcmp(e->of.ct.dayweek, "*") == 0))
1724 			day2 = day1;
1725 		if ((strcmp(e->of.ct.daymon, "*") == 0) &&
1726 		    (strcmp(e->of.ct.dayweek, "*") != 0))
1727 			day1 = day2;
1728 		day = (day1 < day2) ? day1 : day2;
1729 	} else {			/* event occurs in this month	*/
1730 		mon = tm->tm_mon;
1731 		if (!carry1 && !carry2)
1732 			day = (day1 < day2) ? day1 : day2;
1733 		else if (!carry1)
1734 			day = day1;
1735 		else
1736 			day = day2;
1737 	}
1738 
1739 	/*
1740 	 * now that we have the min, hr, day, mon, yr of the next event,
1741 	 * figure out what time that turns out to be.
1742 	 */
1743 	tm_setup(&tmp, yr, mon, day, hr, min, -1);
1744 	tmp2 = tmp;
1745 	if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
1746 		return (0);
1747 	}
1748 	if (tm_cmp(&tmp, &tmp2)) {
1749 		/*
1750 		 * mktime returns clock for the current time zone. If the
1751 		 * target date was in fallback period, it needs to be adjusted
1752 		 * to the time comes first.
1753 		 * Suppose, we are at Jan and scheduling job at 1:30am10/26/03.
1754 		 * mktime returns the time in PST, but 1:30am in PDT comes
1755 		 * first. So reverse the tm_isdst, and see if we have such
1756 		 * time/date.
1757 		 */
1758 		if (daylight) {
1759 			int dst = tmp2.tm_isdst;
1760 
1761 			tmp2 = tmp;
1762 			tmp2.tm_isdst = (dst > 0 ? 0 : 1);
1763 			if ((t2 = xmktime(&tmp2)) == (time_t)-1) {
1764 				return (0);
1765 			}
1766 			if (tm_cmp(&tmp, &tmp2)) {
1767 				/*
1768 				 * same time/date found in the opposite zone.
1769 				 * check the clock to see which comes early.
1770 				 */
1771 				if (t2 > ref_t && t2 < t1) {
1772 					t1 = t2;
1773 				}
1774 			}
1775 		}
1776 		return (t1);
1777 	} else {
1778 		/*
1779 		 * mktime has set different time/date for the given date.
1780 		 * This means that the next job is scheduled to be run on the
1781 		 * invalid time. There are three possible invalid date/time.
1782 		 * 1. Non existing day of the month. such as April 31th.
1783 		 * 2. Feb 29th in the non-leap year.
1784 		 * 3. Time gap during the DST switch over.
1785 		 */
1786 		d1 = days_in_mon(mon, yr);
1787 		if ((mon != 1 && day > d1) || (mon == 1 && day > 29)) {
1788 			/*
1789 			 * see if we have got a specific date which
1790 			 * is invalid.
1791 			 */
1792 			if (strcmp(e->of.ct.dayweek, "*") == 0 &&
1793 			    mon == (next_ge((mon + 1)%12 + 1,
1794 			    e->of.ct.month) - 1) &&
1795 			    day <= next_ge(1, e->of.ct.daymon)) {
1796 				/* job never run */
1797 				return (0);
1798 			}
1799 			/*
1800 			 * Since the day has gone invalid, we need to go to
1801 			 * next month, and recalcuate the first occurrence.
1802 			 * eg the cron tab such as:
1803 			 * 0 0 1,15,31 1,2,3,4,5 * /usr/bin....
1804 			 * 2/31 is invalid, so the next job is 3/1.
1805 			 */
1806 			tmp2 = tmp;
1807 			tmp2.tm_min = 0;
1808 			tmp2.tm_hour = 0;
1809 			tmp2.tm_mday = 1; /* 1st day of the month */
1810 			if (mon == 11) {
1811 				tmp2.tm_mon = 0;
1812 				tmp2.tm_year = yr + 1;
1813 			} else {
1814 				tmp2.tm_mon = mon + 1;
1815 			}
1816 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
1817 				return (0);
1818 			}
1819 		} else if (mon == 1 && day > d1) {
1820 			/*
1821 			 * ie 29th in the non-leap year. Forwarding the
1822 			 * clock to Feb 29th 00:00 (March 1st), and recalculate
1823 			 * the next time.
1824 			 */
1825 			tmp2 = tmp;
1826 			tmp2.tm_min = 0;
1827 			tmp2.tm_hour = 0;
1828 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
1829 				return (0);
1830 			}
1831 		} else if (daylight) {
1832 			/*
1833 			 * Non existing time, eg 2am PST during summer time
1834 			 * switch.
1835 			 * We need to get the correct isdst which we are
1836 			 * swithing to, by adding time difference to make sure
1837 			 * that t2 is in the zone being switched.
1838 			 */
1839 			t2 = t1;
1840 			t2 += abs(timezone - altzone);
1841 			(void) localtime_r(&t2, &tmp2);
1842 			zone_start = get_switching_time(tmp2.tm_isdst,
1843 			    t1 - abs(timezone - altzone));
1844 			if (zone_start == (time_t)-1) {
1845 				return (0);
1846 			}
1847 			t = zone_start;
1848 		} else {
1849 			/*
1850 			 * This should never happen, but fall back to the
1851 			 * old code.
1852 			 */
1853 			days = days_btwn(tm->tm_mon,
1854 			    tm->tm_mday, tm->tm_year, mon, day, yr);
1855 			t += (time_t)(23-tm->tm_hour)*HOUR
1856 			    + (time_t)(60-tm->tm_min)*MINUTE
1857 			    + (time_t)hr*HOUR + (time_t)min*MINUTE
1858 			    + (time_t)days*DAY;
1859 			t1 = t;
1860 			t -= (time_t)tm->tm_sec;
1861 			(void) localtime_r(&t, &tmp);
1862 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
1863 				t -= (timezone - altzone);
1864 			return (t <= ref_t ? t1 : t);
1865 		}
1866 		goto recalc;
1867 	}
1868 	/*NOTREACHED*/
1869 }
1870 
1871 static time_t
1872 next_time(struct event *e, time_t tflag)
1873 {
1874 	time_t ret;
1875 
1876 	if (e->of.ct.tz != NULL) {
1877 		(void) putenv((char *)get_obj(e->of.ct.tz));
1878 		tzset();
1879 		ret = tz_next_time(e, tflag);
1880 		(void) putenv(tzone);
1881 		tzset();
1882 	} else {
1883 		ret = tz_next_time(e, tflag);
1884 	}
1885 
1886 	if (e->of.ct.max_random_delay > 0) {
1887 		ret += arc4random_uniform(e->of.ct.max_random_delay * 60 - 1);
1888 	}
1889 	return (ret);
1890 }
1891 
1892 /*
1893  * This returns TOD in time_t that zone switch will happen, and this
1894  * will be called when clock fallback is about to happen.
1895  * (ie 30minutes before the time of PST -> PDT switch. 2:00 AM PST
1896  * will fall back to 1:00 PDT. So this function will be called only
1897  * for the time between 1:00 AM PST and 2:00 PST(1:00 PST)).
1898  * First goes through the common time differences to see if zone
1899  * switch happens at those minutes later. If not, check every minutes
1900  * until 6 hours ahead see if it happens(We might have 45minutes
1901  * fallback).
1902  */
1903 static time_t
1904 get_switching_time(int to_dst, time_t t_ref)
1905 {
1906 	time_t t, t1;
1907 	struct tm tmp, tmp1;
1908 	int hints[] = { 60, 120, 30, 90, 0}; /* minutes */
1909 	int i;
1910 
1911 	(void) localtime_r(&t_ref, &tmp);
1912 	tmp1 = tmp;
1913 	tmp1.tm_sec = 0;
1914 	tmp1.tm_min = 0;
1915 	if ((t = xmktime(&tmp1)) == (time_t)-1)
1916 		return ((time_t)-1);
1917 
1918 	/* fast path */
1919 	for (i = 0; hints[i] != 0; i++) {
1920 		t1 = t + hints[i] * 60;
1921 		(void) localtime_r(&t1, &tmp1);
1922 		if (tmp1.tm_isdst == to_dst) {
1923 			t1--;
1924 			(void) localtime_r(&t1, &tmp1);
1925 			if (tmp1.tm_isdst != to_dst) {
1926 				return (t1 + 1);
1927 			}
1928 		}
1929 	}
1930 
1931 	/* ugly, but don't know other than this. */
1932 	tmp1 = tmp;
1933 	tmp1.tm_sec = 0;
1934 	if ((t = xmktime(&tmp1)) == (time_t)-1)
1935 		return ((time_t)-1);
1936 	while (t < (t_ref + 6*60*60)) { /* 6 hours should be enough */
1937 		t += 60; /* at least one minute, I assume */
1938 		(void) localtime_r(&t, &tmp);
1939 		if (tmp.tm_isdst == to_dst)
1940 			return (t);
1941 	}
1942 	return ((time_t)-1);
1943 }
1944 
1945 static time_t
1946 xmktime(struct tm *tmp)
1947 {
1948 	time_t ret;
1949 
1950 	if ((ret = mktime(tmp)) == (time_t)-1) {
1951 		if (errno == EOVERFLOW) {
1952 			return ((time_t)-1);
1953 		}
1954 		crabort("internal error: mktime failed",
1955 		    REMOVE_FIFO|CONSOLE_MSG);
1956 	}
1957 	return (ret);
1958 }
1959 
1960 #define	DUMMY	100
1961 
1962 static int
1963 next_ge(int current, char *list)
1964 {
1965 	/*
1966 	 * list is a character field as in a crontab file;
1967 	 * for example: "40, 20, 50-10"
1968 	 * next_ge returns the next number in the list that is
1969 	 * greater than  or equal to current. if no numbers of list
1970 	 * are >= current, the smallest element of list is returned.
1971 	 * NOTE: current must be in the appropriate range.
1972 	 */
1973 
1974 	char *ptr;
1975 	int n, n2, min, min_gt;
1976 
1977 	if (strcmp(list, "*") == 0)
1978 		return (current);
1979 	ptr = list;
1980 	min = DUMMY;
1981 	min_gt = DUMMY;
1982 	for (;;) {
1983 		if ((n = (int)num(&ptr)) == current)
1984 			return (current);
1985 		if (n < min)
1986 			min = n;
1987 		if ((n > current) && (n < min_gt))
1988 			min_gt = n;
1989 		if (*ptr == '-') {
1990 			ptr++;
1991 			if ((n2 = (int)num(&ptr)) > n) {
1992 				if ((current > n) && (current <= n2))
1993 					return (current);
1994 			} else {	/* range that wraps around */
1995 				if (current > n)
1996 					return (current);
1997 				if (current <= n2)
1998 					return (current);
1999 			}
2000 		}
2001 		if (*ptr == '\0')
2002 			break;
2003 		ptr += 1;
2004 	}
2005 	if (min_gt != DUMMY)
2006 		return (min_gt);
2007 	else
2008 		return (min);
2009 }
2010 
2011 static void
2012 free_if_unused(struct usr *u)
2013 {
2014 	struct usr *cur, *prev;
2015 	/*
2016 	 *	To make sure a usr structure is idle we must check that
2017 	 *	there are no at jobs queued for the user; the user does
2018 	 *	not have a crontab, and also that there are no running at
2019 	 *	or cron jobs (since the runinfo structure also has a
2020 	 *	pointer to the usr structure).
2021 	 */
2022 	if (!u->ctexists && u->atevents == NULL &&
2023 	    u->cruncnt == 0 && u->aruncnt == 0) {
2024 #ifdef DEBUG
2025 		(void) fprintf(stderr, "%s removed from usr list\n", u->name);
2026 #endif
2027 		for (cur = uhead, prev = NULL;
2028 		    cur != u;
2029 		    prev = cur, cur = cur->nextusr) {
2030 			if (cur == NULL) {
2031 				return;
2032 			}
2033 		}
2034 
2035 		if (prev == NULL)
2036 			uhead = u->nextusr;
2037 		else
2038 			prev->nextusr = u->nextusr;
2039 		free(u->name);
2040 		free(u->home);
2041 		free(u);
2042 	}
2043 }
2044 
2045 static void
2046 del_atjob(char *name, char *usrname)
2047 {
2048 
2049 	struct	event	*e, *eprev;
2050 	struct	usr	*u;
2051 
2052 	if ((u = find_usr(usrname)) == NULL)
2053 		return;
2054 	e = u->atevents;
2055 	eprev = NULL;
2056 	while (e != NULL) {
2057 		if (strcmp(name, e->cmd) == 0) {
2058 			if (next_event == e)
2059 				next_event = NULL;
2060 			if (eprev == NULL)
2061 				u->atevents = e->link;
2062 			else
2063 				eprev->link = e->link;
2064 			el_remove(e->of.at.eventid, 1);
2065 			free(e->cmd);
2066 			free(e);
2067 			break;
2068 		} else {
2069 			eprev = e;
2070 			e = e->link;
2071 		}
2072 	}
2073 
2074 	free_if_unused(u);
2075 }
2076 
2077 static void
2078 del_ctab(char *name)
2079 {
2080 
2081 	struct	usr *u;
2082 
2083 	if ((u = find_usr(name)) == NULL)
2084 		return;
2085 	rm_ctevents(u);
2086 	el_remove(u->ctid, 0);
2087 	u->ctid = 0;
2088 	u->ctexists = 0;
2089 
2090 	free_if_unused(u);
2091 }
2092 
2093 static void
2094 rm_ctevents(struct usr *u)
2095 {
2096 	struct event *e2, *e3;
2097 
2098 	/*
2099 	 * see if the next event (to be run by cron) is a cronevent
2100 	 * owned by this user.
2101 	 */
2102 
2103 	if ((next_event != NULL) &&
2104 	    (next_event->etype == CRONEVENT) &&
2105 	    (next_event->u == u)) {
2106 		next_event = NULL;
2107 	}
2108 	e2 = u->ctevents;
2109 	while (e2 != NULL) {
2110 		free(e2->cmd);
2111 		rel_shared(e2->of.ct.tz);
2112 		rel_shared(e2->of.ct.shell);
2113 		rel_shared(e2->of.ct.home);
2114 		free(e2->of.ct.minute);
2115 		free(e2->of.ct.hour);
2116 		free(e2->of.ct.daymon);
2117 		free(e2->of.ct.month);
2118 		free(e2->of.ct.dayweek);
2119 		if (e2->of.ct.input != NULL)
2120 			free(e2->of.ct.input);
2121 		e3 = e2->link;
2122 		free(e2);
2123 		e2 = e3;
2124 	}
2125 	u->ctevents = NULL;
2126 }
2127 
2128 
2129 static struct usr *
2130 find_usr(char *uname)
2131 {
2132 	struct usr *u;
2133 
2134 	u = uhead;
2135 	while (u != NULL) {
2136 		if (strcmp(u->name, uname) == 0)
2137 			return (u);
2138 		u = u->nextusr;
2139 	}
2140 	return (NULL);
2141 }
2142 
2143 /*
2144  * Execute cron command or at/batch job.
2145  * If ever a premature return is added to this function pay attention to
2146  * free at_cmdfile and outfile plus jobname buffers of the runinfo structure.
2147  */
2148 static int
2149 ex(struct event *e)
2150 {
2151 	int r;
2152 	int fd;
2153 	pid_t rfork;
2154 	FILE *atcmdfp;
2155 	char mailvar[4];
2156 	char *at_cmdfile = NULL;
2157 	struct stat buf;
2158 	struct queue *qp;
2159 	struct runinfo *rp;
2160 	struct project proj, *pproj = NULL;
2161 	union {
2162 		struct {
2163 			char buf[PROJECT_BUFSZ];
2164 			char buf2[PROJECT_BUFSZ];
2165 		} p;
2166 		char error[CANT_STR_LEN + PATH_MAX];
2167 	} bufs;
2168 	char *tmpfile;
2169 	FILE *fptr;
2170 	time_t dhltime;
2171 	projid_t projid;
2172 	int projflag = 0;
2173 	char *home;
2174 	char *sh;
2175 
2176 	qp = &qt[e->etype];	/* set pointer to queue defs */
2177 	if (qp->nrun >= qp->njob) {
2178 		msg("%c queue max run limit reached", e->etype + 'a');
2179 		resched(qp->nwait);
2180 		return (0);
2181 	}
2182 
2183 	rp = rinfo_get(0); /* allocating a new runinfo struct */
2184 
2185 	/*
2186 	 * the tempnam() function uses malloc(3C) to allocate space for the
2187 	 * constructed file name, and returns a pointer to this area, which
2188 	 * is assigned to rp->outfile. Here rp->outfile is not overwritten.
2189 	 */
2190 
2191 	rp->outfile = tempnam(TMPDIR, PFX);
2192 	rp->jobtype = e->etype;
2193 	if (e->etype == CRONEVENT) {
2194 		rp->jobname = xmalloc(strlen(e->cmd) + 1);
2195 		(void) strcpy(rp->jobname, e->cmd);
2196 		/* "cron" jobs only produce mail if there's output */
2197 		rp->mailwhendone = 0;
2198 	} else {
2199 		at_cmdfile = xmalloc(strlen(ATDIR) + strlen(e->cmd) + 2);
2200 		(void) sprintf(at_cmdfile, "%s/%s", ATDIR, e->cmd);
2201 		if ((atcmdfp = fopen(at_cmdfile, "r")) == NULL) {
2202 			if (errno == ENAMETOOLONG) {
2203 				if (chdir(ATDIR) == 0)
2204 					cron_unlink(e->cmd);
2205 			} else {
2206 				cron_unlink(at_cmdfile);
2207 			}
2208 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECAT);
2209 			free(at_cmdfile);
2210 			rinfo_free(rp);
2211 			return (0);
2212 		}
2213 		rp->jobname = xmalloc(strlen(at_cmdfile) + 1);
2214 		(void) strcpy(rp->jobname, at_cmdfile);
2215 
2216 		/*
2217 		 * Skip over the first two lines.
2218 		 */
2219 		(void) fscanf(atcmdfp, "%*[^\n]\n");
2220 		(void) fscanf(atcmdfp, "%*[^\n]\n");
2221 		if (fscanf(atcmdfp, ": notify by mail: %3s%*[^\n]\n",
2222 		    mailvar) == 1) {
2223 			/*
2224 			 * Check to see if we should always send mail
2225 			 * to the owner.
2226 			 */
2227 			rp->mailwhendone = (strcmp(mailvar, "yes") == 0);
2228 		} else {
2229 			rp->mailwhendone = 0;
2230 		}
2231 
2232 		if (fscanf(atcmdfp, "\n: project: %d\n", &projid) == 1) {
2233 			projflag = 1;
2234 		}
2235 		(void) fclose(atcmdfp);
2236 	}
2237 
2238 	/*
2239 	 * we make sure that the system time
2240 	 * hasn't drifted backwards. if it has, el_add() is now
2241 	 * called, to make sure that the event queue is back in order,
2242 	 * and we set the delayed flag. cron will pick up the request
2243 	 * later on at the proper time.
2244 	 */
2245 	dhltime = time(NULL);
2246 	if ((dhltime - e->time) < 0) {
2247 		msg("clock time drifted backwards!\n");
2248 		if (next_event->etype == CRONEVENT) {
2249 			msg("correcting cron event\n");
2250 			next_event->time = next_time(next_event, dhltime);
2251 			switch (el_add(next_event, next_event->time,
2252 			    (next_event->u)->ctid)) {
2253 			case -1:
2254 				ignore_msg("ex", "cron", next_event);
2255 				break;
2256 			case -2: /* event time lower than init time */
2257 				reset_needed = 1;
2258 				break;
2259 			}
2260 		} else { /* etype == ATEVENT */
2261 			msg("correcting batch event\n");
2262 			if (el_add(next_event, next_event->time,
2263 			    next_event->of.at.eventid) < 0) {
2264 				ignore_msg("ex", "at", next_event);
2265 			}
2266 		}
2267 		delayed++;
2268 		t_old = time(NULL);
2269 		free(at_cmdfile);
2270 		rinfo_free(rp);
2271 		return (0);
2272 	}
2273 
2274 	if ((rfork = fork()) == (pid_t)-1) {
2275 		reap_child();
2276 		if ((rfork = fork()) == (pid_t)-1) {
2277 			msg("cannot fork");
2278 			free(at_cmdfile);
2279 			rinfo_free(rp);
2280 			resched(60);
2281 			(void) sleep(30);
2282 			return (0);
2283 		}
2284 	}
2285 	if (rfork) {		/* parent process */
2286 		contract_abandon_latest(rfork);
2287 
2288 		++qp->nrun;
2289 		rp->pid = rfork;
2290 		rp->que = e->etype;
2291 		if (e->etype != CRONEVENT)
2292 			(e->u)->aruncnt++;
2293 		else
2294 			(e->u)->cruncnt++;
2295 		rp->rusr = (e->u);
2296 		logit(BCHAR, rp, 0);
2297 		free(at_cmdfile);
2298 
2299 		return (0);
2300 	}
2301 
2302 	child_sigreset();
2303 	contract_clear_template();
2304 
2305 	if (e->etype != CRONEVENT) {
2306 		/* open jobfile as stdin to shell */
2307 		if (stat(at_cmdfile, &buf)) {
2308 			if (errno == ENAMETOOLONG) {
2309 				if (chdir(ATDIR) == 0)
2310 					cron_unlink(e->cmd);
2311 			} else
2312 				cron_unlink(at_cmdfile);
2313 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
2314 			exit(1);
2315 		}
2316 		if (!(buf.st_mode&ISUID)) {
2317 			/*
2318 			 * if setuid bit off, original owner has
2319 			 * given this file to someone else
2320 			 */
2321 			cron_unlink(at_cmdfile);
2322 			exit(1);
2323 		}
2324 		if ((fd = open(at_cmdfile, O_RDONLY)) == -1) {
2325 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
2326 			cron_unlink(at_cmdfile);
2327 			exit(1);
2328 		}
2329 		if (fd != 0) {
2330 			(void) dup2(fd, 0);
2331 			(void) close(fd);
2332 		}
2333 		/*
2334 		 * retrieve the project id of the at job and convert it
2335 		 * to a project name.  fail if it's not a valid project
2336 		 * or if the user isn't a member of the project.
2337 		 */
2338 		if (projflag == 1) {
2339 			if ((pproj = getprojbyid(projid, &proj,
2340 			    (void *)&bufs.p.buf,
2341 			    sizeof (bufs.p.buf))) == NULL ||
2342 			    !inproj(e->u->name, pproj->pj_name,
2343 			    bufs.p.buf2, sizeof (bufs.p.buf2))) {
2344 				cron_unlink(at_cmdfile);
2345 				mail((e->u)->name, BADPROJID, ERR_CANTEXECAT);
2346 				exit(1);
2347 			}
2348 		}
2349 	}
2350 
2351 	/*
2352 	 * Put process in a new session, and create a new task.
2353 	 */
2354 	if (setsid() < 0) {
2355 		msg("setsid failed with errno = %d. job failed (%s)"
2356 		    " for user %s", errno, e->cmd, e->u->name);
2357 		if (e->etype != CRONEVENT)
2358 			cron_unlink(at_cmdfile);
2359 		exit(1);
2360 	}
2361 
2362 	/*
2363 	 * set correct user identification and check their account
2364 	 */
2365 	r = set_user_cred(e->u, pproj);
2366 	if (r == VUC_EXPIRED) {
2367 		msg("user (%s) account is expired", e->u->name);
2368 		audit_cron_user_acct_expired(e->u->name);
2369 		clean_out_user(e->u);
2370 		exit(1);
2371 	}
2372 	if (r == VUC_NEW_AUTH) {
2373 		msg("user (%s) password has expired", e->u->name);
2374 		audit_cron_user_acct_expired(e->u->name);
2375 		clean_out_user(e->u);
2376 		exit(1);
2377 	}
2378 	if (r != VUC_OK) {
2379 		msg("bad user (%s)", e->u->name);
2380 		audit_cron_bad_user(e->u->name);
2381 		clean_out_user(e->u);
2382 		exit(1);
2383 	}
2384 	/*
2385 	 * check user and initialize the supplementary group access list.
2386 	 * bugid 1230784: deleted from parent to avoid cron hang. Now
2387 	 * only child handles the call.
2388 	 */
2389 
2390 	if (verify_user_cred(e->u) != VUC_OK ||
2391 	    setgid(e->u->gid) == -1 ||
2392 	    initgroups(e->u->name, e->u->gid) == -1) {
2393 		msg("bad user (%s) or setgid failed (%s)",
2394 		    e->u->name, e->u->name);
2395 		audit_cron_bad_user(e->u->name);
2396 		clean_out_user(e->u);
2397 		exit(1);
2398 	}
2399 
2400 	if ((e->u)->uid == 0) { /* set default path */
2401 		/* path settable in defaults file */
2402 		envinit[2] = supath;
2403 	} else {
2404 		envinit[2] = path;
2405 	}
2406 
2407 	if (e->etype != CRONEVENT) {
2408 		r = audit_cron_session(e->u->name, NULL,
2409 		    e->u->uid, e->u->gid, at_cmdfile);
2410 		cron_unlink(at_cmdfile);
2411 	} else {
2412 		r = audit_cron_session(e->u->name, CRONDIR,
2413 		    e->u->uid, e->u->gid, NULL);
2414 	}
2415 	if (r != 0) {
2416 		msg("cron audit problem. job failed (%s) for user %s",
2417 		    e->cmd, e->u->name);
2418 		exit(1);
2419 	}
2420 
2421 	audit_cron_new_job(e->cmd, e->etype, (void *)e);
2422 
2423 	if (setuid(e->u->uid) == -1)  {
2424 		msg("setuid failed (%s)", e->u->name);
2425 		clean_out_user(e->u);
2426 		exit(1);
2427 	}
2428 
2429 	if (e->etype == CRONEVENT) {
2430 		/* check for standard input to command	*/
2431 		if (e->of.ct.input != NULL) {
2432 			if ((tmpfile = strdup(TMPINFILE)) == NULL) {
2433 				mail((e->u)->name, MALLOCERR,
2434 				    ERR_CANTEXECCRON);
2435 				exit(1);
2436 			}
2437 			if ((fd = mkstemp(tmpfile)) == -1 ||
2438 			    (fptr = fdopen(fd, "w")) == NULL) {
2439 				mail((e->u)->name, NOSTDIN,
2440 				    ERR_CANTEXECCRON);
2441 				cron_unlink(tmpfile);
2442 				free(tmpfile);
2443 				exit(1);
2444 			}
2445 			if ((fwrite(e->of.ct.input, sizeof (char),
2446 			    strlen(e->of.ct.input), fptr)) !=
2447 			    strlen(e->of.ct.input)) {
2448 				mail((e->u)->name, NOSTDIN, ERR_CANTEXECCRON);
2449 				cron_unlink(tmpfile);
2450 				free(tmpfile);
2451 				(void) close(fd);
2452 				(void) fclose(fptr);
2453 				exit(1);
2454 			}
2455 			if (fseek(fptr, (off_t)0, SEEK_SET) != -1) {
2456 				if (fd != 0) {
2457 					(void) dup2(fd, 0);
2458 					(void) close(fd);
2459 				}
2460 			}
2461 			cron_unlink(tmpfile);
2462 			free(tmpfile);
2463 			(void) fclose(fptr);
2464 		} else if ((fd = open("/dev/null", O_RDONLY)) > 0) {
2465 			(void) dup2(fd, 0);
2466 			(void) close(fd);
2467 		}
2468 	}
2469 
2470 	/* redirect stdout and stderr for the shell	*/
2471 	if ((fd = open(rp->outfile, O_WRONLY|O_CREAT|O_EXCL, OUTMODE)) == 1)
2472 		fd = open("/dev/null", O_WRONLY);
2473 
2474 	if (fd >= 0 && fd != 1)
2475 		(void) dup2(fd, 1);
2476 
2477 	if (fd >= 0 && fd != 2) {
2478 		(void) dup2(fd, 2);
2479 		if (fd != 1)
2480 			(void) close(fd);
2481 	}
2482 
2483 	if (e->etype == CRONEVENT && e->of.ct.home != NULL) {
2484 		home = (char *)get_obj(e->of.ct.home);
2485 	} else {
2486 		home = (e->u)->home;
2487 	}
2488 	(void) strlcat(homedir, home, sizeof (homedir));
2489 	(void) strlcat(logname, (e->u)->name, sizeof (logname));
2490 	environ = envinit;
2491 	if (chdir(home) == -1) {
2492 		snprintf(bufs.error, sizeof (bufs.error), CANTCDHOME, home);
2493 		mail((e->u)->name, bufs.error,
2494 		    e->etype == CRONEVENT ? ERR_CANTEXECCRON :
2495 		    ERR_CANTEXECAT);
2496 		exit(1);
2497 	}
2498 #ifdef TESTING
2499 	exit(1);
2500 #endif
2501 	/*
2502 	 * make sure that all file descriptors EXCEPT 0, 1 and 2
2503 	 * will be closed.
2504 	 */
2505 	closefrom(3);
2506 
2507 	if ((e->u)->uid != 0)
2508 		(void) nice(qp->nice);
2509 	if (e->etype == CRONEVENT) {
2510 		if (e->of.ct.tz) {
2511 			(void) putenv((char *)get_obj(e->of.ct.tz));
2512 		}
2513 		if (e->of.ct.shell) {
2514 			char *name;
2515 
2516 			sh = (char *)get_obj(e->of.ct.shell);
2517 			name = strrchr(sh, '/');
2518 			if (name == NULL)
2519 				name = sh;
2520 			else
2521 				name++;
2522 
2523 			(void) putenv(sh);
2524 			sh += strlen(ENV_SHELL);
2525 			(void) execl(sh, name, "-c", e->cmd, 0);
2526 		} else {
2527 			(void) execl(SHELL, "sh", "-c", e->cmd, 0);
2528 			sh = SHELL;
2529 		}
2530 	} else {		/* type == ATEVENT */
2531 		(void) execl(SHELL, "sh", 0);
2532 		sh = SHELL;
2533 	}
2534 	snprintf(bufs.error, sizeof (bufs.error), CANTEXECSH, sh);
2535 	mail((e->u)->name, bufs.error,
2536 	    e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT);
2537 	exit(1);
2538 	/*NOTREACHED*/
2539 }
2540 
2541 /*
2542  * Main idle loop.
2543  * When timed out to run the job, return 0.
2544  * If for some reasons we need to reschedule jobs, return 1.
2545  */
2546 static int
2547 idle(long t)
2548 {
2549 	time_t	now;
2550 
2551 	refresh = 0;
2552 
2553 	while (t > 0L) {
2554 		if (msg_wait(t) != 0) {
2555 			/* we need to run next job immediately */
2556 			return (0);
2557 		}
2558 
2559 		reap_child();
2560 
2561 		if (refresh) {
2562 			/* We got THAW or REFRESH message  */
2563 			return (1);
2564 		}
2565 
2566 		now = time(NULL);
2567 		if (last_time > now) {
2568 			/* clock has been reset to backward */
2569 			return (1);
2570 		}
2571 
2572 		if (next_event == NULL && !el_empty()) {
2573 			next_event = (struct event *)el_first();
2574 		}
2575 
2576 		if (next_event == NULL)
2577 			t = INFINITY;
2578 		else
2579 			t = (long)next_event->time - now;
2580 	}
2581 	return (0);
2582 }
2583 
2584 /*
2585  * This used to be in the idle(), but moved to the separate function.
2586  * This called from various place when cron needs to reap the
2587  * child. It includes the situation that cron hit maxrun, and needs
2588  * to reschedule the job.
2589  */
2590 static void
2591 reap_child()
2592 {
2593 	pid_t	pid;
2594 	int	prc;
2595 	struct	runinfo	*rp;
2596 
2597 	for (;;) {
2598 		pid = waitpid((pid_t)-1, &prc, WNOHANG);
2599 		if (pid <= 0)
2600 			break;
2601 #ifdef DEBUG
2602 		fprintf(stderr,
2603 		    "wait returned %x for process %d\n", prc, pid);
2604 #endif
2605 		if ((rp = rinfo_get(pid)) == NULL) {
2606 			if (miscpid_delete(pid) == 0) {
2607 				/* not found in anywhere */
2608 				msg(PIDERR, pid);
2609 			}
2610 		} else if (rp->que == ZOMB) {
2611 			(void) unlink(rp->outfile);
2612 			rinfo_free(rp);
2613 		} else {
2614 			cleanup(rp, prc);
2615 		}
2616 	}
2617 }
2618 
2619 static void
2620 cleanup(struct runinfo *pr, int rc)
2621 {
2622 	int	nextfork = 1;
2623 	struct	usr	*p;
2624 	struct	stat	buf;
2625 
2626 	logit(ECHAR, pr, rc);
2627 	--qt[pr->que].nrun;
2628 	p = pr->rusr;
2629 	if (pr->que != CRONEVENT)
2630 		--p->aruncnt;
2631 	else
2632 		--p->cruncnt;
2633 
2634 	if (lstat(pr->outfile, &buf) == 0) {
2635 		if (!S_ISLNK(buf.st_mode) &&
2636 		    (buf.st_size > 0 || pr->mailwhendone)) {
2637 			/* mail user stdout and stderr */
2638 			for (;;) {
2639 				if ((pr->pid = fork()) < 0) {
2640 					/*
2641 					 * if fork fails try forever in doubling
2642 					 * retry times, up to 16 seconds
2643 					 */
2644 					(void) sleep(nextfork);
2645 					if (nextfork < 16)
2646 						nextfork += nextfork;
2647 					continue;
2648 				} else if (pr->pid == 0) {
2649 					child_sigreset();
2650 					contract_clear_template();
2651 
2652 					mail_result(p, pr, buf.st_size);
2653 					/* NOTREACHED */
2654 				} else {
2655 					contract_abandon_latest(pr->pid);
2656 					pr->que = ZOMB;
2657 					break;
2658 				}
2659 			}
2660 		} else {
2661 			(void) unlink(pr->outfile);
2662 			rinfo_free(pr);
2663 		}
2664 	} else {
2665 		rinfo_free(pr);
2666 	}
2667 
2668 	free_if_unused(p);
2669 }
2670 
2671 /*
2672  * Mail stdout and stderr of a job to user. Get uid for real user and become
2673  * that person. We do this so that mail won't come from root since this
2674  * could be a security hole. If failure, quit - don't send mail as root.
2675  */
2676 static void
2677 mail_result(struct usr *p, struct runinfo *pr, size_t filesize)
2678 {
2679 	struct	passwd	*ruser_ids;
2680 	FILE	*mailpipe;
2681 	FILE	*st;
2682 	struct utsname	name;
2683 	int	nbytes;
2684 	char	iobuf[BUFSIZ];
2685 	char	*cmd;
2686 	char	*lowname = (pr->jobtype == CRONEVENT ? "cron" : "at");
2687 
2688 	(void) uname(&name);
2689 	if ((ruser_ids = getpwnam(p->name)) == NULL)
2690 		exit(0);
2691 	(void) setuid(ruser_ids->pw_uid);
2692 
2693 	cmd = xmalloc(strlen(MAIL) + strlen(p->name)+2);
2694 	(void) sprintf(cmd, "%s %s", MAIL, p->name);
2695 	mailpipe = popen(cmd, "w");
2696 	free(cmd);
2697 	if (mailpipe == NULL)
2698 		exit(127);
2699 	(void) fprintf(mailpipe, "To: %s\n", p->name);
2700 	(void) fprintf(mailpipe, "Subject: %s <%s@%s> %s\n",
2701 	    (pr->jobtype == CRONEVENT ? "Cron" : "At"),
2702 	    p->name, name.nodename, pr->jobname);
2703 
2704 	/*
2705 	 * RFC3834 (Section 5) defines the Auto-Submitted header to prevent
2706 	 * vacation replies, et al, from being sent in response to
2707 	 * machine-generated mail.
2708 	 */
2709 	(void) fprintf(mailpipe, "Auto-Submitted: auto-generated\n");
2710 
2711 	/*
2712 	 * Additional headers for mail filtering and diagnostics:
2713 	 */
2714 	(void) fprintf(mailpipe, "X-Mailer: cron (%s %s)\n", name.sysname,
2715 	    name.release);
2716 	(void) fprintf(mailpipe, "X-Cron-User: %s\n", p->name);
2717 	(void) fprintf(mailpipe, "X-Cron-Host: %s\n", name.nodename);
2718 	(void) fprintf(mailpipe, "X-Cron-Job-Name: %s\n", pr->jobname);
2719 	(void) fprintf(mailpipe, "X-Cron-Job-Type: %s\n", lowname);
2720 
2721 	/*
2722 	 * Message Body:
2723 	 *
2724 	 * (Temporary file is fopen'ed with "r", secure open.)
2725 	 */
2726 	(void) fprintf(mailpipe, "\n");
2727 	if (filesize > 0 &&
2728 	    (st = fopen(pr->outfile, "r")) != NULL) {
2729 		while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0)
2730 			(void) fwrite(iobuf, sizeof (char), nbytes, mailpipe);
2731 		(void) fclose(st);
2732 	} else {
2733 		(void) fprintf(mailpipe, "Job completed with no output.\n");
2734 	}
2735 	(void) pclose(mailpipe);
2736 	exit(0);
2737 }
2738 
2739 static int
2740 msg_wait(long tim)
2741 {
2742 	struct	message	msg;
2743 	int	cnt;
2744 	time_t	reftime;
2745 	fd_set	fds;
2746 	struct timespec tout, *toutp;
2747 	static int	pending_msg;
2748 	static time_t	pending_reftime;
2749 
2750 	if (pending_msg) {
2751 		process_msg(&msgbuf, pending_reftime);
2752 		pending_msg = 0;
2753 		return (0);
2754 	}
2755 
2756 	FD_ZERO(&fds);
2757 	FD_SET(msgfd, &fds);
2758 
2759 	toutp = NULL;
2760 	if (tim != INFINITY) {
2761 #ifdef CRON_MAXSLEEP
2762 		/*
2763 		 * CRON_MAXSLEEP can be defined to have cron periodically wake
2764 		 * up, so that cron can detect a change of TOD and adjust the
2765 		 * sleep time more frequently.
2766 		 */
2767 		tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim;
2768 #endif
2769 		tout.tv_nsec = 0;
2770 		tout.tv_sec = tim;
2771 		toutp = &tout;
2772 	}
2773 
2774 	cnt = pselect(msgfd + 1, &fds, NULL, NULL, toutp, &defmask);
2775 	if (cnt == -1 && errno != EINTR)
2776 		perror("! pselect");
2777 
2778 	/* pselect timeout or interrupted */
2779 	if (cnt <= 0)
2780 		return (0);
2781 
2782 	errno = 0;
2783 	if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) {
2784 		if (cnt != -1 || errno != EAGAIN)
2785 			perror("! read");
2786 		return (0);
2787 	}
2788 	reftime = time(NULL);
2789 	if (next_event != NULL && reftime >= next_event->time) {
2790 		/*
2791 		 * we need to run the job before reloading crontab.
2792 		 */
2793 		(void) memcpy(&msgbuf, &msg, sizeof (msg));
2794 		pending_msg = 1;
2795 		pending_reftime = reftime;
2796 		return (1);
2797 	}
2798 	process_msg(&msg, reftime);
2799 	return (0);
2800 }
2801 
2802 /*
2803  * process the message supplied via pipe. This will be called either
2804  * immediately after cron read the message from pipe, or idle time
2805  * if the message was pending due to the job execution.
2806  */
2807 static void
2808 process_msg(struct message *pmsg, time_t reftime)
2809 {
2810 	if (pmsg->etype == 0)
2811 		return;
2812 
2813 	switch (pmsg->etype) {
2814 	case AT:
2815 		if (pmsg->action == DELETE)
2816 			del_atjob(pmsg->fname, pmsg->logname);
2817 		else
2818 			mod_atjob(pmsg->fname, (time_t)0);
2819 		break;
2820 	case CRON:
2821 		if (pmsg->action == DELETE)
2822 			del_ctab(pmsg->fname);
2823 		else
2824 			mod_ctab(pmsg->fname, reftime);
2825 		break;
2826 	case REFRESH:
2827 		refresh = 1;
2828 		pmsg->etype = 0;
2829 		return;
2830 	default:
2831 		msg("message received - bad format");
2832 		break;
2833 	}
2834 	if (next_event != NULL) {
2835 		if (next_event->etype == CRONEVENT) {
2836 			switch (el_add(next_event, next_event->time,
2837 			    (next_event->u)->ctid)) {
2838 			case -1:
2839 				ignore_msg("process_msg", "cron", next_event);
2840 				break;
2841 			case -2: /* event time lower than init time */
2842 				reset_needed = 1;
2843 				break;
2844 			}
2845 		} else { /* etype == ATEVENT */
2846 			if (el_add(next_event, next_event->time,
2847 			    next_event->of.at.eventid) < 0) {
2848 				ignore_msg("process_msg", "at", next_event);
2849 			}
2850 		}
2851 		next_event = NULL;
2852 	}
2853 	(void) fflush(stdout);
2854 	pmsg->etype = 0;
2855 }
2856 
2857 /*
2858  * Allocate a new or find an existing runinfo structure
2859  */
2860 static struct runinfo *
2861 rinfo_get(pid_t pid)
2862 {
2863 	struct runinfo *rp;
2864 
2865 	if (pid == 0) {		/* allocate a new entry */
2866 		rp = xcalloc(1, sizeof (struct runinfo));
2867 		rp->next = rthead;	/* link the entry into the list */
2868 		rthead = rp;
2869 		return (rp);
2870 	}
2871 	/* search the list for an existing entry */
2872 	for (rp = rthead; rp != NULL; rp = rp->next) {
2873 		if (rp->pid == pid)
2874 			break;
2875 	}
2876 	return (rp);
2877 }
2878 
2879 /*
2880  * Free a runinfo structure and its associated memory
2881  */
2882 static void
2883 rinfo_free(struct runinfo *entry)
2884 {
2885 	struct runinfo **rpp;
2886 	struct runinfo *rp;
2887 
2888 #ifdef DEBUG
2889 	(void) fprintf(stderr, "freeing job %s\n", entry->jobname);
2890 #endif
2891 	for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) {
2892 		if (rp == entry) {
2893 			*rpp = rp->next;	/* unlink the entry */
2894 			free(rp->outfile);
2895 			free(rp->jobname);
2896 			free(rp);
2897 			break;
2898 		}
2899 	}
2900 }
2901 
2902 static void
2903 thaw_handler(int sig __unused)
2904 {
2905 	refresh = 1;
2906 }
2907 
2908 
2909 static void
2910 cronend(int sig __unused)
2911 {
2912 	crabort("SIGTERM", REMOVE_FIFO);
2913 }
2914 
2915 static void
2916 child_handler(int sig __unused)
2917 {
2918 	;
2919 }
2920 
2921 static void
2922 child_sigreset(void)
2923 {
2924 	(void) signal(SIGCLD, SIG_DFL);
2925 	(void) sigprocmask(SIG_SETMASK, &defmask, NULL);
2926 }
2927 
2928 /*
2929  * crabort() - handle exits out of cron
2930  */
2931 static void
2932 crabort(char *mssg, int action)
2933 {
2934 	int	c;
2935 
2936 	if (action & REMOVE_FIFO) {
2937 		/* FIFO vanishes when cron finishes */
2938 		if (unlink(FIFO) < 0)
2939 			perror("cron could not unlink FIFO");
2940 	}
2941 
2942 	if (action & CONSOLE_MSG) {
2943 		/* write error msg to console */
2944 		if ((c = open(CONSOLE, O_WRONLY)) >= 0) {
2945 			(void) write(c, "cron aborted: ", 14);
2946 			(void) write(c, mssg, strlen(mssg));
2947 			(void) write(c, "\n", 1);
2948 			(void) close(c);
2949 		}
2950 	}
2951 
2952 	/* always log the message */
2953 	msg(mssg);
2954 	msg("******* CRON ABORTED ********");
2955 	exit(1);
2956 }
2957 
2958 /*
2959  * msg() - time-stamped error reporting function
2960  */
2961 /*PRINTFLIKE1*/
2962 static void
2963 msg(char *fmt, ...)
2964 {
2965 	va_list args;
2966 	time_t	t;
2967 
2968 	t = time(NULL);
2969 
2970 	(void) fflush(stdout);
2971 
2972 	(void) fprintf(stderr, "! ");
2973 
2974 	va_start(args, fmt);
2975 	(void) vfprintf(stderr, fmt, args);
2976 	va_end(args);
2977 
2978 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
2979 	(void) fprintf(stderr, " %s\n", timebuf);
2980 
2981 	(void) fflush(stderr);
2982 }
2983 
2984 static void
2985 ignore_msg(char *func_name, char *job_type, struct event *event)
2986 {
2987 	msg("%s: ignoring %s job (user: %s, cmd: %s, time: %ld)",
2988 	    func_name, job_type,
2989 	    event->u->name ? event->u->name : "unknown",
2990 	    event->cmd ? event->cmd : "unknown",
2991 	    event->time);
2992 }
2993 
2994 static void
2995 logit(int cc, struct runinfo *rp, int rc)
2996 {
2997 	time_t t;
2998 	int    ret;
2999 
3000 	if (!log)
3001 		return;
3002 
3003 	t = time(NULL);
3004 	if (cc == BCHAR)
3005 		(void) printf("%c  CMD: %s\n", cc, next_event->cmd);
3006 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
3007 	(void) printf("%c  %s %u %c %s",
3008 	    cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf);
3009 	if ((ret = TSTAT(rc)) != 0)
3010 		(void) printf(" ts=%d", ret);
3011 	if ((ret = RCODE(rc)) != 0)
3012 		(void) printf(" rc=%d", ret);
3013 	(void) putchar('\n');
3014 	(void) fflush(stdout);
3015 }
3016 
3017 static void
3018 resched(int delay)
3019 {
3020 	time_t	nt;
3021 
3022 	/* run job at a later time */
3023 	nt = next_event->time + delay;
3024 	if (next_event->etype == CRONEVENT) {
3025 		next_event->time = next_time(next_event, (time_t)0);
3026 		if (nt < next_event->time)
3027 			next_event->time = nt;
3028 		switch (el_add(next_event, next_event->time,
3029 		    (next_event->u)->ctid)) {
3030 		case -1:
3031 			ignore_msg("resched", "cron", next_event);
3032 			break;
3033 		case -2: /* event time lower than init time */
3034 			reset_needed = 1;
3035 			break;
3036 		}
3037 		delayed = 1;
3038 		msg("rescheduling a cron job");
3039 		return;
3040 	}
3041 	add_atevent(next_event->u, next_event->cmd, nt, next_event->etype);
3042 	msg("rescheduling at job");
3043 }
3044 
3045 static void
3046 quedefs(int action)
3047 {
3048 	int	i;
3049 	int	j;
3050 	char	qbuf[QBUFSIZ];
3051 	FILE	*fd;
3052 
3053 	/* set up default queue definitions */
3054 	for (i = 0; i < NQUEUE; i++) {
3055 		qt[i].njob = qd.njob;
3056 		qt[i].nice = qd.nice;
3057 		qt[i].nwait = qd.nwait;
3058 	}
3059 	if (action == DEFAULT)
3060 		return;
3061 	if ((fd = fopen(QUEDEFS, "r")) == NULL) {
3062 		msg("cannot open quedefs file");
3063 		msg("using default queue definitions");
3064 		return;
3065 	}
3066 	while (fgets(qbuf, QBUFSIZ, fd) != NULL) {
3067 		if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.')
3068 			continue;
3069 		parsqdef(&qbuf[2]);
3070 		qt[j].njob = qq.njob;
3071 		qt[j].nice = qq.nice;
3072 		qt[j].nwait = qq.nwait;
3073 	}
3074 	(void) fclose(fd);
3075 }
3076 
3077 static void
3078 parsqdef(char *name)
3079 {
3080 	int i;
3081 
3082 	qq = qd;
3083 	while (*name) {
3084 		i = 0;
3085 		while (isdigit(*name)) {
3086 			i *= 10;
3087 			i += *name++ - '0';
3088 		}
3089 		switch (*name++) {
3090 		case JOBF:
3091 			qq.njob = i;
3092 			break;
3093 		case NICEF:
3094 			qq.nice = i;
3095 			break;
3096 		case WAITF:
3097 			qq.nwait = i;
3098 			break;
3099 		}
3100 	}
3101 }
3102 
3103 /*
3104  * defaults - read defaults from /etc/default/cron
3105  */
3106 static void
3107 defaults()
3108 {
3109 	int  flags;
3110 	char *deflog;
3111 	char *hz, *tz;
3112 
3113 	/*
3114 	 * get HZ value for environment
3115 	 */
3116 	if ((hz = getenv("HZ")) == (char *)NULL)
3117 		(void) sprintf(hzname, "HZ=%d", HZ);
3118 	else
3119 		(void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz);
3120 	/*
3121 	 * get TZ value for environment
3122 	 */
3123 	(void) snprintf(tzone, sizeof (tzone), "TZ=%s",
3124 	    ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ);
3125 
3126 	if (defopen(DEFFILE) == 0) {
3127 		/* ignore case */
3128 		flags = defcntl(DC_GETFLAGS, 0);
3129 		TURNOFF(flags, DC_CASE);
3130 		(void) defcntl(DC_SETFLAGS, flags);
3131 
3132 		if (((deflog = defread("CRONLOG=")) == NULL) ||
3133 		    (*deflog == 'N') || (*deflog == 'n'))
3134 			log = 0;
3135 		else
3136 			log = 1;
3137 		/* fix for 1087611 - allow paths to be set in defaults file */
3138 		if ((Def_path = defread("PATH=")) != NULL) {
3139 			(void) strlcat(path, Def_path, LINE_MAX);
3140 		} else {
3141 			(void) strlcpy(path, NONROOTPATH, LINE_MAX);
3142 		}
3143 		if ((Def_supath = defread("SUPATH=")) != NULL) {
3144 			(void) strlcat(supath, Def_supath, LINE_MAX);
3145 		} else {
3146 			(void) strlcpy(supath, ROOTPATH, LINE_MAX);
3147 		}
3148 		(void) defopen(NULL);
3149 	}
3150 }
3151 
3152 /*
3153  * Determine if a user entry for a job is still ok.  The method used here
3154  * is a lot (about 75x) faster than using setgrent() / getgrent()
3155  * endgrent().  It should be safe because we use the sysconf to determine
3156  * the max, and it tolerates the max being 0.
3157  */
3158 
3159 static int
3160 verify_user_cred(struct usr *u)
3161 {
3162 	struct passwd *pw;
3163 	size_t numUsrGrps = 0;
3164 	size_t numOrigGrps = 0;
3165 	size_t i;
3166 	int retval;
3167 
3168 	/*
3169 	 * Maximum number of groups a user may be in concurrently.  This
3170 	 * is a value which we obtain at runtime through a sysconf()
3171 	 * call.
3172 	 */
3173 
3174 	static size_t nGroupsMax = (size_t)-1;
3175 
3176 	/*
3177 	 * Arrays for cron user's group list, constructed at startup to
3178 	 * be nGroupsMax elements long, used for verifying user
3179 	 * credentials prior to execution.
3180 	 */
3181 
3182 	static gid_t *UsrGrps;
3183 	static gid_t *OrigGrps;
3184 
3185 	if ((pw = getpwnam(u->name)) == NULL)
3186 		return (VUC_BADUSER);
3187 	if (u->home != NULL) {
3188 		if (strcmp(u->home, pw->pw_dir) != 0) {
3189 			free(u->home);
3190 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
3191 			(void) strcpy(u->home, pw->pw_dir);
3192 		}
3193 	} else {
3194 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
3195 		(void) strcpy(u->home, pw->pw_dir);
3196 	}
3197 	if (u->uid != pw->pw_uid)
3198 		u->uid = pw->pw_uid;
3199 	if (u->gid != pw->pw_gid)
3200 		u->gid  = pw->pw_gid;
3201 
3202 	/*
3203 	 * Create the group id lists needed for job credential
3204 	 * verification.
3205 	 */
3206 
3207 	if (nGroupsMax == (size_t)-1) {
3208 		if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) {
3209 			UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t));
3210 			OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t));
3211 		}
3212 
3213 #ifdef DEBUG
3214 		(void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax);
3215 #endif
3216 	}
3217 
3218 #ifdef DEBUG
3219 	(void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name,
3220 	    pw->pw_uid);
3221 	(void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, "
3222 	    "u->gid = %d\n", pw->pw_gid, u->gid);
3223 #endif
3224 
3225 	retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP;
3226 
3227 	if (nGroupsMax > 0) {
3228 		numOrigGrps = getgroups(nGroupsMax, OrigGrps);
3229 
3230 		(void) initgroups(pw->pw_name, pw->pw_gid);
3231 		numUsrGrps = getgroups(nGroupsMax, UsrGrps);
3232 
3233 		for (i = 0; i < numUsrGrps; i++) {
3234 			if (UsrGrps[i] == u->gid) {
3235 				retval = VUC_OK;
3236 				break;
3237 			}
3238 		}
3239 
3240 		if (OrigGrps) {
3241 			(void) setgroups(numOrigGrps, OrigGrps);
3242 		}
3243 	}
3244 
3245 #ifdef DEBUG
3246 	(void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval);
3247 #endif
3248 
3249 	return (retval);
3250 }
3251 
3252 static int
3253 set_user_cred(const struct usr *u, struct project *pproj)
3254 {
3255 	static char *progname = "cron";
3256 	int r = 0, rval = 0;
3257 
3258 	if ((r = pam_start(progname, u->name, &pam_conv, &pamh))
3259 	    != PAM_SUCCESS) {
3260 #ifdef DEBUG
3261 		msg("pam_start returns %d\n", r);
3262 #endif
3263 		rval = VUC_BADUSER;
3264 		goto set_eser_cred_exit;
3265 	}
3266 
3267 	r = pam_acct_mgmt(pamh, 0);
3268 #ifdef DEBUG
3269 	msg("pam_acc_mgmt returns %d\n", r);
3270 #endif
3271 	if (r == PAM_ACCT_EXPIRED) {
3272 		rval = VUC_EXPIRED;
3273 		goto set_eser_cred_exit;
3274 	}
3275 	if (r == PAM_NEW_AUTHTOK_REQD) {
3276 		rval = VUC_NEW_AUTH;
3277 		goto set_eser_cred_exit;
3278 	}
3279 	if (r != PAM_SUCCESS) {
3280 		rval = VUC_BADUSER;
3281 		goto set_eser_cred_exit;
3282 	}
3283 
3284 	if (pproj != NULL) {
3285 		size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name);
3286 		char *buf = alloca(sz);
3287 
3288 		(void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name);
3289 		(void) pam_set_item(pamh, PAM_RESOURCE, buf);
3290 	}
3291 
3292 	r = pam_setcred(pamh, PAM_ESTABLISH_CRED);
3293 	if (r != PAM_SUCCESS)
3294 		rval = VUC_BADUSER;
3295 
3296 set_eser_cred_exit:
3297 	(void) pam_end(pamh, r);
3298 	return (rval);
3299 }
3300 
3301 static void
3302 clean_out_user(struct usr *u)
3303 {
3304 	if (next_event->u == u) {
3305 		next_event = NULL;
3306 	}
3307 
3308 	clean_out_ctab(u);
3309 	clean_out_atjobs(u);
3310 	free_if_unused(u);
3311 }
3312 
3313 static void
3314 clean_out_atjobs(struct usr *u)
3315 {
3316 	struct event *ev, *pv;
3317 
3318 	for (pv = NULL, ev = u->atevents;
3319 	    ev != NULL;
3320 	    pv = ev, ev = ev->link, free(pv)) {
3321 		el_remove(ev->of.at.eventid, 1);
3322 		if (cwd == AT)
3323 			cron_unlink(ev->cmd);
3324 		else {
3325 			char buf[PATH_MAX];
3326 			if (strlen(ATDIR) + strlen(ev->cmd) + 2
3327 			    < PATH_MAX) {
3328 				(void) sprintf(buf, "%s/%s", ATDIR, ev->cmd);
3329 				cron_unlink(buf);
3330 			}
3331 		}
3332 		free(ev->cmd);
3333 	}
3334 
3335 	u->atevents = NULL;
3336 }
3337 
3338 static void
3339 clean_out_ctab(struct usr *u)
3340 {
3341 	rm_ctevents(u);
3342 	el_remove(u->ctid, 0);
3343 	u->ctid = 0;
3344 	u->ctexists = 0;
3345 }
3346 
3347 static void
3348 cron_unlink(char *name)
3349 {
3350 	int r;
3351 
3352 	r = unlink(name);
3353 	if (r == 0 || (r == -1 && errno == ENOENT)) {
3354 		(void) audit_cron_delete_anc_file(name, NULL);
3355 	}
3356 }
3357 
3358 static void
3359 create_anc_ctab(struct event *e)
3360 {
3361 	if (audit_cron_create_anc_file(e->u->name,
3362 	    (cwd == CRON) ? NULL:CRONDIR,
3363 	    e->u->name, e->u->uid) == -1) {
3364 		process_anc_files(CRON_ANC_DELETE);
3365 		crabort("cannot create ancillary files for crontabs",
3366 		    REMOVE_FIFO|CONSOLE_MSG);
3367 	}
3368 }
3369 
3370 static void
3371 delete_anc_ctab(struct event *e)
3372 {
3373 	(void) audit_cron_delete_anc_file(e->u->name,
3374 	    (cwd == CRON) ? NULL:CRONDIR);
3375 }
3376 
3377 static void
3378 create_anc_atjob(struct event *e)
3379 {
3380 	if (!e->of.at.exists)
3381 		return;
3382 
3383 	if (audit_cron_create_anc_file(e->cmd,
3384 	    (cwd == AT) ? NULL:ATDIR,
3385 	    e->u->name, e->u->uid) == -1) {
3386 		process_anc_files(CRON_ANC_DELETE);
3387 		crabort("cannot create ancillary files for atjobs",
3388 		    REMOVE_FIFO|CONSOLE_MSG);
3389 	}
3390 }
3391 
3392 static void
3393 delete_anc_atjob(struct event *e)
3394 {
3395 	if (!e->of.at.exists)
3396 		return;
3397 
3398 	(void) audit_cron_delete_anc_file(e->cmd,
3399 	    (cwd == AT) ? NULL:ATDIR);
3400 }
3401 
3402 
3403 static void
3404 process_anc_files(int del)
3405 {
3406 	struct usr	*u = uhead;
3407 	struct event	*e;
3408 
3409 	if (!audit_cron_mode())
3410 		return;
3411 
3412 	for (;;) {
3413 		if (u->ctexists && u->ctevents != NULL) {
3414 			e = u->ctevents;
3415 			for (;;) {
3416 				if (del)
3417 					delete_anc_ctab(e);
3418 				else
3419 					create_anc_ctab(e);
3420 				if ((e = e->link) == NULL)
3421 					break;
3422 			}
3423 		}
3424 
3425 		if (u->atevents != NULL) {
3426 			e = u->atevents;
3427 			for (;;) {
3428 				if (del)
3429 					delete_anc_atjob(e);
3430 				else
3431 					create_anc_atjob(e);
3432 				if ((e = e->link) == NULL)
3433 					break;
3434 			}
3435 		}
3436 
3437 		if ((u = u->nextusr)  == NULL)
3438 			break;
3439 	}
3440 }
3441 
3442 static int
3443 cron_conv(int num_msg, struct pam_message **msgs,
3444     struct pam_response **response __unused, void *appdata_ptr __unused)
3445 {
3446 	struct pam_message	**m = msgs;
3447 	int i;
3448 
3449 	for (i = 0; i < num_msg; i++) {
3450 		switch (m[i]->msg_style) {
3451 		case PAM_ERROR_MSG:
3452 		case PAM_TEXT_INFO:
3453 			if (m[i]->msg != NULL) {
3454 				(void) msg("%s\n", m[i]->msg);
3455 			}
3456 			break;
3457 
3458 		default:
3459 			break;
3460 		}
3461 	}
3462 	return (0);
3463 }
3464 
3465 /*
3466  * Cron creates process for other than job. Mail process is the
3467  * one which rinfo does not cover. Therefore, miscpid will keep
3468  * track of the pids executed from cron. Otherwise, we will see
3469  * "unexpected pid returned.." messages appear in the log file.
3470  */
3471 static void
3472 miscpid_insert(pid_t pid)
3473 {
3474 	struct miscpid *mp;
3475 
3476 	mp = xmalloc(sizeof (*mp));
3477 	mp->pid = pid;
3478 	mp->next = miscpid_head;
3479 	miscpid_head = mp;
3480 }
3481 
3482 static int
3483 miscpid_delete(pid_t pid)
3484 {
3485 	struct miscpid *mp, *omp;
3486 	int found = 0;
3487 
3488 	omp = NULL;
3489 	for (mp = miscpid_head; mp != NULL; mp = mp->next) {
3490 		if (mp->pid == pid) {
3491 			found = 1;
3492 			break;
3493 		}
3494 		omp = mp;
3495 	}
3496 	if (found) {
3497 		if (omp != NULL)
3498 			omp->next = mp->next;
3499 		else
3500 			miscpid_head = NULL;
3501 		free(mp);
3502 	}
3503 	return (found);
3504 }
3505 
3506 /*
3507  * Establish contract terms such that all children are in abandoned
3508  * process contracts.
3509  */
3510 static void
3511 contract_set_template(void)
3512 {
3513 	int fd;
3514 
3515 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
3516 		crabort("cannot open process contract template",
3517 		    REMOVE_FIFO | CONSOLE_MSG);
3518 
3519 	if (ct_pr_tmpl_set_param(fd, 0) ||
3520 	    ct_tmpl_set_informative(fd, 0) ||
3521 	    ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR))
3522 		crabort("cannot establish contract template terms",
3523 		    REMOVE_FIFO | CONSOLE_MSG);
3524 
3525 	if (ct_tmpl_activate(fd))
3526 		crabort("cannot activate contract template",
3527 		    REMOVE_FIFO | CONSOLE_MSG);
3528 
3529 	(void) close(fd);
3530 }
3531 
3532 /*
3533  * Clear active process contract template.
3534  */
3535 static void
3536 contract_clear_template(void)
3537 {
3538 	int fd;
3539 
3540 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
3541 		crabort("cannot open process contract template",
3542 		    REMOVE_FIFO | CONSOLE_MSG);
3543 
3544 	if (ct_tmpl_clear(fd))
3545 		crabort("cannot clear contract template",
3546 		    REMOVE_FIFO | CONSOLE_MSG);
3547 
3548 	(void) close(fd);
3549 }
3550 
3551 /*
3552  * Abandon latest process contract unconditionally.  If we have leaked [some
3553  * critical amount], exit such that the kernel reaps our contracts.
3554  */
3555 static void
3556 contract_abandon_latest(pid_t pid)
3557 {
3558 	int r;
3559 	ctid_t id;
3560 	static uint_t cts_lost;
3561 
3562 	if (cts_lost > MAX_LOST_CONTRACTS)
3563 		crabort("repeated failure to abandon contracts",
3564 		    REMOVE_FIFO | CONSOLE_MSG);
3565 
3566 	if ((r = contract_latest(&id)) != 0) {
3567 		msg("could not obtain latest contract for "
3568 		    "PID %ld: %s", pid, strerror(r));
3569 		cts_lost++;
3570 		return;
3571 	}
3572 
3573 	if ((r = contract_abandon_id(id)) != 0) {
3574 		msg("could not abandon latest contract %ld: %s", id,
3575 		    strerror(r));
3576 		cts_lost++;
3577 		return;
3578 	}
3579 }
3580 
3581 static struct shared *
3582 create_shared(void *obj, void * (*obj_alloc)(void *obj),
3583     void (*obj_free)(void *))
3584 {
3585 	struct shared *out;
3586 
3587 	if ((out = xmalloc(sizeof (struct shared))) == NULL) {
3588 		return (NULL);
3589 	}
3590 	if ((out->obj = obj_alloc(obj)) == NULL) {
3591 		free(out);
3592 		return (NULL);
3593 	}
3594 	out->count = 1;
3595 	out->free = obj_free;
3596 
3597 	return (out);
3598 }
3599 
3600 static struct shared *
3601 create_shared_str(char *str)
3602 {
3603 	return (create_shared(str, (void *(*)(void *))strdup, free));
3604 }
3605 
3606 static struct shared *
3607 dup_shared(struct shared *obj)
3608 {
3609 	if (obj != NULL) {
3610 		obj->count++;
3611 	}
3612 	return (obj);
3613 }
3614 
3615 static void
3616 rel_shared(struct shared *obj)
3617 {
3618 	if (obj && (--obj->count) == 0) {
3619 		obj->free(obj->obj);
3620 		free(obj);
3621 	}
3622 }
3623 
3624 static void *
3625 get_obj(struct shared *obj)
3626 {
3627 	return (obj->obj);
3628 }
3629