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