xref: /illumos-gate/usr/src/cmd/cron/cron.c (revision 5b08e637)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54c4c9110Sbasabi  * Common Development and Distribution License (the "License").
64c4c9110Sbasabi  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2263d5d94cSjc  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
277c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
307c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #ifdef lint
337c478bd9Sstevel@tonic-gate /* make lint happy */
347c478bd9Sstevel@tonic-gate #define	__EXTENSIONS__
357c478bd9Sstevel@tonic-gate #endif
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate #include <sys/contract/process.h>
387c478bd9Sstevel@tonic-gate #include <sys/ctfs.h>
397c478bd9Sstevel@tonic-gate #include <sys/param.h>
407c478bd9Sstevel@tonic-gate #include <sys/resource.h>
417c478bd9Sstevel@tonic-gate #include <sys/stat.h>
427c478bd9Sstevel@tonic-gate #include <sys/task.h>
437c478bd9Sstevel@tonic-gate #include <sys/time.h>
447c478bd9Sstevel@tonic-gate #include <sys/types.h>
457c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
467c478bd9Sstevel@tonic-gate #include <sys/wait.h>
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate #include <alloca.h>
517c478bd9Sstevel@tonic-gate #include <ctype.h>
527c478bd9Sstevel@tonic-gate #include <deflt.h>
537c478bd9Sstevel@tonic-gate #include <dirent.h>
547c478bd9Sstevel@tonic-gate #include <errno.h>
557c478bd9Sstevel@tonic-gate #include <fcntl.h>
567c478bd9Sstevel@tonic-gate #include <grp.h>
577c478bd9Sstevel@tonic-gate #include <libcontract.h>
587c478bd9Sstevel@tonic-gate #include <libcontract_priv.h>
597c478bd9Sstevel@tonic-gate #include <limits.h>
607c478bd9Sstevel@tonic-gate #include <locale.h>
617c478bd9Sstevel@tonic-gate #include <poll.h>
627c478bd9Sstevel@tonic-gate #include <project.h>
637c478bd9Sstevel@tonic-gate #include <pwd.h>
647c478bd9Sstevel@tonic-gate #include <signal.h>
657c478bd9Sstevel@tonic-gate #include <stdarg.h>
667c478bd9Sstevel@tonic-gate #include <stdio.h>
677c478bd9Sstevel@tonic-gate #include <stdlib.h>
687c478bd9Sstevel@tonic-gate #include <string.h>
697c478bd9Sstevel@tonic-gate #include <stropts.h>
707c478bd9Sstevel@tonic-gate #include <time.h>
717c478bd9Sstevel@tonic-gate #include <unistd.h>
72*5b08e637SChris Gerhard #include <libzoneinfo.h>
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate #include "cron.h"
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate  * #define	DEBUG
787c478bd9Sstevel@tonic-gate  */
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate #define	MAIL		"/usr/bin/mail"	/* mail program to use */
817c478bd9Sstevel@tonic-gate #define	CONSOLE		"/dev/console"	/* where messages go when cron dies */
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate #define	TMPINFILE	"/tmp/crinXXXXXX"  /* file to put stdin in for cmd  */
847c478bd9Sstevel@tonic-gate #define	TMPDIR		"/tmp"
857c478bd9Sstevel@tonic-gate #define	PFX		"crout"
867c478bd9Sstevel@tonic-gate #define	TMPOUTFILE	"/tmp/croutXXXXXX" /* file to place stdout, stderr */
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate #define	INMODE		00400		/* mode for stdin file	*/
897c478bd9Sstevel@tonic-gate #define	OUTMODE		00600		/* mode for stdout file */
907c478bd9Sstevel@tonic-gate #define	ISUID		S_ISUID		/* mode for verifing at jobs */
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate #define	INFINITY	2147483647L	/* upper bound on time	*/
937c478bd9Sstevel@tonic-gate #define	CUSHION		180L
947c478bd9Sstevel@tonic-gate #define	ZOMB		100		/* proc slot used for mailing output */
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate #define	JOBF		'j'
977c478bd9Sstevel@tonic-gate #define	NICEF		'n'
987c478bd9Sstevel@tonic-gate #define	USERF		'u'
997c478bd9Sstevel@tonic-gate #define	WAITF		'w'
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate #define	BCHAR		'>'
1027c478bd9Sstevel@tonic-gate #define	ECHAR		'<'
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate #define	DEFAULT		0
1057c478bd9Sstevel@tonic-gate #define	LOAD		1
1067c478bd9Sstevel@tonic-gate #define	QBUFSIZ		80
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate /* Defined actions for crabort() routine */
1097c478bd9Sstevel@tonic-gate #define	NO_ACTION	000
1107c478bd9Sstevel@tonic-gate #define	REMOVE_FIFO	001
1117c478bd9Sstevel@tonic-gate #define	CONSOLE_MSG	002
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate #define	BADCD		"can't change directory to the crontab directory."
1147c478bd9Sstevel@tonic-gate #define	NOREADDIR	"can't read the crontab directory."
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate #define	BADJOBOPEN	"unable to read your at job."
1177c478bd9Sstevel@tonic-gate #define	BADSHELL	"because your login shell \
1187c478bd9Sstevel@tonic-gate isn't /usr/bin/sh, you can't use cron."
1197c478bd9Sstevel@tonic-gate 
1204c4c9110Sbasabi #define	BADSTAT		"can't access your crontab or at-job file. Resubmit it."
1217c478bd9Sstevel@tonic-gate #define	BADPROJID	"can't set project id for your job."
122*5b08e637SChris Gerhard #define	CANTCDHOME	"can't change directory to %s.\
1237c478bd9Sstevel@tonic-gate \nYour commands will not be executed."
124*5b08e637SChris Gerhard #define	CANTEXECSH	"unable to exec the shell, %s, for one of your \
125*5b08e637SChris Gerhard commands."
126*5b08e637SChris Gerhard #define	CANT_STR_LEN (sizeof (CANTEXECSH) > sizeof (CANTCDHOME) ? \
127*5b08e637SChris Gerhard 	sizeof (CANTEXECSH) : sizeof (CANTCDHOME))
1287c478bd9Sstevel@tonic-gate #define	NOREAD		"can't read your crontab file.  Resubmit it."
1294c4c9110Sbasabi #define	BADTYPE		"crontab or at-job file is not a regular file.\n"
1307c478bd9Sstevel@tonic-gate #define	NOSTDIN		"unable to create a standard input file for \
1317c478bd9Sstevel@tonic-gate one of your crontab commands. \
1327c478bd9Sstevel@tonic-gate \nThat command was not executed."
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate #define	NOTALLOWED	"you are not authorized to use cron.  Sorry."
1357c478bd9Sstevel@tonic-gate #define	STDERRMSG	"\n\n********************************************\
1367c478bd9Sstevel@tonic-gate *****\nCron: The previous message is the \
1377c478bd9Sstevel@tonic-gate standard output and standard error \
1387c478bd9Sstevel@tonic-gate \nof one of your cron commands.\n"
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate #define	STDOUTERR	"one of your commands generated output or errors, \
1417c478bd9Sstevel@tonic-gate but cron was unable to mail you this output.\
1427c478bd9Sstevel@tonic-gate \nRemember to redirect standard output and standard \
1437c478bd9Sstevel@tonic-gate error for each of your commands."
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate #define	CLOCK_DRIFT	"clock time drifted backwards after event!\n"
1467c478bd9Sstevel@tonic-gate #define	PIDERR		"unexpected pid returned %d (ignored)"
1477c478bd9Sstevel@tonic-gate #define	CRONTABERR	"Subject: Your crontab file has an error in it\n\n"
1487c478bd9Sstevel@tonic-gate #define	CRONOUT		"Subject: Output from \"cron\" command\n\n"
1497c478bd9Sstevel@tonic-gate #define	MALLOCERR	"out of space, cannot create new string\n"
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate #define	DIDFORK didfork
1527c478bd9Sstevel@tonic-gate #define	NOFORK !didfork
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate #define	MAILBUFLEN	(8*1024)
1557c478bd9Sstevel@tonic-gate #define	LINELIMIT	80
1567c478bd9Sstevel@tonic-gate #define	MAILBINITFREE	(MAILBUFLEN - (sizeof (cte_intro) - 1) \
1577c478bd9Sstevel@tonic-gate 	    - (sizeof (cte_trail1) - 1) - (sizeof (cte_trail2) - 1) - 1)
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate #define	ERR_CRONTABENT	0	/* error in crontab file entry */
1607c478bd9Sstevel@tonic-gate #define	ERR_UNIXERR	1	/* error in some system call */
1617c478bd9Sstevel@tonic-gate #define	ERR_CANTEXECCRON 2	/* error setting up "cron" job environment */
1627c478bd9Sstevel@tonic-gate #define	ERR_CANTEXECAT	3	/* error setting up "at" job environment */
1634c4c9110Sbasabi #define	ERR_NOTREG	4	/* error not a regular file */
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate #define	PROJECT		"project="
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate #define	MAX_LOST_CONTRACTS	2048	/* reset if this many failed abandons */
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate #define	FORMAT	"%a %b %e %H:%M:%S %Y"
1707c478bd9Sstevel@tonic-gate static char	timebuf[80];
1717c478bd9Sstevel@tonic-gate 
172*5b08e637SChris Gerhard struct shared {
173*5b08e637SChris Gerhard 	int count;			/* usage count */
174*5b08e637SChris Gerhard 	void (*free)(void *obj);	/* routine that will free obj */
175*5b08e637SChris Gerhard 	void *obj;			/* object */
176*5b08e637SChris Gerhard };
177*5b08e637SChris Gerhard 
1787c478bd9Sstevel@tonic-gate struct event {
1797c478bd9Sstevel@tonic-gate 	time_t time;	/* time of the event	*/
1807c478bd9Sstevel@tonic-gate 	short etype;	/* what type of event; 0=cron, 1=at	*/
1817c478bd9Sstevel@tonic-gate 	char *cmd;	/* command for cron, job name for at	*/
1827c478bd9Sstevel@tonic-gate 	struct usr *u;	/* ptr to the owner (usr) of this event	*/
1837c478bd9Sstevel@tonic-gate 	struct event *link;	/* ptr to another event for this user */
1847c478bd9Sstevel@tonic-gate 	union {
1857c478bd9Sstevel@tonic-gate 		struct { /* for crontab events */
1867c478bd9Sstevel@tonic-gate 			char *minute;	/*  (these	*/
1877c478bd9Sstevel@tonic-gate 			char *hour;	/*   fields	*/
1887c478bd9Sstevel@tonic-gate 			char *daymon;	/*   are	*/
1897c478bd9Sstevel@tonic-gate 			char *month;	/*   from	*/
1907c478bd9Sstevel@tonic-gate 			char *dayweek;	/*   crontab)	*/
1917c478bd9Sstevel@tonic-gate 			char *input;	/* ptr to stdin	*/
192*5b08e637SChris Gerhard 			struct shared *tz;	/* timezone of this event */
193*5b08e637SChris Gerhard 			struct shared *home;	/* directory for this event */
194*5b08e637SChris Gerhard 			struct shared *shell;	/* shell for this event */
1957c478bd9Sstevel@tonic-gate 		} ct;
1967c478bd9Sstevel@tonic-gate 		struct { /* for at events */
1977c478bd9Sstevel@tonic-gate 			short exists;	/* for revising at events	*/
1987c478bd9Sstevel@tonic-gate 			int eventid;	/* for el_remove-ing at events	*/
1997c478bd9Sstevel@tonic-gate 		} at;
2007c478bd9Sstevel@tonic-gate 	} of;
2017c478bd9Sstevel@tonic-gate };
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate struct usr {
2047c478bd9Sstevel@tonic-gate 	char *name;	/* name of user (e.g. "root")	*/
2057c478bd9Sstevel@tonic-gate 	char *home;	/* home directory for user	*/
2067c478bd9Sstevel@tonic-gate 	uid_t uid;	/* user id	*/
2077c478bd9Sstevel@tonic-gate 	gid_t gid;	/* group id	*/
2087c478bd9Sstevel@tonic-gate 	int aruncnt;	/* counter for running jobs per uid */
2097c478bd9Sstevel@tonic-gate 	int cruncnt;	/* counter for running cron jobs per uid */
2107c478bd9Sstevel@tonic-gate 	int ctid;	/* for el_remove-ing crontab events */
2117c478bd9Sstevel@tonic-gate 	short ctexists;	/* for revising crontab events	*/
2127c478bd9Sstevel@tonic-gate 	struct event *ctevents;	/* list of this usr's crontab events */
2137c478bd9Sstevel@tonic-gate 	struct event *atevents;	/* list of this usr's at events */
2147c478bd9Sstevel@tonic-gate 	struct usr *nextusr;
2157c478bd9Sstevel@tonic-gate };	/* ptr to next user	*/
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate static struct	queue
2187c478bd9Sstevel@tonic-gate {
2197c478bd9Sstevel@tonic-gate 	int njob;	/* limit */
2207c478bd9Sstevel@tonic-gate 	int nice;	/* nice for execution */
2217c478bd9Sstevel@tonic-gate 	int nwait;	/* wait time to next execution attempt */
2227c478bd9Sstevel@tonic-gate 	int nrun;	/* number running */
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate 	qd = {100, 2, 60},		/* default values for queue defs */
2257c478bd9Sstevel@tonic-gate 	qt[NQUEUE];
2267c478bd9Sstevel@tonic-gate static struct	queue	qq;
2277c478bd9Sstevel@tonic-gate 
2289f163834Sbasabi static struct runinfo
2297c478bd9Sstevel@tonic-gate {
2307c478bd9Sstevel@tonic-gate 	pid_t	pid;
2317c478bd9Sstevel@tonic-gate 	short	que;
2327c478bd9Sstevel@tonic-gate 	struct  usr *rusr;	/* pointer to usr struct */
2337c478bd9Sstevel@tonic-gate 	char	*outfile;	/* file where stdout & stderr are trapped */
2347c478bd9Sstevel@tonic-gate 	short	jobtype;	/* what type of event: 0=cron, 1=at */
2357c478bd9Sstevel@tonic-gate 	char	*jobname;	/* command for "cron", jobname for "at" */
2367c478bd9Sstevel@tonic-gate 	int	mailwhendone;	/* 1 = send mail even if no ouptut */
2379f163834Sbasabi 	struct runinfo *next;
2389f163834Sbasabi }	*rthead;
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate static struct miscpid {
2417c478bd9Sstevel@tonic-gate 	pid_t		pid;
2427c478bd9Sstevel@tonic-gate 	struct miscpid	*next;
2437c478bd9Sstevel@tonic-gate }	*miscpid_head;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate static pid_t cron_pid;	/* own pid */
2467c478bd9Sstevel@tonic-gate static char didfork = 0; /* flag to see if I'm process group leader */
2477c478bd9Sstevel@tonic-gate static int msgfd;	/* file descriptor for fifo queue */
2487c478bd9Sstevel@tonic-gate static int ecid = 1;	/* event class id for el_remove(); MUST be set to 1 */
2497c478bd9Sstevel@tonic-gate static int delayed;	/* is job being rescheduled or did it run first time */
2507c478bd9Sstevel@tonic-gate static int cwd;		/* current working directory */
2517c478bd9Sstevel@tonic-gate static struct event *next_event;	/* the next event to execute	*/
2527c478bd9Sstevel@tonic-gate static struct usr *uhead;		/* ptr to the list of users	*/
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate /* Variables for error handling at reading crontabs. */
2557c478bd9Sstevel@tonic-gate static char cte_intro[] = "Line(s) with errors:\n\n";
2567c478bd9Sstevel@tonic-gate static char cte_trail1[] = "\nMax number of errors encountered.";
2577c478bd9Sstevel@tonic-gate static char cte_trail2[] = " Evaluation of crontab aborted.\n";
2587c478bd9Sstevel@tonic-gate static int cte_free = MAILBINITFREE;	/* Free buffer space */
2597c478bd9Sstevel@tonic-gate static char *cte_text = NULL;		/* Text buffer pointer */
2607c478bd9Sstevel@tonic-gate static char *cte_lp;			/* Next free line in cte_text */
2617c478bd9Sstevel@tonic-gate static int cte_nvalid;			/* Valid lines found */
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate /* user's default environment for the shell */
2647c478bd9Sstevel@tonic-gate #define	ROOTPATH	"PATH=/usr/sbin:/usr/bin"
2657c478bd9Sstevel@tonic-gate #define	NONROOTPATH	"PATH=/usr/bin:"
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate static char *Def_supath	= NULL;
2687c478bd9Sstevel@tonic-gate static char *Def_path		= NULL;
2697c478bd9Sstevel@tonic-gate static char path[LINE_MAX]	= "PATH=";
2707c478bd9Sstevel@tonic-gate static char supath[LINE_MAX]	= "PATH=";
271*5b08e637SChris Gerhard static char homedir[LINE_MAX]	= ENV_HOME;
2727c478bd9Sstevel@tonic-gate static char logname[LINE_MAX]	= "LOGNAME=";
273*5b08e637SChris Gerhard static char tzone[LINE_MAX]	= ENV_TZ;
2747c478bd9Sstevel@tonic-gate static char *envinit[] = {
2757c478bd9Sstevel@tonic-gate 	homedir,
2767c478bd9Sstevel@tonic-gate 	logname,
2777c478bd9Sstevel@tonic-gate 	ROOTPATH,
2787c478bd9Sstevel@tonic-gate 	"SHELL=/usr/bin/sh",
2797c478bd9Sstevel@tonic-gate 	tzone,
2807c478bd9Sstevel@tonic-gate 	NULL
2817c478bd9Sstevel@tonic-gate };
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate extern char **environ;
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate #define	DEFTZ		"GMT"
2867c478bd9Sstevel@tonic-gate static	int	log = 0;
2877c478bd9Sstevel@tonic-gate static	char	hzname[10];
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate static void cronend(int);
2907c478bd9Sstevel@tonic-gate static void thaw_handler(int);
2917c478bd9Sstevel@tonic-gate static void child_handler(int);
2927c478bd9Sstevel@tonic-gate static void child_sigreset(void);
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate static void mod_ctab(char *, time_t);
2957c478bd9Sstevel@tonic-gate static void mod_atjob(char *, time_t);
2967c478bd9Sstevel@tonic-gate static void add_atevent(struct usr *, char *, time_t, int);
2977c478bd9Sstevel@tonic-gate static void rm_ctevents(struct usr *);
2987c478bd9Sstevel@tonic-gate static void cleanup(struct runinfo *rn, int r);
2997c478bd9Sstevel@tonic-gate static void crabort(char *, int);
3007c478bd9Sstevel@tonic-gate static void msg(char *fmt, ...);
301e2553d68SSerge Dussud static void ignore_msg(char *, char *, struct event *);
3027c478bd9Sstevel@tonic-gate static void logit(int, struct runinfo *, int);
3037c478bd9Sstevel@tonic-gate static void parsqdef(char *);
3047c478bd9Sstevel@tonic-gate static void defaults();
3057c478bd9Sstevel@tonic-gate static void initialize(int);
3067c478bd9Sstevel@tonic-gate static void quedefs(int);
3077c478bd9Sstevel@tonic-gate static int idle(long);
3087c478bd9Sstevel@tonic-gate static struct usr *find_usr(char *);
3097c478bd9Sstevel@tonic-gate static int ex(struct event *e);
3104c4c9110Sbasabi static void read_dirs(int);
3117c478bd9Sstevel@tonic-gate static void mail(char *, char *, int);
3127c478bd9Sstevel@tonic-gate static char *next_field(int, int);
3137c478bd9Sstevel@tonic-gate static void readcron(struct usr *, time_t);
3147c478bd9Sstevel@tonic-gate static int next_ge(int, char *);
3157c478bd9Sstevel@tonic-gate static void free_if_unused(struct usr *);
3167c478bd9Sstevel@tonic-gate static void del_atjob(char *, char *);
3177c478bd9Sstevel@tonic-gate static void del_ctab(char *);
3187c478bd9Sstevel@tonic-gate static void resched(int);
3197c478bd9Sstevel@tonic-gate static int msg_wait(long);
3207c478bd9Sstevel@tonic-gate static struct runinfo *rinfo_get(pid_t);
3217c478bd9Sstevel@tonic-gate static void rinfo_free(struct runinfo *rp);
3227c478bd9Sstevel@tonic-gate static void mail_result(struct usr *p, struct runinfo *pr, size_t filesize);
3237c478bd9Sstevel@tonic-gate static time_t next_time(struct event *, time_t);
3247c478bd9Sstevel@tonic-gate static time_t get_switching_time(int, time_t);
3257c478bd9Sstevel@tonic-gate static time_t xmktime(struct tm *);
3267c478bd9Sstevel@tonic-gate static void process_msg(struct message *, time_t);
3277c478bd9Sstevel@tonic-gate static void reap_child(void);
3287c478bd9Sstevel@tonic-gate static void miscpid_insert(pid_t);
3297c478bd9Sstevel@tonic-gate static int miscpid_delete(pid_t);
330032624d5Sbasabi static void contract_set_template(void);
331032624d5Sbasabi static void contract_clear_template(void);
3327c478bd9Sstevel@tonic-gate static void contract_abandon_latest(pid_t);
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate static void cte_init(void);
3357c478bd9Sstevel@tonic-gate static void cte_add(int, char *);
3367c478bd9Sstevel@tonic-gate static void cte_valid(void);
3377c478bd9Sstevel@tonic-gate static int cte_istoomany(void);
3387c478bd9Sstevel@tonic-gate static void cte_sendmail(char *);
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate static int set_user_cred(const struct usr *, struct project *);
3417c478bd9Sstevel@tonic-gate 
342*5b08e637SChris Gerhard static struct shared *create_shared_str(char *str);
343*5b08e637SChris Gerhard static struct shared *dup_shared(struct shared *obj);
344*5b08e637SChris Gerhard static void rel_shared(struct shared *obj);
345*5b08e637SChris Gerhard static void *get_obj(struct shared *obj);
3467c478bd9Sstevel@tonic-gate /*
3477c478bd9Sstevel@tonic-gate  * last_time is set immediately prior to exection of an event (via ex())
3487c478bd9Sstevel@tonic-gate  * to indicate the last time an event was executed.  This was (surely)
3497c478bd9Sstevel@tonic-gate  * it's original intended use.
3507c478bd9Sstevel@tonic-gate  */
3517c478bd9Sstevel@tonic-gate static time_t last_time, init_time, t_old;
352e2553d68SSerge Dussud static int reset_needed; /* set to 1 when cron(1M) needs to re-initialize */
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate static int		accept_sigcld, notifypipe[2];
3557c478bd9Sstevel@tonic-gate static sigset_t		defmask, childmask;
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate /*
3587c478bd9Sstevel@tonic-gate  * BSM hooks
3597c478bd9Sstevel@tonic-gate  */
3607c478bd9Sstevel@tonic-gate extern int	audit_cron_session(char *, char *, uid_t, gid_t, char *);
3617c478bd9Sstevel@tonic-gate extern void	audit_cron_new_job(char *, int, void *);
3627c478bd9Sstevel@tonic-gate extern void	audit_cron_bad_user(char *);
3637c478bd9Sstevel@tonic-gate extern void	audit_cron_user_acct_expired(char *);
3647c478bd9Sstevel@tonic-gate extern int	audit_cron_create_anc_file(char *, char *, char *, uid_t);
3657c478bd9Sstevel@tonic-gate extern int	audit_cron_delete_anc_file(char *, char *);
3667c478bd9Sstevel@tonic-gate extern int	audit_cron_is_anc_name(char *);
3677c478bd9Sstevel@tonic-gate extern int	audit_cron_mode();
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate static int cron_conv(int, struct pam_message **,
3707c478bd9Sstevel@tonic-gate 		struct pam_response **, void *);
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate static struct pam_conv pam_conv = {cron_conv, NULL};
3737c478bd9Sstevel@tonic-gate static pam_handle_t *pamh;	/* Authentication handle */
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate /*
3767c478bd9Sstevel@tonic-gate  * Function to help check a user's credentials.
3777c478bd9Sstevel@tonic-gate  */
3787c478bd9Sstevel@tonic-gate 
3794c4c9110Sbasabi static int verify_user_cred(struct usr *u);
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate /*
3827c478bd9Sstevel@tonic-gate  * Values returned by verify_user_cred and set_user_cred:
3837c478bd9Sstevel@tonic-gate  */
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate #define	VUC_OK		0
3867c478bd9Sstevel@tonic-gate #define	VUC_BADUSER	1
3877c478bd9Sstevel@tonic-gate #define	VUC_NOTINGROUP	2
3887c478bd9Sstevel@tonic-gate #define	VUC_EXPIRED	3
3897c478bd9Sstevel@tonic-gate #define	VUC_NEW_AUTH	4
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate /*
3927c478bd9Sstevel@tonic-gate  * Modes of process_anc_files function
3937c478bd9Sstevel@tonic-gate  */
3947c478bd9Sstevel@tonic-gate #define	CRON_ANC_DELETE	1
3957c478bd9Sstevel@tonic-gate #define	CRON_ANC_CREATE	0
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate /*
3987c478bd9Sstevel@tonic-gate  * Functions to remove a user or job completely from the running database.
3997c478bd9Sstevel@tonic-gate  */
4007c478bd9Sstevel@tonic-gate static void clean_out_atjobs(struct usr *u);
4017c478bd9Sstevel@tonic-gate static void clean_out_ctab(struct usr *u);
4027c478bd9Sstevel@tonic-gate static void clean_out_user(struct usr *u);
4037c478bd9Sstevel@tonic-gate static void cron_unlink(char *name);
4047c478bd9Sstevel@tonic-gate static void process_anc_files(int);
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate /*
4077c478bd9Sstevel@tonic-gate  * functions in elm.c
4087c478bd9Sstevel@tonic-gate  */
4097c478bd9Sstevel@tonic-gate extern void el_init(int, time_t, time_t, int);
410e2553d68SSerge Dussud extern int el_add(void *, time_t, int);
4117c478bd9Sstevel@tonic-gate extern void el_remove(int, int);
4127c478bd9Sstevel@tonic-gate extern int el_empty(void);
4137c478bd9Sstevel@tonic-gate extern void *el_first(void);
4147c478bd9Sstevel@tonic-gate extern void el_delete(void);
4157c478bd9Sstevel@tonic-gate 
4164c4c9110Sbasabi static int valid_entry(char *, int);
4174c4c9110Sbasabi static struct usr *create_ulist(char *, int);
4184c4c9110Sbasabi static void init_cronevent(char *, int);
4194c4c9110Sbasabi static void init_atevent(char *, time_t, int, int);
4204c4c9110Sbasabi static void update_atevent(struct usr *, char *, time_t, int);
4214c4c9110Sbasabi 
4227c478bd9Sstevel@tonic-gate int
4237c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
4247c478bd9Sstevel@tonic-gate {
4257c478bd9Sstevel@tonic-gate 	time_t t;
4267c478bd9Sstevel@tonic-gate 	time_t ne_time;		/* amt of time until next event execution */
4277c478bd9Sstevel@tonic-gate 	time_t newtime, lastmtime = 0L;
4287c478bd9Sstevel@tonic-gate 	struct usr *u;
4297c478bd9Sstevel@tonic-gate 	struct event *e, *e2, *eprev;
4307c478bd9Sstevel@tonic-gate 	struct stat buf;
4317c478bd9Sstevel@tonic-gate 	pid_t rfork;
4327c478bd9Sstevel@tonic-gate 	struct sigaction act;
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	/*
435e2553d68SSerge Dussud 	 * reset_needed is set to 1 whenever el_add() finds out that a cron
436e2553d68SSerge Dussud 	 * job is scheduled to be run before the time when cron(1M) daemon
437e2553d68SSerge Dussud 	 * initialized.
438e2553d68SSerge Dussud 	 * Other cases where a reset is needed is when ex() finds that the
439e2553d68SSerge Dussud 	 * event to be executed is being run at the wrong time, or when idle()
440e2553d68SSerge Dussud 	 * determines that time was reset.
4417c478bd9Sstevel@tonic-gate 	 * We immediately return to the top of the while (TRUE) loop in
442e2553d68SSerge Dussud 	 * main() where the event list is cleared and rebuilt, and reset_needed
4437c478bd9Sstevel@tonic-gate 	 * is set back to 0.
4447c478bd9Sstevel@tonic-gate 	 */
445e2553d68SSerge Dussud 	reset_needed = 0;
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	/*
4487c478bd9Sstevel@tonic-gate 	 * Only the privileged user can run this command.
4497c478bd9Sstevel@tonic-gate 	 */
4507c478bd9Sstevel@tonic-gate 	if (getuid() != 0)
4517c478bd9Sstevel@tonic-gate 		crabort(NOTALLOWED, 0);
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate begin:
4547c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4557c478bd9Sstevel@tonic-gate 	/* fork unless 'nofork' is specified */
4567c478bd9Sstevel@tonic-gate 	if ((argc <= 1) || (strcmp(argv[1], "nofork"))) {
4577c478bd9Sstevel@tonic-gate 		if (rfork = fork()) {
4587c478bd9Sstevel@tonic-gate 			if (rfork == (pid_t)-1) {
4597c478bd9Sstevel@tonic-gate 				(void) sleep(30);
4607c478bd9Sstevel@tonic-gate 				goto begin;
4617c478bd9Sstevel@tonic-gate 			}
4627c478bd9Sstevel@tonic-gate 			return (0);
4637c478bd9Sstevel@tonic-gate 		}
4647c478bd9Sstevel@tonic-gate 		didfork++;
4657c478bd9Sstevel@tonic-gate 		(void) setpgrp();	/* detach cron from console */
4667c478bd9Sstevel@tonic-gate 	}
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate 	(void) umask(022);
4697c478bd9Sstevel@tonic-gate 	(void) signal(SIGHUP, SIG_IGN);
4707c478bd9Sstevel@tonic-gate 	(void) signal(SIGINT, SIG_IGN);
4717c478bd9Sstevel@tonic-gate 	(void) signal(SIGQUIT, SIG_IGN);
4727c478bd9Sstevel@tonic-gate 	(void) signal(SIGTERM, cronend);
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 	defaults();
4757c478bd9Sstevel@tonic-gate 	initialize(1);
4767c478bd9Sstevel@tonic-gate 	quedefs(DEFAULT);	/* load default queue definitions */
4777c478bd9Sstevel@tonic-gate 	cron_pid = getpid();
4787c478bd9Sstevel@tonic-gate 	msg("*** cron started ***   pid = %d", cron_pid);
4797c478bd9Sstevel@tonic-gate 	(void) sigset(SIGTHAW, thaw_handler);
4807c478bd9Sstevel@tonic-gate 	/*
4817c478bd9Sstevel@tonic-gate 	 * setup SIGCLD handler/mask
4827c478bd9Sstevel@tonic-gate 	 */
4837c478bd9Sstevel@tonic-gate 	act.sa_handler = child_handler;
4847c478bd9Sstevel@tonic-gate 	act.sa_flags = 0;
4857c478bd9Sstevel@tonic-gate 	(void) sigemptyset(&act.sa_mask);
4867c478bd9Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, SIGCLD);
4877c478bd9Sstevel@tonic-gate 	(void) sigaction(SIGCLD, &act, NULL);
4887c478bd9Sstevel@tonic-gate 	(void) sigemptyset(&childmask);
4897c478bd9Sstevel@tonic-gate 	(void) sigaddset(&childmask, SIGCLD);
4907c478bd9Sstevel@tonic-gate 	(void) sigprocmask(SIG_BLOCK, &childmask, &defmask);
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	if (pipe(notifypipe) != 0) {
4937c478bd9Sstevel@tonic-gate 		crabort("cannot create pipe", REMOVE_FIFO|CONSOLE_MSG);
4947c478bd9Sstevel@tonic-gate 	}
4957c478bd9Sstevel@tonic-gate 	/*
4967c478bd9Sstevel@tonic-gate 	 * will set O_NONBLOCK, so that the write() from child_handler
4977c478bd9Sstevel@tonic-gate 	 * never be blocked.
4987c478bd9Sstevel@tonic-gate 	 */
4997c478bd9Sstevel@tonic-gate 	(void) fcntl(notifypipe[0], F_SETFL, O_WRONLY|O_NONBLOCK);
5007c478bd9Sstevel@tonic-gate 
501e2553d68SSerge Dussud 	t_old = init_time;
5027c478bd9Sstevel@tonic-gate 	last_time = t_old;
5037c478bd9Sstevel@tonic-gate 	for (;;) {		/* MAIN LOOP */
5047c478bd9Sstevel@tonic-gate 		t = time(NULL);
505e2553d68SSerge Dussud 		if ((t_old > t) || (t-last_time > CUSHION) || reset_needed) {
506e2553d68SSerge Dussud 			reset_needed = 0;
5077c478bd9Sstevel@tonic-gate 			/* the time was set backwards or forward */
508e2553d68SSerge Dussud 			msg("time was reset, re-initializing");
5097c478bd9Sstevel@tonic-gate 			el_delete();
5107c478bd9Sstevel@tonic-gate 			u = uhead;
5117c478bd9Sstevel@tonic-gate 			while (u != NULL) {
5127c478bd9Sstevel@tonic-gate 				rm_ctevents(u);
5137c478bd9Sstevel@tonic-gate 				e = u->atevents;
5147c478bd9Sstevel@tonic-gate 				while (e != NULL) {
5157c478bd9Sstevel@tonic-gate 					free(e->cmd);
5167c478bd9Sstevel@tonic-gate 					e2 = e->link;
5177c478bd9Sstevel@tonic-gate 					free(e);
5187c478bd9Sstevel@tonic-gate 					e = e2;
5197c478bd9Sstevel@tonic-gate 				}
5207c478bd9Sstevel@tonic-gate 				u->atevents = NULL;
5217c478bd9Sstevel@tonic-gate 				u = u->nextusr;
5227c478bd9Sstevel@tonic-gate 			}
5237c478bd9Sstevel@tonic-gate 			(void) close(msgfd);
5247c478bd9Sstevel@tonic-gate 			initialize(0);
5257c478bd9Sstevel@tonic-gate 			t = time(NULL);
5267c478bd9Sstevel@tonic-gate 			last_time = t;
527e2553d68SSerge Dussud 			/*
528e2553d68SSerge Dussud 			 * reset_needed might have been set in the functions
529e2553d68SSerge Dussud 			 * call path from initialize()
530e2553d68SSerge Dussud 			 */
531e2553d68SSerge Dussud 			if (reset_needed) {
532e2553d68SSerge Dussud 				continue;
533e2553d68SSerge Dussud 			}
5347c478bd9Sstevel@tonic-gate 		}
5357c478bd9Sstevel@tonic-gate 		t_old = t;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 		if (next_event == NULL && !el_empty()) {
5387c478bd9Sstevel@tonic-gate 			next_event = (struct event *)el_first();
5397c478bd9Sstevel@tonic-gate 		}
5407c478bd9Sstevel@tonic-gate 		if (next_event == NULL) {
5417c478bd9Sstevel@tonic-gate 			ne_time = INFINITY;
5427c478bd9Sstevel@tonic-gate 		} else {
5437c478bd9Sstevel@tonic-gate 			ne_time = next_event->time - t;
5447c478bd9Sstevel@tonic-gate #ifdef DEBUG
5457c478bd9Sstevel@tonic-gate 			cftime(timebuf, "%C", &next_event->time);
5467c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "next_time=%ld %s\n",
5474c4c9110Sbasabi 			    next_event->time, timebuf);
5487c478bd9Sstevel@tonic-gate #endif
5497c478bd9Sstevel@tonic-gate 		}
5507c478bd9Sstevel@tonic-gate 		if (ne_time > 0) {
551e2553d68SSerge Dussud 			/*
552e2553d68SSerge Dussud 			 * reset_needed may be set in the functions call path
553e2553d68SSerge Dussud 			 * from idle()
554e2553d68SSerge Dussud 			 */
555e2553d68SSerge Dussud 			if (idle(ne_time) || reset_needed) {
556e2553d68SSerge Dussud 				reset_needed = 1;
5577c478bd9Sstevel@tonic-gate 				continue;
558e2553d68SSerge Dussud 			}
5597c478bd9Sstevel@tonic-gate 		}
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 		if (stat(QUEDEFS, &buf)) {
5627c478bd9Sstevel@tonic-gate 			msg("cannot stat QUEDEFS file");
5637c478bd9Sstevel@tonic-gate 		} else if (lastmtime != buf.st_mtime) {
5647c478bd9Sstevel@tonic-gate 			quedefs(LOAD);
5657c478bd9Sstevel@tonic-gate 			lastmtime = buf.st_mtime;
5667c478bd9Sstevel@tonic-gate 		}
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 		last_time = next_event->time; /* save execution time */
5697c478bd9Sstevel@tonic-gate 
570e2553d68SSerge Dussud 		/*
571e2553d68SSerge Dussud 		 * reset_needed may be set in the functions call path
572e2553d68SSerge Dussud 		 * from ex()
573e2553d68SSerge Dussud 		 */
574e2553d68SSerge Dussud 		if (ex(next_event) || reset_needed) {
575e2553d68SSerge Dussud 			reset_needed = 1;
5767c478bd9Sstevel@tonic-gate 			continue;
577e2553d68SSerge Dussud 		}
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate 		switch (next_event->etype) {
5807c478bd9Sstevel@tonic-gate 		case CRONEVENT:
5817c478bd9Sstevel@tonic-gate 			/* add cronevent back into the main event list */
5827c478bd9Sstevel@tonic-gate 			if (delayed) {
5837c478bd9Sstevel@tonic-gate 				delayed = 0;
5847c478bd9Sstevel@tonic-gate 				break;
5857c478bd9Sstevel@tonic-gate 			}
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 			/*
5887c478bd9Sstevel@tonic-gate 			 * check if time(0)< last_time. if so, then the
5897c478bd9Sstevel@tonic-gate 			 * system clock has gone backwards. to prevent this
5907c478bd9Sstevel@tonic-gate 			 * job from being started twice, we reschedule this
5917c478bd9Sstevel@tonic-gate 			 * job for the >>next time after last_time<<, and
5927c478bd9Sstevel@tonic-gate 			 * then set next_event->time to this. note that
5937c478bd9Sstevel@tonic-gate 			 * crontab's resolution is 1 minute.
5947c478bd9Sstevel@tonic-gate 			 */
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 			if (last_time > time(NULL)) {
5977c478bd9Sstevel@tonic-gate 				msg(CLOCK_DRIFT);
5987c478bd9Sstevel@tonic-gate 				/*
5997c478bd9Sstevel@tonic-gate 				 * bump up to next 30 second
6007c478bd9Sstevel@tonic-gate 				 * increment
6017c478bd9Sstevel@tonic-gate 				 * 1 <= newtime <= 30
6027c478bd9Sstevel@tonic-gate 				 */
6037c478bd9Sstevel@tonic-gate 				newtime = 30 - (last_time % 30);
6047c478bd9Sstevel@tonic-gate 				newtime += last_time;
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 				/*
6077c478bd9Sstevel@tonic-gate 				 * get the next scheduled event,
6087c478bd9Sstevel@tonic-gate 				 * not the one that we just
6097c478bd9Sstevel@tonic-gate 				 * kicked off!
6107c478bd9Sstevel@tonic-gate 				 */
6117c478bd9Sstevel@tonic-gate 				next_event->time =
6124c4c9110Sbasabi 				    next_time(next_event, newtime);
6137c478bd9Sstevel@tonic-gate 				t_old = time(NULL);
6147c478bd9Sstevel@tonic-gate 			} else {
6157c478bd9Sstevel@tonic-gate 				next_event->time =
6164c4c9110Sbasabi 				    next_time(next_event, (time_t)0);
6177c478bd9Sstevel@tonic-gate 			}
6187c478bd9Sstevel@tonic-gate #ifdef DEBUG
6197c478bd9Sstevel@tonic-gate 			cftime(timebuf, "%C", &next_event->time);
6207c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
6214c4c9110Sbasabi 			    "pushing back cron event %s at %ld (%s)\n",
6224c4c9110Sbasabi 			    next_event->cmd, next_event->time, timebuf);
6237c478bd9Sstevel@tonic-gate #endif
6247c478bd9Sstevel@tonic-gate 
625e2553d68SSerge Dussud 			switch (el_add(next_event, next_event->time,
626e2553d68SSerge Dussud 			    (next_event->u)->ctid)) {
627e2553d68SSerge Dussud 			case -1:
628e2553d68SSerge Dussud 				ignore_msg("main", "cron", next_event);
629e2553d68SSerge Dussud 				break;
630e2553d68SSerge Dussud 			case -2: /* event time lower than init time */
631e2553d68SSerge Dussud 				reset_needed = 1;
632e2553d68SSerge Dussud 				break;
633e2553d68SSerge Dussud 			}
6347c478bd9Sstevel@tonic-gate 			break;
6357c478bd9Sstevel@tonic-gate 		default:
6367c478bd9Sstevel@tonic-gate 			/* remove at or batch job from system */
6377c478bd9Sstevel@tonic-gate 			if (delayed) {
6387c478bd9Sstevel@tonic-gate 				delayed = 0;
6397c478bd9Sstevel@tonic-gate 				break;
6407c478bd9Sstevel@tonic-gate 			}
6417c478bd9Sstevel@tonic-gate 			eprev = NULL;
6427c478bd9Sstevel@tonic-gate 			e = (next_event->u)->atevents;
6437c478bd9Sstevel@tonic-gate 			while (e != NULL) {
6447c478bd9Sstevel@tonic-gate 				if (e == next_event) {
6457c478bd9Sstevel@tonic-gate 					if (eprev == NULL)
6467c478bd9Sstevel@tonic-gate 						(e->u)->atevents = e->link;
6477c478bd9Sstevel@tonic-gate 					else
6487c478bd9Sstevel@tonic-gate 						eprev->link = e->link;
6497c478bd9Sstevel@tonic-gate 					free(e->cmd);
6507c478bd9Sstevel@tonic-gate 					free(e);
6517c478bd9Sstevel@tonic-gate 					break;
6527c478bd9Sstevel@tonic-gate 				} else {
6537c478bd9Sstevel@tonic-gate 					eprev = e;
6547c478bd9Sstevel@tonic-gate 					e = e->link;
6557c478bd9Sstevel@tonic-gate 				}
6567c478bd9Sstevel@tonic-gate 			}
6577c478bd9Sstevel@tonic-gate 			break;
6587c478bd9Sstevel@tonic-gate 		}
6597c478bd9Sstevel@tonic-gate 		next_event = NULL;
6607c478bd9Sstevel@tonic-gate 	}
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
6637c478bd9Sstevel@tonic-gate }
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate static void
6667c478bd9Sstevel@tonic-gate initialize(int firstpass)
6677c478bd9Sstevel@tonic-gate {
6687c478bd9Sstevel@tonic-gate #ifdef DEBUG
6697c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "in initialize\n");
6707c478bd9Sstevel@tonic-gate #endif
6717c478bd9Sstevel@tonic-gate 	if (firstpass) {
6727c478bd9Sstevel@tonic-gate 		/* for mail(1), make sure messages come from root */
6737c478bd9Sstevel@tonic-gate 		if (putenv("LOGNAME=root") != 0) {
6747c478bd9Sstevel@tonic-gate 			crabort("cannot expand env variable",
6754c4c9110Sbasabi 			    REMOVE_FIFO|CONSOLE_MSG);
6767c478bd9Sstevel@tonic-gate 		}
6777c478bd9Sstevel@tonic-gate 		if (access(FIFO, R_OK) == -1) {
6787c478bd9Sstevel@tonic-gate 			if (errno == ENOENT) {
6797c478bd9Sstevel@tonic-gate 				if (mknod(FIFO, S_IFIFO|0600, 0) != 0)
6807c478bd9Sstevel@tonic-gate 					crabort("cannot create fifo queue",
6814c4c9110Sbasabi 					    REMOVE_FIFO|CONSOLE_MSG);
6827c478bd9Sstevel@tonic-gate 			} else {
6837c478bd9Sstevel@tonic-gate 				if (NOFORK) {
6847c478bd9Sstevel@tonic-gate 					/* didn't fork... init(1M) is waiting */
6857c478bd9Sstevel@tonic-gate 					(void) sleep(60);
6867c478bd9Sstevel@tonic-gate 				}
6877c478bd9Sstevel@tonic-gate 				perror("FIFO");
6887c478bd9Sstevel@tonic-gate 				crabort("cannot access fifo queue",
6894c4c9110Sbasabi 				    REMOVE_FIFO|CONSOLE_MSG);
6907c478bd9Sstevel@tonic-gate 			}
6917c478bd9Sstevel@tonic-gate 		} else {
6927c478bd9Sstevel@tonic-gate 			if (NOFORK) {
6937c478bd9Sstevel@tonic-gate 				/* didn't fork... init(1M) is waiting */
6947c478bd9Sstevel@tonic-gate 				(void) sleep(60);
6957c478bd9Sstevel@tonic-gate 				/*
6967c478bd9Sstevel@tonic-gate 				 * the wait is painful, but we don't want
6977c478bd9Sstevel@tonic-gate 				 * init respawning this quickly
6987c478bd9Sstevel@tonic-gate 				 */
6997c478bd9Sstevel@tonic-gate 			}
7007c478bd9Sstevel@tonic-gate 			crabort("cannot start cron; FIFO exists", CONSOLE_MSG);
7017c478bd9Sstevel@tonic-gate 		}
7027c478bd9Sstevel@tonic-gate 	}
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 	if ((msgfd = open(FIFO, O_RDWR)) < 0) {
7057c478bd9Sstevel@tonic-gate 		perror("! open");
7067c478bd9Sstevel@tonic-gate 		crabort("cannot open fifo queue", REMOVE_FIFO|CONSOLE_MSG);
7077c478bd9Sstevel@tonic-gate 	}
7087c478bd9Sstevel@tonic-gate 
709e2553d68SSerge Dussud 	init_time = time(NULL);
710e2553d68SSerge Dussud 	el_init(8, init_time, (time_t)(60*60*24), 10);
711e2553d68SSerge Dussud 
712*5b08e637SChris Gerhard 	init_time = time(NULL);
713*5b08e637SChris Gerhard 	el_init(8, init_time, (time_t)(60*60*24), 10);
714*5b08e637SChris Gerhard 
7157c478bd9Sstevel@tonic-gate 	/*
7167c478bd9Sstevel@tonic-gate 	 * read directories, create users list, and add events to the
7177c478bd9Sstevel@tonic-gate 	 * main event list. Only zero user list on firstpass.
7187c478bd9Sstevel@tonic-gate 	 */
7197c478bd9Sstevel@tonic-gate 	if (firstpass)
7207c478bd9Sstevel@tonic-gate 		uhead = NULL;
7214c4c9110Sbasabi 	read_dirs(firstpass);
7227c478bd9Sstevel@tonic-gate 	next_event = NULL;
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate 	if (!firstpass)
7257c478bd9Sstevel@tonic-gate 		return;
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 	/* stdout is log file */
7287c478bd9Sstevel@tonic-gate 	if (freopen(ACCTFILE, "a", stdout) == NULL)
7297c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "cannot open %s\n", ACCTFILE);
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	/* log should be root-only */
7327c478bd9Sstevel@tonic-gate 	(void) fchmod(1, S_IRUSR|S_IWUSR);
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	/* stderr also goes to ACCTFILE */
7357c478bd9Sstevel@tonic-gate 	(void) close(fileno(stderr));
7367c478bd9Sstevel@tonic-gate 	(void) dup(1);
7377c478bd9Sstevel@tonic-gate 	/* null for stdin */
7387c478bd9Sstevel@tonic-gate 	(void) freopen("/dev/null", "r", stdin);
7397c478bd9Sstevel@tonic-gate 
7407c478bd9Sstevel@tonic-gate 	contract_set_template();
7417c478bd9Sstevel@tonic-gate }
7427c478bd9Sstevel@tonic-gate 
7437c478bd9Sstevel@tonic-gate static void
7444c4c9110Sbasabi read_dirs(int first)
7457c478bd9Sstevel@tonic-gate {
7464c4c9110Sbasabi 	DIR		*dir;
7474c4c9110Sbasabi 	struct dirent	*dp;
7484c4c9110Sbasabi 	char		*ptr;
7494c4c9110Sbasabi 	int		jobtype;
7504c4c9110Sbasabi 	time_t		tim;
7514c4c9110Sbasabi 
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 	if (chdir(CRONDIR) == -1)
7547c478bd9Sstevel@tonic-gate 		crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG);
7557c478bd9Sstevel@tonic-gate 	cwd = CRON;
7567c478bd9Sstevel@tonic-gate 	if ((dir = opendir(".")) == NULL)
7577c478bd9Sstevel@tonic-gate 		crabort(NOREADDIR, REMOVE_FIFO|CONSOLE_MSG);
7584c4c9110Sbasabi 	while ((dp = readdir(dir)) != NULL) {
7594c4c9110Sbasabi 		if (!valid_entry(dp->d_name, CRONEVENT))
7604c4c9110Sbasabi 			continue;
7614c4c9110Sbasabi 		init_cronevent(dp->d_name, first);
7624c4c9110Sbasabi 	}
7637c478bd9Sstevel@tonic-gate 	(void) closedir(dir);
7644c4c9110Sbasabi 
7657c478bd9Sstevel@tonic-gate 	if (chdir(ATDIR) == -1) {
7667c478bd9Sstevel@tonic-gate 		msg("cannot chdir to at directory");
7677c478bd9Sstevel@tonic-gate 		return;
7687c478bd9Sstevel@tonic-gate 	}
7697c478bd9Sstevel@tonic-gate 	if ((dir = opendir(".")) == NULL) {
7707c478bd9Sstevel@tonic-gate 		msg("cannot read at at directory");
7717c478bd9Sstevel@tonic-gate 		return;
7727c478bd9Sstevel@tonic-gate 	}
7734c4c9110Sbasabi 	cwd = AT;
7744c4c9110Sbasabi 	while ((dp = readdir(dir)) != NULL) {
7754c4c9110Sbasabi 		if (!valid_entry(dp->d_name, ATEVENT))
7764c4c9110Sbasabi 			continue;
7774c4c9110Sbasabi 		ptr = dp->d_name;
7784c4c9110Sbasabi 		if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
7794c4c9110Sbasabi 			continue;
7804c4c9110Sbasabi 		ptr++;
7814c4c9110Sbasabi 		if (!isalpha(*ptr))
7824c4c9110Sbasabi 			continue;
7834c4c9110Sbasabi 		jobtype = *ptr - 'a';
7844c4c9110Sbasabi 		if (jobtype >= NQUEUE) {
7854c4c9110Sbasabi 			cron_unlink(dp->d_name);
7864c4c9110Sbasabi 			continue;
7874c4c9110Sbasabi 		}
7884c4c9110Sbasabi 		init_atevent(dp->d_name, tim, jobtype, first);
7894c4c9110Sbasabi 	}
7907c478bd9Sstevel@tonic-gate 	(void) closedir(dir);
7917c478bd9Sstevel@tonic-gate }
7927c478bd9Sstevel@tonic-gate 
7934c4c9110Sbasabi static int
7944c4c9110Sbasabi valid_entry(char *name, int type)
7957c478bd9Sstevel@tonic-gate {
7964c4c9110Sbasabi 	struct stat	buf;
7977c478bd9Sstevel@tonic-gate 
7984c4c9110Sbasabi 	if (strcmp(name, ".") == 0 ||
7994c4c9110Sbasabi 	    strcmp(name, "..") == 0)
8004c4c9110Sbasabi 		return (0);
8014c4c9110Sbasabi 
8024c4c9110Sbasabi 	/* skip over ancillary file names */
8034c4c9110Sbasabi 	if (audit_cron_is_anc_name(name))
8044c4c9110Sbasabi 		return (0);
8054c4c9110Sbasabi 
8064c4c9110Sbasabi 	if (stat(name, &buf)) {
8074c4c9110Sbasabi 		mail(name, BADSTAT, ERR_UNIXERR);
8084c4c9110Sbasabi 		cron_unlink(name);
8094c4c9110Sbasabi 		return (0);
8104c4c9110Sbasabi 	}
8114c4c9110Sbasabi 	if (!S_ISREG(buf.st_mode)) {
8124c4c9110Sbasabi 		mail(name, BADTYPE, ERR_NOTREG);
8134c4c9110Sbasabi 		cron_unlink(name);
8144c4c9110Sbasabi 		return (0);
8154c4c9110Sbasabi 	}
8164c4c9110Sbasabi 	if (type == ATEVENT) {
8174c4c9110Sbasabi 		if (!(buf.st_mode & ISUID)) {
8184c4c9110Sbasabi 			cron_unlink(name);
8194c4c9110Sbasabi 			return (0);
8204c4c9110Sbasabi 		}
8214c4c9110Sbasabi 	}
8224c4c9110Sbasabi 	return (1);
8234c4c9110Sbasabi }
8244c4c9110Sbasabi 
8254c4c9110Sbasabi struct usr *
8264c4c9110Sbasabi create_ulist(char *name, int type)
8274c4c9110Sbasabi {
8284c4c9110Sbasabi 	struct usr	*u;
8294c4c9110Sbasabi 
8304c4c9110Sbasabi 	u = xmalloc(sizeof (struct usr));
8314c4c9110Sbasabi 	u->name = xmalloc(strlen(name) + 1);
8324c4c9110Sbasabi 	(void) strcpy(u->name, name);
8334c4c9110Sbasabi 	u->home = NULL;
8344c4c9110Sbasabi 	if (type == CRONEVENT) {
8354c4c9110Sbasabi 		u->ctexists = TRUE;
8364c4c9110Sbasabi 		u->ctid = ecid++;
8374c4c9110Sbasabi 	} else {
8384c4c9110Sbasabi 		u->ctexists = FALSE;
8394c4c9110Sbasabi 		u->ctid = 0;
8404c4c9110Sbasabi 	}
8414c4c9110Sbasabi 	u->ctevents = NULL;
8424c4c9110Sbasabi 	u->atevents = NULL;
8434c4c9110Sbasabi 	u->aruncnt = 0;
8444c4c9110Sbasabi 	u->cruncnt = 0;
8454c4c9110Sbasabi 	u->nextusr = uhead;
8464c4c9110Sbasabi 	uhead = u;
8474c4c9110Sbasabi 	return (u);
8484c4c9110Sbasabi }
8494c4c9110Sbasabi 
8504c4c9110Sbasabi void
8514c4c9110Sbasabi init_cronevent(char *name, int first)
8524c4c9110Sbasabi {
8534c4c9110Sbasabi 	struct usr	*u;
8544c4c9110Sbasabi 
8554c4c9110Sbasabi 	if (first) {
8564c4c9110Sbasabi 		u = create_ulist(name, CRONEVENT);
8574c4c9110Sbasabi 		readcron(u, 0);
8584c4c9110Sbasabi 	} else {
8594c4c9110Sbasabi 		if ((u = find_usr(name)) == NULL) {
8604c4c9110Sbasabi 			u = create_ulist(name, CRONEVENT);
8614c4c9110Sbasabi 			readcron(u, 0);
8624c4c9110Sbasabi 		} else {
8634c4c9110Sbasabi 			u->ctexists = TRUE;
8644c4c9110Sbasabi 			rm_ctevents(u);
8654c4c9110Sbasabi 			el_remove(u->ctid, 0);
8664c4c9110Sbasabi 			readcron(u, 0);
8674c4c9110Sbasabi 		}
8684c4c9110Sbasabi 	}
8694c4c9110Sbasabi }
8704c4c9110Sbasabi 
8714c4c9110Sbasabi void
8724c4c9110Sbasabi init_atevent(char *name, time_t tim, int jobtype, int first)
8734c4c9110Sbasabi {
8744c4c9110Sbasabi 	struct usr	*u;
8754c4c9110Sbasabi 
8764c4c9110Sbasabi 	if (first) {
8774c4c9110Sbasabi 		u = create_ulist(name, ATEVENT);
8784c4c9110Sbasabi 		add_atevent(u, name, tim, jobtype);
8794c4c9110Sbasabi 	} else {
8804c4c9110Sbasabi 		if ((u = find_usr(name)) == NULL) {
8814c4c9110Sbasabi 			u = create_ulist(name, ATEVENT);
8824c4c9110Sbasabi 			add_atevent(u, name, tim, jobtype);
8834c4c9110Sbasabi 		} else {
8844c4c9110Sbasabi 			update_atevent(u, name, tim, jobtype);
8857c478bd9Sstevel@tonic-gate 		}
8867c478bd9Sstevel@tonic-gate 	}
8877c478bd9Sstevel@tonic-gate }
8887c478bd9Sstevel@tonic-gate 
8897c478bd9Sstevel@tonic-gate static void
8907c478bd9Sstevel@tonic-gate mod_ctab(char *name, time_t reftime)
8917c478bd9Sstevel@tonic-gate {
8927c478bd9Sstevel@tonic-gate 	struct	passwd	*pw;
8937c478bd9Sstevel@tonic-gate 	struct	stat	buf;
8947c478bd9Sstevel@tonic-gate 	struct	usr	*u;
895*5b08e637SChris Gerhard 	char	namebuf[LINE_MAX];
8967c478bd9Sstevel@tonic-gate 	char	*pname;
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate 	/* skip over ancillary file names */
8997c478bd9Sstevel@tonic-gate 	if (audit_cron_is_anc_name(name))
9007c478bd9Sstevel@tonic-gate 		return;
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 	if ((pw = getpwnam(name)) == NULL) {
9037c478bd9Sstevel@tonic-gate 		msg("No such user as %s - cron entries not created", name);
9047c478bd9Sstevel@tonic-gate 		return;
9057c478bd9Sstevel@tonic-gate 	}
9067c478bd9Sstevel@tonic-gate 	if (cwd != CRON) {
9077c478bd9Sstevel@tonic-gate 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
9084c4c9110Sbasabi 		    CRONDIR, name) >= sizeof (namebuf)) {
9097c478bd9Sstevel@tonic-gate 			msg("Too long path name %s - cron entries not created",
9104c4c9110Sbasabi 			    namebuf);
9117c478bd9Sstevel@tonic-gate 			return;
9127c478bd9Sstevel@tonic-gate 		}
9137c478bd9Sstevel@tonic-gate 		pname = namebuf;
9147c478bd9Sstevel@tonic-gate 	} else {
9157c478bd9Sstevel@tonic-gate 		pname = name;
9167c478bd9Sstevel@tonic-gate 	}
9177c478bd9Sstevel@tonic-gate 	/*
9187c478bd9Sstevel@tonic-gate 	 * a warning message is given by the crontab command so there is
9197c478bd9Sstevel@tonic-gate 	 * no need to give one here......  use this code if you only want
9207c478bd9Sstevel@tonic-gate 	 * users with a login shell of /usr/bin/sh to use cron
9217c478bd9Sstevel@tonic-gate 	 */
9227c478bd9Sstevel@tonic-gate #ifdef BOURNESHELLONLY
9237c478bd9Sstevel@tonic-gate 	if ((strcmp(pw->pw_shell, "") != 0) &&
9247c478bd9Sstevel@tonic-gate 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
9257c478bd9Sstevel@tonic-gate 		mail(name, BADSHELL, ERR_CANTEXECCRON);
9267c478bd9Sstevel@tonic-gate 		cron_unlink(pname);
9277c478bd9Sstevel@tonic-gate 		return;
9287c478bd9Sstevel@tonic-gate 	}
9297c478bd9Sstevel@tonic-gate #endif
9307c478bd9Sstevel@tonic-gate 	if (stat(pname, &buf)) {
9317c478bd9Sstevel@tonic-gate 		mail(name, BADSTAT, ERR_UNIXERR);
9327c478bd9Sstevel@tonic-gate 		cron_unlink(pname);
9337c478bd9Sstevel@tonic-gate 		return;
9347c478bd9Sstevel@tonic-gate 	}
9357c478bd9Sstevel@tonic-gate 	if (!S_ISREG(buf.st_mode)) {
9367c478bd9Sstevel@tonic-gate 		mail(name, BADTYPE, ERR_CRONTABENT);
9377c478bd9Sstevel@tonic-gate 		return;
9387c478bd9Sstevel@tonic-gate 	}
9397c478bd9Sstevel@tonic-gate 	if ((u = find_usr(name)) == NULL) {
9407c478bd9Sstevel@tonic-gate #ifdef DEBUG
9417c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "new user (%s) with a crontab\n", name);
9427c478bd9Sstevel@tonic-gate #endif
9434c4c9110Sbasabi 		u = create_ulist(name, CRONEVENT);
9444c4c9110Sbasabi 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
9457c478bd9Sstevel@tonic-gate 		(void) strcpy(u->home, pw->pw_dir);
9467c478bd9Sstevel@tonic-gate 		u->uid = pw->pw_uid;
9477c478bd9Sstevel@tonic-gate 		u->gid = pw->pw_gid;
9487c478bd9Sstevel@tonic-gate 		readcron(u, reftime);
9497c478bd9Sstevel@tonic-gate 	} else {
9507c478bd9Sstevel@tonic-gate 		u->uid = pw->pw_uid;
9517c478bd9Sstevel@tonic-gate 		u->gid = pw->pw_gid;
952d9f27107Sbasabi 		if (u->home != NULL) {
953d9f27107Sbasabi 			if (strcmp(u->home, pw->pw_dir) != 0) {
954d9f27107Sbasabi 				free(u->home);
955d9f27107Sbasabi 				u->home = xmalloc(strlen(pw->pw_dir) + 1);
956d9f27107Sbasabi 				(void) strcpy(u->home, pw->pw_dir);
957d9f27107Sbasabi 			}
958d9f27107Sbasabi 		} else {
9594c4c9110Sbasabi 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
9607c478bd9Sstevel@tonic-gate 			(void) strcpy(u->home, pw->pw_dir);
9617c478bd9Sstevel@tonic-gate 		}
9627c478bd9Sstevel@tonic-gate 		u->ctexists = TRUE;
9637c478bd9Sstevel@tonic-gate 		if (u->ctid == 0) {
9647c478bd9Sstevel@tonic-gate #ifdef DEBUG
9657c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "%s now has a crontab\n",
9667c478bd9Sstevel@tonic-gate 			    u->name);
9677c478bd9Sstevel@tonic-gate #endif
9687c478bd9Sstevel@tonic-gate 			/* user didnt have a crontab last time */
9697c478bd9Sstevel@tonic-gate 			u->ctid = ecid++;
9707c478bd9Sstevel@tonic-gate 			u->ctevents = NULL;
9717c478bd9Sstevel@tonic-gate 			readcron(u, reftime);
9727c478bd9Sstevel@tonic-gate 			return;
9737c478bd9Sstevel@tonic-gate 		}
9747c478bd9Sstevel@tonic-gate #ifdef DEBUG
9757c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s has revised his crontab\n", u->name);
9767c478bd9Sstevel@tonic-gate #endif
9777c478bd9Sstevel@tonic-gate 		rm_ctevents(u);
9787c478bd9Sstevel@tonic-gate 		el_remove(u->ctid, 0);
9797c478bd9Sstevel@tonic-gate 		readcron(u, reftime);
9807c478bd9Sstevel@tonic-gate 	}
9817c478bd9Sstevel@tonic-gate }
9827c478bd9Sstevel@tonic-gate 
9837c478bd9Sstevel@tonic-gate /* ARGSUSED */
9847c478bd9Sstevel@tonic-gate static void
9857c478bd9Sstevel@tonic-gate mod_atjob(char *name, time_t reftime)
9867c478bd9Sstevel@tonic-gate {
9877c478bd9Sstevel@tonic-gate 	char	*ptr;
9887c478bd9Sstevel@tonic-gate 	time_t	tim;
9897c478bd9Sstevel@tonic-gate 	struct	passwd	*pw;
9907c478bd9Sstevel@tonic-gate 	struct	stat	buf;
9917c478bd9Sstevel@tonic-gate 	struct	usr	*u;
9927c478bd9Sstevel@tonic-gate 	char	namebuf[PATH_MAX];
9937c478bd9Sstevel@tonic-gate 	char	*pname;
9947c478bd9Sstevel@tonic-gate 	int	jobtype;
9957c478bd9Sstevel@tonic-gate 
9967c478bd9Sstevel@tonic-gate 	ptr = name;
9977c478bd9Sstevel@tonic-gate 	if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
9987c478bd9Sstevel@tonic-gate 		return;
9997c478bd9Sstevel@tonic-gate 	ptr++;
10007c478bd9Sstevel@tonic-gate 	if (!isalpha(*ptr))
10017c478bd9Sstevel@tonic-gate 		return;
10027c478bd9Sstevel@tonic-gate 	jobtype = *ptr - 'a';
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 	/* check for audit ancillary file */
10057c478bd9Sstevel@tonic-gate 	if (audit_cron_is_anc_name(name))
10067c478bd9Sstevel@tonic-gate 		return;
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 	if (cwd != AT) {
10097c478bd9Sstevel@tonic-gate 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name)
10107c478bd9Sstevel@tonic-gate 		    >= sizeof (namebuf)) {
10117c478bd9Sstevel@tonic-gate 			return;
10127c478bd9Sstevel@tonic-gate 		}
10137c478bd9Sstevel@tonic-gate 		pname = namebuf;
10147c478bd9Sstevel@tonic-gate 	} else {
10157c478bd9Sstevel@tonic-gate 		pname = name;
10167c478bd9Sstevel@tonic-gate 	}
10177c478bd9Sstevel@tonic-gate 	if (stat(pname, &buf) || jobtype >= NQUEUE) {
10187c478bd9Sstevel@tonic-gate 		cron_unlink(pname);
10197c478bd9Sstevel@tonic-gate 		return;
10207c478bd9Sstevel@tonic-gate 	}
10217c478bd9Sstevel@tonic-gate 	if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) {
10227c478bd9Sstevel@tonic-gate 		cron_unlink(pname);
10237c478bd9Sstevel@tonic-gate 		return;
10247c478bd9Sstevel@tonic-gate 	}
10257c478bd9Sstevel@tonic-gate 	if ((pw = getpwuid(buf.st_uid)) == NULL) {
10267c478bd9Sstevel@tonic-gate 		cron_unlink(pname);
10277c478bd9Sstevel@tonic-gate 		return;
10287c478bd9Sstevel@tonic-gate 	}
10297c478bd9Sstevel@tonic-gate 	/*
10307c478bd9Sstevel@tonic-gate 	 * a warning message is given by the at command so there is no
10317c478bd9Sstevel@tonic-gate 	 * need to give one here......use this code if you only want
10327c478bd9Sstevel@tonic-gate 	 * users with a login shell of /usr/bin/sh to use cron
10337c478bd9Sstevel@tonic-gate 	 */
10347c478bd9Sstevel@tonic-gate #ifdef BOURNESHELLONLY
10357c478bd9Sstevel@tonic-gate 	if ((strcmp(pw->pw_shell, "") != 0) &&
10367c478bd9Sstevel@tonic-gate 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
10377c478bd9Sstevel@tonic-gate 		mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT);
10387c478bd9Sstevel@tonic-gate 		cron_unlink(pname);
10397c478bd9Sstevel@tonic-gate 		return;
10407c478bd9Sstevel@tonic-gate 	}
10417c478bd9Sstevel@tonic-gate #endif
10427c478bd9Sstevel@tonic-gate 	if ((u = find_usr(pw->pw_name)) == NULL) {
10437c478bd9Sstevel@tonic-gate #ifdef DEBUG
10447c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "new user (%s) with an at job = %s\n",
10454c4c9110Sbasabi 		    pw->pw_name, name);
10467c478bd9Sstevel@tonic-gate #endif
10474c4c9110Sbasabi 		u = create_ulist(pw->pw_name, ATEVENT);
10484c4c9110Sbasabi 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
10497c478bd9Sstevel@tonic-gate 		(void) strcpy(u->home, pw->pw_dir);
10507c478bd9Sstevel@tonic-gate 		u->uid = pw->pw_uid;
10517c478bd9Sstevel@tonic-gate 		u->gid = pw->pw_gid;
10527c478bd9Sstevel@tonic-gate 		add_atevent(u, name, tim, jobtype);
10537c478bd9Sstevel@tonic-gate 	} else {
10547c478bd9Sstevel@tonic-gate 		u->uid = pw->pw_uid;
10557c478bd9Sstevel@tonic-gate 		u->gid = pw->pw_gid;
10564c4c9110Sbasabi 		free(u->home);
10574c4c9110Sbasabi 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
10584c4c9110Sbasabi 		(void) strcpy(u->home, pw->pw_dir);
10594c4c9110Sbasabi 		update_atevent(u, name, tim, jobtype);
10607c478bd9Sstevel@tonic-gate 	}
10617c478bd9Sstevel@tonic-gate }
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate static void
10647c478bd9Sstevel@tonic-gate add_atevent(struct usr *u, char *job, time_t tim, int jobtype)
10657c478bd9Sstevel@tonic-gate {
10667c478bd9Sstevel@tonic-gate 	struct event *e;
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 	e = xmalloc(sizeof (struct event));
10697c478bd9Sstevel@tonic-gate 	e->etype = jobtype;
10704c4c9110Sbasabi 	e->cmd = xmalloc(strlen(job) + 1);
10717c478bd9Sstevel@tonic-gate 	(void) strcpy(e->cmd, job);
10727c478bd9Sstevel@tonic-gate 	e->u = u;
10737c478bd9Sstevel@tonic-gate 	e->link = u->atevents;
10747c478bd9Sstevel@tonic-gate 	u->atevents = e;
10757c478bd9Sstevel@tonic-gate 	e->of.at.exists = TRUE;
10767c478bd9Sstevel@tonic-gate 	e->of.at.eventid = ecid++;
10777c478bd9Sstevel@tonic-gate 	if (tim < init_time)	/* old job */
10787c478bd9Sstevel@tonic-gate 		e->time = init_time;
10797c478bd9Sstevel@tonic-gate 	else
10807c478bd9Sstevel@tonic-gate 		e->time = tim;
10817c478bd9Sstevel@tonic-gate #ifdef DEBUG
10827c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "add_atevent: user=%s, job=%s, time=%ld\n",
10834c4c9110Sbasabi 	    u->name, e->cmd, e->time);
10847c478bd9Sstevel@tonic-gate #endif
1085e2553d68SSerge Dussud 	if (el_add(e, e->time, e->of.at.eventid) < 0) {
1086e2553d68SSerge Dussud 		ignore_msg("add_atevent", "at", e);
1087e2553d68SSerge Dussud 	}
10887c478bd9Sstevel@tonic-gate }
10897c478bd9Sstevel@tonic-gate 
10904c4c9110Sbasabi void
10914c4c9110Sbasabi update_atevent(struct usr *u, char *name, time_t tim, int jobtype)
10924c4c9110Sbasabi {
10934c4c9110Sbasabi 	struct event *e;
10944c4c9110Sbasabi 
10954c4c9110Sbasabi 	e = u->atevents;
10964c4c9110Sbasabi 	while (e != NULL) {
10974c4c9110Sbasabi 		if (strcmp(e->cmd, name) == 0) {
10984c4c9110Sbasabi 			e->of.at.exists = TRUE;
10994c4c9110Sbasabi 			break;
11004c4c9110Sbasabi 		} else {
11014c4c9110Sbasabi 			e = e->link;
11024c4c9110Sbasabi 		}
11034c4c9110Sbasabi 	}
11044c4c9110Sbasabi 	if (e == NULL) {
11054c4c9110Sbasabi #ifdef DEBUG
11064c4c9110Sbasabi 		(void) fprintf(stderr, "%s has a new at job = %s\n",
11074c4c9110Sbasabi 		    u->name, name);
11084c4c9110Sbasabi #endif
11094c4c9110Sbasabi 			add_atevent(u, name, tim, jobtype);
11104c4c9110Sbasabi 	}
11114c4c9110Sbasabi }
11127c478bd9Sstevel@tonic-gate 
11137c478bd9Sstevel@tonic-gate static char line[CTLINESIZE];	/* holds a line from a crontab file */
11147c478bd9Sstevel@tonic-gate static int cursor;		/* cursor for the above line */
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate static void
11177c478bd9Sstevel@tonic-gate readcron(struct usr *u, time_t reftime)
11187c478bd9Sstevel@tonic-gate {
11197c478bd9Sstevel@tonic-gate 	/*
11207c478bd9Sstevel@tonic-gate 	 * readcron reads in a crontab file for a user (u). The list of
11217c478bd9Sstevel@tonic-gate 	 * events for user u is built, and u->events is made to point to
11227c478bd9Sstevel@tonic-gate 	 * this list. Each event is also entered into the main event
11237c478bd9Sstevel@tonic-gate 	 * list.
11247c478bd9Sstevel@tonic-gate 	 */
11257c478bd9Sstevel@tonic-gate 	FILE *cf;	/* cf will be a user's crontab file */
11267c478bd9Sstevel@tonic-gate 	struct event *e;
11277c478bd9Sstevel@tonic-gate 	int start;
11287c478bd9Sstevel@tonic-gate 	unsigned int i;
11297c478bd9Sstevel@tonic-gate 	char namebuf[PATH_MAX];
11307c478bd9Sstevel@tonic-gate 	char *pname;
1131*5b08e637SChris Gerhard 	struct shared *tz = NULL;
1132*5b08e637SChris Gerhard 	struct shared *home = NULL;
1133*5b08e637SChris Gerhard 	struct shared *shell = NULL;
11347c478bd9Sstevel@tonic-gate 	int lineno = 0;
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	/* read the crontab file */
11377c478bd9Sstevel@tonic-gate 	cte_init();		/* Init error handling */
11387c478bd9Sstevel@tonic-gate 	if (cwd != CRON) {
11397c478bd9Sstevel@tonic-gate 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
11407c478bd9Sstevel@tonic-gate 		    CRONDIR, u->name) >= sizeof (namebuf)) {
11417c478bd9Sstevel@tonic-gate 			return;
11427c478bd9Sstevel@tonic-gate 		}
11437c478bd9Sstevel@tonic-gate 		pname = namebuf;
11447c478bd9Sstevel@tonic-gate 	} else {
11457c478bd9Sstevel@tonic-gate 		pname = u->name;
11467c478bd9Sstevel@tonic-gate 	}
11477c478bd9Sstevel@tonic-gate 	if ((cf = fopen(pname, "r")) == NULL) {
11487c478bd9Sstevel@tonic-gate 		mail(u->name, NOREAD, ERR_UNIXERR);
11497c478bd9Sstevel@tonic-gate 		return;
11507c478bd9Sstevel@tonic-gate 	}
11517c478bd9Sstevel@tonic-gate 	while (fgets(line, CTLINESIZE, cf) != NULL) {
1152*5b08e637SChris Gerhard 		char *tmp;
11537c478bd9Sstevel@tonic-gate 		/* process a line of a crontab file */
11547c478bd9Sstevel@tonic-gate 		lineno++;
11557c478bd9Sstevel@tonic-gate 		if (cte_istoomany())
11567c478bd9Sstevel@tonic-gate 			break;
11577c478bd9Sstevel@tonic-gate 		cursor = 0;
11587c478bd9Sstevel@tonic-gate 		while (line[cursor] == ' ' || line[cursor] == '\t')
11597c478bd9Sstevel@tonic-gate 			cursor++;
11607c478bd9Sstevel@tonic-gate 		if (line[cursor] == '#' || line[cursor] == '\n')
11617c478bd9Sstevel@tonic-gate 			continue;
1162*5b08e637SChris Gerhard 
1163*5b08e637SChris Gerhard 		if (strncmp(&line[cursor], ENV_TZ,
1164*5b08e637SChris Gerhard 		    strlen(ENV_TZ)) == 0) {
1165*5b08e637SChris Gerhard 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
1166*5b08e637SChris Gerhard 				*tmp = NULL;
1167*5b08e637SChris Gerhard 			}
1168*5b08e637SChris Gerhard 
1169*5b08e637SChris Gerhard 			if (!isvalid_tz(&line[cursor + strlen(ENV_TZ)], NULL,
1170*5b08e637SChris Gerhard 			    _VTZ_ALL)) {
1171*5b08e637SChris Gerhard 				cte_add(lineno, line);
1172*5b08e637SChris Gerhard 				break;
1173*5b08e637SChris Gerhard 			}
1174*5b08e637SChris Gerhard 			if (tz == NULL || strcmp(&line[cursor], get_obj(tz))) {
1175*5b08e637SChris Gerhard 				rel_shared(tz);
1176*5b08e637SChris Gerhard 				tz = create_shared_str(&line[cursor]);
1177*5b08e637SChris Gerhard 			}
1178*5b08e637SChris Gerhard 			continue;
1179*5b08e637SChris Gerhard 		}
1180*5b08e637SChris Gerhard 
1181*5b08e637SChris Gerhard 		if (strncmp(&line[cursor], ENV_HOME,
1182*5b08e637SChris Gerhard 		    strlen(ENV_HOME)) == 0) {
1183*5b08e637SChris Gerhard 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
1184*5b08e637SChris Gerhard 				*tmp = NULL;
1185*5b08e637SChris Gerhard 			}
1186*5b08e637SChris Gerhard 			if (home == NULL ||
1187*5b08e637SChris Gerhard 			    strcmp(&line[cursor], get_obj(home))) {
1188*5b08e637SChris Gerhard 				rel_shared(home);
1189*5b08e637SChris Gerhard 				home = create_shared_str(
1190*5b08e637SChris Gerhard 				    &line[cursor + strlen(ENV_HOME)]);
1191*5b08e637SChris Gerhard 			}
1192*5b08e637SChris Gerhard 			continue;
1193*5b08e637SChris Gerhard 		}
1194*5b08e637SChris Gerhard 
1195*5b08e637SChris Gerhard 		if (strncmp(&line[cursor], ENV_SHELL,
1196*5b08e637SChris Gerhard 		    strlen(ENV_SHELL)) == 0) {
1197*5b08e637SChris Gerhard 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
1198*5b08e637SChris Gerhard 				*tmp = NULL;
1199*5b08e637SChris Gerhard 			}
1200*5b08e637SChris Gerhard 			if (shell == NULL ||
1201*5b08e637SChris Gerhard 			    strcmp(&line[cursor], get_obj(shell))) {
1202*5b08e637SChris Gerhard 				rel_shared(shell);
1203*5b08e637SChris Gerhard 				shell = create_shared_str(&line[cursor]);
1204*5b08e637SChris Gerhard 			}
1205*5b08e637SChris Gerhard 			continue;
1206*5b08e637SChris Gerhard 		}
1207*5b08e637SChris Gerhard 
12087c478bd9Sstevel@tonic-gate 		e = xmalloc(sizeof (struct event));
12097c478bd9Sstevel@tonic-gate 		e->etype = CRONEVENT;
12107c478bd9Sstevel@tonic-gate 		if (!(((e->of.ct.minute = next_field(0, 59)) != NULL) &&
12117c478bd9Sstevel@tonic-gate 		    ((e->of.ct.hour = next_field(0, 23)) != NULL) &&
12127c478bd9Sstevel@tonic-gate 		    ((e->of.ct.daymon = next_field(1, 31)) != NULL) &&
12137c478bd9Sstevel@tonic-gate 		    ((e->of.ct.month = next_field(1, 12)) != NULL) &&
12147c478bd9Sstevel@tonic-gate 		    ((e->of.ct.dayweek = next_field(0, 6)) != NULL))) {
12157c478bd9Sstevel@tonic-gate 			free(e);
12167c478bd9Sstevel@tonic-gate 			cte_add(lineno, line);
12177c478bd9Sstevel@tonic-gate 			continue;
12187c478bd9Sstevel@tonic-gate 		}
12197c478bd9Sstevel@tonic-gate 		while (line[cursor] == ' ' || line[cursor] == '\t')
12207c478bd9Sstevel@tonic-gate 			cursor++;
12217c478bd9Sstevel@tonic-gate 		if (line[cursor] == '\n' || line[cursor] == '\0')
12227c478bd9Sstevel@tonic-gate 			continue;
12237c478bd9Sstevel@tonic-gate 		/* get the command to execute	*/
12247c478bd9Sstevel@tonic-gate 		start = cursor;
12257c478bd9Sstevel@tonic-gate again:
12267c478bd9Sstevel@tonic-gate 		while ((line[cursor] != '%') &&
12277c478bd9Sstevel@tonic-gate 		    (line[cursor] != '\n') &&
12287c478bd9Sstevel@tonic-gate 		    (line[cursor] != '\0') &&
12297c478bd9Sstevel@tonic-gate 		    (line[cursor] != '\\'))
12307c478bd9Sstevel@tonic-gate 			cursor++;
12317c478bd9Sstevel@tonic-gate 		if (line[cursor] == '\\') {
12327c478bd9Sstevel@tonic-gate 			cursor += 2;
12337c478bd9Sstevel@tonic-gate 			goto again;
12347c478bd9Sstevel@tonic-gate 		}
12354c4c9110Sbasabi 		e->cmd = xmalloc(cursor-start + 1);
12364c4c9110Sbasabi 		(void) strncpy(e->cmd, line + start, cursor-start);
12377c478bd9Sstevel@tonic-gate 		e->cmd[cursor-start] = '\0';
12387c478bd9Sstevel@tonic-gate 		/* see if there is any standard input	*/
12397c478bd9Sstevel@tonic-gate 		if (line[cursor] == '%') {
12404c4c9110Sbasabi 			e->of.ct.input = xmalloc(strlen(line)-cursor + 1);
12414c4c9110Sbasabi 			(void) strcpy(e->of.ct.input, line + cursor + 1);
12427c478bd9Sstevel@tonic-gate 			for (i = 0; i < strlen(e->of.ct.input); i++) {
12437c478bd9Sstevel@tonic-gate 				if (e->of.ct.input[i] == '%')
12447c478bd9Sstevel@tonic-gate 					e->of.ct.input[i] = '\n';
12457c478bd9Sstevel@tonic-gate 			}
12467c478bd9Sstevel@tonic-gate 		} else {
12477c478bd9Sstevel@tonic-gate 			e->of.ct.input = NULL;
12487c478bd9Sstevel@tonic-gate 		}
1249*5b08e637SChris Gerhard 		/* set the timezone of this entry */
1250*5b08e637SChris Gerhard 		e->of.ct.tz = dup_shared(tz);
1251*5b08e637SChris Gerhard 		/* set the shell of this entry */
1252*5b08e637SChris Gerhard 		e->of.ct.shell = dup_shared(shell);
1253*5b08e637SChris Gerhard 		/* set the home of this entry */
1254*5b08e637SChris Gerhard 		e->of.ct.home = dup_shared(home);
12557c478bd9Sstevel@tonic-gate 		/* have the event point to it's owner	*/
12567c478bd9Sstevel@tonic-gate 		e->u = u;
12577c478bd9Sstevel@tonic-gate 		/* insert this event at the front of this user's event list */
12587c478bd9Sstevel@tonic-gate 		e->link = u->ctevents;
12597c478bd9Sstevel@tonic-gate 		u->ctevents = e;
12607c478bd9Sstevel@tonic-gate 		/* set the time for the first occurance of this event	*/
12617c478bd9Sstevel@tonic-gate 		e->time = next_time(e, reftime);
12627c478bd9Sstevel@tonic-gate 		/* finally, add this event to the main event list	*/
1263e2553d68SSerge Dussud 		switch (el_add(e, e->time, u->ctid)) {
1264e2553d68SSerge Dussud 		case -1:
1265e2553d68SSerge Dussud 			ignore_msg("readcron", "cron", e);
1266e2553d68SSerge Dussud 			break;
1267e2553d68SSerge Dussud 		case -2: /* event time lower than init time */
1268e2553d68SSerge Dussud 			reset_needed = 1;
1269e2553d68SSerge Dussud 			break;
1270e2553d68SSerge Dussud 		}
12717c478bd9Sstevel@tonic-gate 		cte_valid();
12727c478bd9Sstevel@tonic-gate #ifdef DEBUG
12737c478bd9Sstevel@tonic-gate 		cftime(timebuf, "%C", &e->time);
12747c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "inserting cron event %s at %ld (%s)\n",
12754c4c9110Sbasabi 		    e->cmd, e->time, timebuf);
12767c478bd9Sstevel@tonic-gate #endif
12777c478bd9Sstevel@tonic-gate 	}
12787c478bd9Sstevel@tonic-gate 	cte_sendmail(u->name);	/* mail errors if any to user */
12797c478bd9Sstevel@tonic-gate 	(void) fclose(cf);
1280*5b08e637SChris Gerhard 	rel_shared(tz);
1281*5b08e637SChris Gerhard 	rel_shared(shell);
1282*5b08e637SChris Gerhard 	rel_shared(home);
12837c478bd9Sstevel@tonic-gate }
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate /*
12867c478bd9Sstevel@tonic-gate  * Below are the functions for handling of errors in crontabs. Concept is to
12877c478bd9Sstevel@tonic-gate  * collect faulty lines and send one email at the end of the crontab
12887c478bd9Sstevel@tonic-gate  * evaluation. If there are erroneous lines only ((cte_nvalid == 0), evaluation
12897c478bd9Sstevel@tonic-gate  * of crontab is aborted. Otherwise reading of crontab is continued to the end
12907c478bd9Sstevel@tonic-gate  * of the file but no further error logging appears.
12917c478bd9Sstevel@tonic-gate  */
12927c478bd9Sstevel@tonic-gate static void
12937c478bd9Sstevel@tonic-gate cte_init()
12947c478bd9Sstevel@tonic-gate {
12957c478bd9Sstevel@tonic-gate 	if (cte_text == NULL)
12967c478bd9Sstevel@tonic-gate 		cte_text = xmalloc(MAILBUFLEN);
12977c478bd9Sstevel@tonic-gate 	(void) strlcpy(cte_text, cte_intro, MAILBUFLEN);
12987c478bd9Sstevel@tonic-gate 	cte_lp = cte_text + sizeof (cte_intro) - 1;
12997c478bd9Sstevel@tonic-gate 	cte_free = MAILBINITFREE;
13007c478bd9Sstevel@tonic-gate 	cte_nvalid = 0;
13017c478bd9Sstevel@tonic-gate }
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate static void
13047c478bd9Sstevel@tonic-gate cte_add(int lineno, char *ctline)
13057c478bd9Sstevel@tonic-gate {
13067c478bd9Sstevel@tonic-gate 	int len;
13077c478bd9Sstevel@tonic-gate 	char *p;
13087c478bd9Sstevel@tonic-gate 
13097c478bd9Sstevel@tonic-gate 	if (cte_free >= LINELIMIT) {
13107c478bd9Sstevel@tonic-gate 		(void) sprintf(cte_lp, "%4d: ", lineno);
13117c478bd9Sstevel@tonic-gate 		(void) strlcat(cte_lp, ctline, LINELIMIT - 1);
13127c478bd9Sstevel@tonic-gate 		len = strlen(cte_lp);
13137c478bd9Sstevel@tonic-gate 		if (cte_lp[len - 1] != '\n') {
13147c478bd9Sstevel@tonic-gate 			cte_lp[len++] = '\n';
13157c478bd9Sstevel@tonic-gate 			cte_lp[len] = '\0';
13167c478bd9Sstevel@tonic-gate 		}
13177c478bd9Sstevel@tonic-gate 		for (p = cte_lp; *p; p++) {
13187c478bd9Sstevel@tonic-gate 			if (isprint(*p) || *p == '\n' || *p == '\t')
13197c478bd9Sstevel@tonic-gate 				continue;
13207c478bd9Sstevel@tonic-gate 			*p = '.';
13217c478bd9Sstevel@tonic-gate 		}
13227c478bd9Sstevel@tonic-gate 		cte_lp += len;
13237c478bd9Sstevel@tonic-gate 		cte_free -= len;
13247c478bd9Sstevel@tonic-gate 		if (cte_free < LINELIMIT) {
13257c478bd9Sstevel@tonic-gate 			size_t buflen = MAILBUFLEN - (cte_lp - cte_text);
13267c478bd9Sstevel@tonic-gate 			(void) strlcpy(cte_lp, cte_trail1, buflen);
13277c478bd9Sstevel@tonic-gate 			if (cte_nvalid == 0)
13287c478bd9Sstevel@tonic-gate 				(void) strlcat(cte_lp, cte_trail2, buflen);
13297c478bd9Sstevel@tonic-gate 		}
13307c478bd9Sstevel@tonic-gate 	}
13317c478bd9Sstevel@tonic-gate }
13327c478bd9Sstevel@tonic-gate 
13337c478bd9Sstevel@tonic-gate static void
13347c478bd9Sstevel@tonic-gate cte_valid()
13357c478bd9Sstevel@tonic-gate {
13367c478bd9Sstevel@tonic-gate 	cte_nvalid++;
13377c478bd9Sstevel@tonic-gate }
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate static int
13407c478bd9Sstevel@tonic-gate cte_istoomany()
13417c478bd9Sstevel@tonic-gate {
13427c478bd9Sstevel@tonic-gate 	/*
13437c478bd9Sstevel@tonic-gate 	 * Return TRUE only if all lines are faulty. So evaluation of
13447c478bd9Sstevel@tonic-gate 	 * a crontab is not aborted if at least one valid line was found.
13457c478bd9Sstevel@tonic-gate 	 */
13467c478bd9Sstevel@tonic-gate 	return (cte_nvalid == 0 && cte_free < LINELIMIT);
13477c478bd9Sstevel@tonic-gate }
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate static void
13507c478bd9Sstevel@tonic-gate cte_sendmail(char *username)
13517c478bd9Sstevel@tonic-gate {
13527c478bd9Sstevel@tonic-gate 	if (cte_free < MAILBINITFREE)
13537c478bd9Sstevel@tonic-gate 		mail(username, cte_text, ERR_CRONTABENT);
13547c478bd9Sstevel@tonic-gate }
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate /*
13577c478bd9Sstevel@tonic-gate  * Send mail with error message to a user
13587c478bd9Sstevel@tonic-gate  */
13597c478bd9Sstevel@tonic-gate static void
13607c478bd9Sstevel@tonic-gate mail(char *usrname, char *mesg, int format)
13617c478bd9Sstevel@tonic-gate {
13627c478bd9Sstevel@tonic-gate 	/* mail mails a user a message.	*/
13637c478bd9Sstevel@tonic-gate 	FILE *pipe;
13647c478bd9Sstevel@tonic-gate 	char *temp;
13657c478bd9Sstevel@tonic-gate 	struct passwd	*ruser_ids;
13667c478bd9Sstevel@tonic-gate 	pid_t fork_val;
13677c478bd9Sstevel@tonic-gate 	int saveerrno = errno;
13687c478bd9Sstevel@tonic-gate 	struct utsname	name;
13697c478bd9Sstevel@tonic-gate 
13707c478bd9Sstevel@tonic-gate #ifdef TESTING
13717c478bd9Sstevel@tonic-gate 	return;
13727c478bd9Sstevel@tonic-gate #endif
13737c478bd9Sstevel@tonic-gate 	(void) uname(&name);
13747c478bd9Sstevel@tonic-gate 	if ((fork_val = fork()) == (pid_t)-1) {
13757c478bd9Sstevel@tonic-gate 		msg("cron cannot fork\n");
13767c478bd9Sstevel@tonic-gate 		return;
13777c478bd9Sstevel@tonic-gate 	}
13787c478bd9Sstevel@tonic-gate 	if (fork_val == 0) {
13797c478bd9Sstevel@tonic-gate 		child_sigreset();
13807c478bd9Sstevel@tonic-gate 		contract_clear_template();
13817c478bd9Sstevel@tonic-gate 		if ((ruser_ids = getpwnam(usrname)) == NULL)
13827c478bd9Sstevel@tonic-gate 			exit(0);
13837c478bd9Sstevel@tonic-gate 		(void) setuid(ruser_ids->pw_uid);
13844c4c9110Sbasabi 		temp = xmalloc(strlen(MAIL) + strlen(usrname) + 2);
13857c478bd9Sstevel@tonic-gate 		(void) sprintf(temp, "%s %s", MAIL, usrname);
13867c478bd9Sstevel@tonic-gate 		pipe = popen(temp, "w");
13877c478bd9Sstevel@tonic-gate 		if (pipe != NULL) {
13887c478bd9Sstevel@tonic-gate 			(void) fprintf(pipe, "To: %s\n", usrname);
13897c478bd9Sstevel@tonic-gate 			switch (format) {
13907c478bd9Sstevel@tonic-gate 			case ERR_CRONTABENT:
13917c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe, CRONTABERR);
13927c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe, "Your \"crontab\" on %s\n",
13937c478bd9Sstevel@tonic-gate 				    name.nodename);
13947c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe, mesg);
13957c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe,
13967c478bd9Sstevel@tonic-gate 				    "\nEntries or crontab have been ignored\n");
13977c478bd9Sstevel@tonic-gate 				break;
13987c478bd9Sstevel@tonic-gate 			case ERR_UNIXERR:
13997c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe, "Subject: %s\n\n", mesg);
14007c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe,
14017c478bd9Sstevel@tonic-gate 				    "The error on %s was \"%s\"\n",
14027c478bd9Sstevel@tonic-gate 				    name.nodename, errmsg(saveerrno));
14037c478bd9Sstevel@tonic-gate 				break;
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 			case ERR_CANTEXECCRON:
14067c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe,
14077c478bd9Sstevel@tonic-gate 				"Subject: Couldn't run your \"cron\" job\n\n");
14087c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe,
14097c478bd9Sstevel@tonic-gate 				    "Your \"cron\" job on %s ", name.nodename);
14107c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe, "couldn't be run\n");
14117c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe, "%s\n", mesg);
14127c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe,
14137c478bd9Sstevel@tonic-gate 				"The error was \"%s\"\n", errmsg(saveerrno));
14147c478bd9Sstevel@tonic-gate 				break;
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 			case ERR_CANTEXECAT:
14177c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe,
14187c478bd9Sstevel@tonic-gate 				"Subject: Couldn't run your \"at\" job\n\n");
14197c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe, "Your \"at\" job on %s ",
14207c478bd9Sstevel@tonic-gate 				    name.nodename);
14217c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe, "couldn't be run\n");
14227c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe, "%s\n", mesg);
14237c478bd9Sstevel@tonic-gate 				(void) fprintf(pipe,
14247c478bd9Sstevel@tonic-gate 				"The error was \"%s\"\n", errmsg(saveerrno));
14257c478bd9Sstevel@tonic-gate 				break;
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate 			default:
14287c478bd9Sstevel@tonic-gate 				break;
14297c478bd9Sstevel@tonic-gate 			}
14307c478bd9Sstevel@tonic-gate 			(void) pclose(pipe);
14317c478bd9Sstevel@tonic-gate 		}
14327c478bd9Sstevel@tonic-gate 		free(temp);
14337c478bd9Sstevel@tonic-gate 		exit(0);
14347c478bd9Sstevel@tonic-gate 	}
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate 	contract_abandon_latest(fork_val);
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 	if (cron_pid == getpid()) {
14397c478bd9Sstevel@tonic-gate 		miscpid_insert(fork_val);
14407c478bd9Sstevel@tonic-gate 	}
14417c478bd9Sstevel@tonic-gate }
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate static char *
14447c478bd9Sstevel@tonic-gate next_field(int lower, int upper)
14457c478bd9Sstevel@tonic-gate {
14467c478bd9Sstevel@tonic-gate 	/*
14477c478bd9Sstevel@tonic-gate 	 * next_field returns a pointer to a string which holds the next
14487c478bd9Sstevel@tonic-gate 	 * field of a line of a crontab file.
14497c478bd9Sstevel@tonic-gate 	 *   if (numbers in this field are out of range (lower..upper),
14507c478bd9Sstevel@tonic-gate 	 *	or there is a syntax error) then
14517c478bd9Sstevel@tonic-gate 	 *	NULL is returned, and a mail message is sent to the
14527c478bd9Sstevel@tonic-gate 	 *	user telling him which line the error was in.
14537c478bd9Sstevel@tonic-gate 	 */
14547c478bd9Sstevel@tonic-gate 
14557c478bd9Sstevel@tonic-gate 	char *s;
14567c478bd9Sstevel@tonic-gate 	int num, num2, start;
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	while ((line[cursor] == ' ') || (line[cursor] == '\t'))
14597c478bd9Sstevel@tonic-gate 		cursor++;
14607c478bd9Sstevel@tonic-gate 	start = cursor;
14617c478bd9Sstevel@tonic-gate 	if (line[cursor] == '\0') {
14627c478bd9Sstevel@tonic-gate 		return (NULL);
14637c478bd9Sstevel@tonic-gate 	}
14647c478bd9Sstevel@tonic-gate 	if (line[cursor] == '*') {
14657c478bd9Sstevel@tonic-gate 		cursor++;
14667c478bd9Sstevel@tonic-gate 		if ((line[cursor] != ' ') && (line[cursor] != '\t'))
14677c478bd9Sstevel@tonic-gate 			return (NULL);
14687c478bd9Sstevel@tonic-gate 		s = xmalloc(2);
14697c478bd9Sstevel@tonic-gate 		(void) strcpy(s, "*");
14707c478bd9Sstevel@tonic-gate 		return (s);
14717c478bd9Sstevel@tonic-gate 	}
14727c478bd9Sstevel@tonic-gate 	for (;;) {
14737c478bd9Sstevel@tonic-gate 		if (!isdigit(line[cursor]))
14747c478bd9Sstevel@tonic-gate 			return (NULL);
14757c478bd9Sstevel@tonic-gate 		num = 0;
14767c478bd9Sstevel@tonic-gate 		do {
14777c478bd9Sstevel@tonic-gate 			num = num*10 + (line[cursor]-'0');
14787c478bd9Sstevel@tonic-gate 		} while (isdigit(line[++cursor]));
14797c478bd9Sstevel@tonic-gate 		if ((num < lower) || (num > upper))
14807c478bd9Sstevel@tonic-gate 			return (NULL);
14817c478bd9Sstevel@tonic-gate 		if (line[cursor] == '-') {
14827c478bd9Sstevel@tonic-gate 			if (!isdigit(line[++cursor]))
14837c478bd9Sstevel@tonic-gate 				return (NULL);
14847c478bd9Sstevel@tonic-gate 			num2 = 0;
14857c478bd9Sstevel@tonic-gate 			do {
14867c478bd9Sstevel@tonic-gate 				num2 = num2*10 + (line[cursor]-'0');
14877c478bd9Sstevel@tonic-gate 			} while (isdigit(line[++cursor]));
14887c478bd9Sstevel@tonic-gate 			if ((num2 < lower) || (num2 > upper))
14897c478bd9Sstevel@tonic-gate 				return (NULL);
14907c478bd9Sstevel@tonic-gate 		}
14917c478bd9Sstevel@tonic-gate 		if ((line[cursor] == ' ') || (line[cursor] == '\t'))
14927c478bd9Sstevel@tonic-gate 			break;
14937c478bd9Sstevel@tonic-gate 		if (line[cursor] == '\0')
14947c478bd9Sstevel@tonic-gate 			return (NULL);
14957c478bd9Sstevel@tonic-gate 		if (line[cursor++] != ',')
14967c478bd9Sstevel@tonic-gate 			return (NULL);
14977c478bd9Sstevel@tonic-gate 	}
14984c4c9110Sbasabi 	s = xmalloc(cursor-start + 1);
14994c4c9110Sbasabi 	(void) strncpy(s, line + start, cursor-start);
15007c478bd9Sstevel@tonic-gate 	s[cursor-start] = '\0';
15017c478bd9Sstevel@tonic-gate 	return (s);
15027c478bd9Sstevel@tonic-gate }
15037c478bd9Sstevel@tonic-gate 
15047c478bd9Sstevel@tonic-gate #define	tm_cmp(t1, t2) (\
15057c478bd9Sstevel@tonic-gate 	(t1)->tm_year == (t2)->tm_year && \
15067c478bd9Sstevel@tonic-gate 	(t1)->tm_mon == (t2)->tm_mon && \
15077c478bd9Sstevel@tonic-gate 	(t1)->tm_mday == (t2)->tm_mday && \
15087c478bd9Sstevel@tonic-gate 	(t1)->tm_hour == (t2)->tm_hour && \
15097c478bd9Sstevel@tonic-gate 	(t1)->tm_min == (t2)->tm_min)
15107c478bd9Sstevel@tonic-gate 
15117c478bd9Sstevel@tonic-gate #define	tm_setup(tp, yr, mon, dy, hr, min, dst) \
15127c478bd9Sstevel@tonic-gate 	(tp)->tm_year = yr; \
15137c478bd9Sstevel@tonic-gate 	(tp)->tm_mon = mon; \
15147c478bd9Sstevel@tonic-gate 	(tp)->tm_mday = dy; \
15157c478bd9Sstevel@tonic-gate 	(tp)->tm_hour = hr; \
15167c478bd9Sstevel@tonic-gate 	(tp)->tm_min = min; \
15177c478bd9Sstevel@tonic-gate 	(tp)->tm_isdst = dst; \
15187c478bd9Sstevel@tonic-gate 	(tp)->tm_sec = 0; \
15197c478bd9Sstevel@tonic-gate 	(tp)->tm_wday = 0; \
15207c478bd9Sstevel@tonic-gate 	(tp)->tm_yday = 0;
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate /*
15237c478bd9Sstevel@tonic-gate  * modification for bugid 1104537. the second argument to next_time is
15247c478bd9Sstevel@tonic-gate  * now the value of time(2) to be used. if this is 0, then use the
15257c478bd9Sstevel@tonic-gate  * current time. otherwise, the second argument is the time from which to
15267c478bd9Sstevel@tonic-gate  * calculate things. this is useful to correct situations where you've
15277c478bd9Sstevel@tonic-gate  * gone backwards in time (I.e. the system's internal clock is correcting
15287c478bd9Sstevel@tonic-gate  * itself backwards).
15297c478bd9Sstevel@tonic-gate  */
15307c478bd9Sstevel@tonic-gate 
1531*5b08e637SChris Gerhard 
1532*5b08e637SChris Gerhard 
15337c478bd9Sstevel@tonic-gate static time_t
1534*5b08e637SChris Gerhard tz_next_time(struct event *e, time_t tflag)
15357c478bd9Sstevel@tonic-gate {
15367c478bd9Sstevel@tonic-gate 	/*
15377c478bd9Sstevel@tonic-gate 	 * returns the integer time for the next occurance of event e.
15387c478bd9Sstevel@tonic-gate 	 * the following fields have ranges as indicated:
15397c478bd9Sstevel@tonic-gate 	 * PRGM  | min	hour	day of month	mon	day of week
15407c478bd9Sstevel@tonic-gate 	 * ------|-------------------------------------------------------
15417c478bd9Sstevel@tonic-gate 	 * cron  | 0-59	0-23	    1-31	1-12	0-6 (0=sunday)
15427c478bd9Sstevel@tonic-gate 	 * time  | 0-59	0-23	    1-31	0-11	0-6 (0=sunday)
15437c478bd9Sstevel@tonic-gate 	 * NOTE: this routine is hard to understand.
15447c478bd9Sstevel@tonic-gate 	 */
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 	struct tm *tm, ref_tm, tmp, tmp1, tmp2;
1547*5b08e637SChris Gerhard 	int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days;
1548*5b08e637SChris Gerhard 	int d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd;
1549*5b08e637SChris Gerhard 	int today;
15507c478bd9Sstevel@tonic-gate 	time_t t, ref_t, t1, t2, zone_start;
15517c478bd9Sstevel@tonic-gate 	int fallback;
15527c478bd9Sstevel@tonic-gate 	extern int days_btwn(int, int, int, int, int, int);
15537c478bd9Sstevel@tonic-gate 
15547c478bd9Sstevel@tonic-gate 	if (tflag == 0) {
15557c478bd9Sstevel@tonic-gate 		t = time(NULL);	/* original way of doing things	*/
15567c478bd9Sstevel@tonic-gate 	} else {
15577c478bd9Sstevel@tonic-gate 		t =  tflag;
15587c478bd9Sstevel@tonic-gate 	}
15597c478bd9Sstevel@tonic-gate 
15607c478bd9Sstevel@tonic-gate 	tm = &ref_tm;	/* use a local variable and call localtime_r() */
15617c478bd9Sstevel@tonic-gate 	ref_t = t;	/* keep a copy of the reference time */
15627c478bd9Sstevel@tonic-gate 
15637c478bd9Sstevel@tonic-gate recalc:
15647c478bd9Sstevel@tonic-gate 	fallback = 0;
15657c478bd9Sstevel@tonic-gate 
15667c478bd9Sstevel@tonic-gate 	(void) localtime_r(&t, tm);
15677c478bd9Sstevel@tonic-gate 
15687c478bd9Sstevel@tonic-gate 	if (daylight) {
15697c478bd9Sstevel@tonic-gate 		tmp = *tm;
15707c478bd9Sstevel@tonic-gate 		tmp.tm_isdst = (tm->tm_isdst > 0 ? 0 : 1);
15717c478bd9Sstevel@tonic-gate 		t1 = xmktime(&tmp);
15727c478bd9Sstevel@tonic-gate 		/*
15737c478bd9Sstevel@tonic-gate 		 * see if we will have timezone switch over, and clock will
15747c478bd9Sstevel@tonic-gate 		 * fall back. zone_start will hold the time when it happens
15757c478bd9Sstevel@tonic-gate 		 * (ie time of PST -> PDT switch over).
15767c478bd9Sstevel@tonic-gate 		 */
15777c478bd9Sstevel@tonic-gate 		if (tm->tm_isdst != tmp.tm_isdst &&
15787c478bd9Sstevel@tonic-gate 		    (t1 - t) == (timezone - altzone) &&
15797c478bd9Sstevel@tonic-gate 		    tm_cmp(tm, &tmp)) {
15807c478bd9Sstevel@tonic-gate 			zone_start = get_switching_time(tmp.tm_isdst, t);
15817c478bd9Sstevel@tonic-gate 			fallback = 1;
15827c478bd9Sstevel@tonic-gate 		}
15837c478bd9Sstevel@tonic-gate 	}
15847c478bd9Sstevel@tonic-gate 
15854c4c9110Sbasabi 	tm_mon = next_ge(tm->tm_mon + 1, e->of.ct.month) - 1;	/* 0-11 */
15867c478bd9Sstevel@tonic-gate 	tm_mday = next_ge(tm->tm_mday, e->of.ct.daymon);	/* 1-31 */
15877c478bd9Sstevel@tonic-gate 	tm_wday = next_ge(tm->tm_wday, e->of.ct.dayweek);	/* 0-6	*/
15887c478bd9Sstevel@tonic-gate 	today = TRUE;
15897c478bd9Sstevel@tonic-gate 	if ((strcmp(e->of.ct.daymon, "*") == 0 && tm->tm_wday != tm_wday) ||
15907c478bd9Sstevel@tonic-gate 	    (strcmp(e->of.ct.dayweek, "*") == 0 && tm->tm_mday != tm_mday) ||
15917c478bd9Sstevel@tonic-gate 	    (tm->tm_mday != tm_mday && tm->tm_wday != tm_wday) ||
15927c478bd9Sstevel@tonic-gate 	    (tm->tm_mon != tm_mon)) {
15937c478bd9Sstevel@tonic-gate 		today = FALSE;
15947c478bd9Sstevel@tonic-gate 	}
15957c478bd9Sstevel@tonic-gate 	m = tm->tm_min + (t == ref_t ? 1 : 0);
15967c478bd9Sstevel@tonic-gate 	if ((tm->tm_hour + 1) <= next_ge(tm->tm_hour, e->of.ct.hour)) {
15977c478bd9Sstevel@tonic-gate 		m = 0;
15987c478bd9Sstevel@tonic-gate 	}
15997c478bd9Sstevel@tonic-gate 	min = next_ge(m%60, e->of.ct.minute);
16007c478bd9Sstevel@tonic-gate 	carry = (min < m) ? 1 : 0;
16017c478bd9Sstevel@tonic-gate 	h = tm->tm_hour + carry;
16027c478bd9Sstevel@tonic-gate 	hr = next_ge(h%24, e->of.ct.hour);
16037c478bd9Sstevel@tonic-gate 	carry = (hr < h) ? 1 : 0;
16047c478bd9Sstevel@tonic-gate 
16057c478bd9Sstevel@tonic-gate 	if (carry == 0 && today) {
16067c478bd9Sstevel@tonic-gate 		/* this event must occur today */
16077c478bd9Sstevel@tonic-gate 		tm_setup(&tmp, tm->tm_year, tm->tm_mon, tm->tm_mday,
16084c4c9110Sbasabi 		    hr, min, tm->tm_isdst);
16097c478bd9Sstevel@tonic-gate 		tmp1 = tmp;
16107c478bd9Sstevel@tonic-gate 		if ((t1 = xmktime(&tmp1)) == (time_t)-1) {
16117c478bd9Sstevel@tonic-gate 			return (0);
16127c478bd9Sstevel@tonic-gate 		}
16137c478bd9Sstevel@tonic-gate 		if (daylight && tmp.tm_isdst != tmp1.tm_isdst) {
16147c478bd9Sstevel@tonic-gate 			/* In case we are falling back */
16157c478bd9Sstevel@tonic-gate 			if (fallback) {
16167c478bd9Sstevel@tonic-gate 				/* we may need to run the job once more. */
16177c478bd9Sstevel@tonic-gate 				t = zone_start;
16187c478bd9Sstevel@tonic-gate 				goto recalc;
16197c478bd9Sstevel@tonic-gate 			}
16207c478bd9Sstevel@tonic-gate 
16217c478bd9Sstevel@tonic-gate 			/*
16227c478bd9Sstevel@tonic-gate 			 * In case we are not in falling back period,
16237c478bd9Sstevel@tonic-gate 			 * calculate the time assuming the DST. If the
16247c478bd9Sstevel@tonic-gate 			 * date/time is not altered by mktime, it is the
16257c478bd9Sstevel@tonic-gate 			 * time to execute the job.
16267c478bd9Sstevel@tonic-gate 			 */
16277c478bd9Sstevel@tonic-gate 			tmp2 = tmp;
16287c478bd9Sstevel@tonic-gate 			tmp2.tm_isdst = tmp1.tm_isdst;
16297c478bd9Sstevel@tonic-gate 			if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
16307c478bd9Sstevel@tonic-gate 				return (0);
16317c478bd9Sstevel@tonic-gate 			}
16327c478bd9Sstevel@tonic-gate 			if (tmp1.tm_isdst == tmp2.tm_isdst &&
16337c478bd9Sstevel@tonic-gate 			    tm_cmp(&tmp, &tmp2)) {
16347c478bd9Sstevel@tonic-gate 				/*
16357c478bd9Sstevel@tonic-gate 				 * We got a valid time.
16367c478bd9Sstevel@tonic-gate 				 */
16377c478bd9Sstevel@tonic-gate 				return (t1);
16387c478bd9Sstevel@tonic-gate 			} else {
16397c478bd9Sstevel@tonic-gate 				/*
16407c478bd9Sstevel@tonic-gate 				 * If the date does not match even if
16417c478bd9Sstevel@tonic-gate 				 * we assume the alternate timezone, then
16427c478bd9Sstevel@tonic-gate 				 * it must be the invalid time. eg
16437c478bd9Sstevel@tonic-gate 				 * 2am while switching 1:59am to 3am.
16447c478bd9Sstevel@tonic-gate 				 * t1 should point the time before the
16457c478bd9Sstevel@tonic-gate 				 * switching over as we've calculate the
16467c478bd9Sstevel@tonic-gate 				 * time with assuming alternate zone.
16477c478bd9Sstevel@tonic-gate 				 */
16487c478bd9Sstevel@tonic-gate 				if (tmp1.tm_isdst != tmp2.tm_isdst) {
16497c478bd9Sstevel@tonic-gate 					t = get_switching_time(tmp1.tm_isdst,
16504c4c9110Sbasabi 					    t1);
16517c478bd9Sstevel@tonic-gate 				} else {
16527c478bd9Sstevel@tonic-gate 					/* does this really happen? */
16537c478bd9Sstevel@tonic-gate 					t = get_switching_time(tmp1.tm_isdst,
16544c4c9110Sbasabi 					    t1 - abs(timezone - altzone));
16557c478bd9Sstevel@tonic-gate 				}
1656*5b08e637SChris Gerhard 				if (t == (time_t)-1) {
16577c478bd9Sstevel@tonic-gate 					return (0);
1658*5b08e637SChris Gerhard 				}
16597c478bd9Sstevel@tonic-gate 			}
16607c478bd9Sstevel@tonic-gate 			goto recalc;
16617c478bd9Sstevel@tonic-gate 		}
16627c478bd9Sstevel@tonic-gate 		if (tm_cmp(&tmp, &tmp1)) {
16637c478bd9Sstevel@tonic-gate 			/* got valid time */
16647c478bd9Sstevel@tonic-gate 			return (t1);
16657c478bd9Sstevel@tonic-gate 		} else {
16667c478bd9Sstevel@tonic-gate 			/*
16677c478bd9Sstevel@tonic-gate 			 * This should never happen, but just in
16687c478bd9Sstevel@tonic-gate 			 * case, we fall back to the old code.
16697c478bd9Sstevel@tonic-gate 			 */
16707c478bd9Sstevel@tonic-gate 			if (tm->tm_min > min) {
16717c478bd9Sstevel@tonic-gate 				t += (time_t)(hr-tm->tm_hour-1) * HOUR +
16724c4c9110Sbasabi 				    (time_t)(60-tm->tm_min + min) * MINUTE;
16737c478bd9Sstevel@tonic-gate 			} else {
16747c478bd9Sstevel@tonic-gate 				t += (time_t)(hr-tm->tm_hour) * HOUR +
16754c4c9110Sbasabi 				    (time_t)(min-tm->tm_min) * MINUTE;
16767c478bd9Sstevel@tonic-gate 			}
16777c478bd9Sstevel@tonic-gate 			t1 = t;
16787c478bd9Sstevel@tonic-gate 			t -= (time_t)tm->tm_sec;
16797c478bd9Sstevel@tonic-gate 			(void) localtime_r(&t, &tmp);
16807c478bd9Sstevel@tonic-gate 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
16817c478bd9Sstevel@tonic-gate 				t -= (timezone - altzone);
16827c478bd9Sstevel@tonic-gate 			return ((t <= ref_t) ? t1 : t);
16837c478bd9Sstevel@tonic-gate 		}
16847c478bd9Sstevel@tonic-gate 	}
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 	/*
16877c478bd9Sstevel@tonic-gate 	 * Job won't run today, however if we have a switch over within
16887c478bd9Sstevel@tonic-gate 	 * one hour and we will have one hour time drifting back in this
16897c478bd9Sstevel@tonic-gate 	 * period, we may need to run the job one more time if the job was
16907c478bd9Sstevel@tonic-gate 	 * set to run on this hour of clock.
16917c478bd9Sstevel@tonic-gate 	 */
16927c478bd9Sstevel@tonic-gate 	if (fallback) {
16937c478bd9Sstevel@tonic-gate 		t = zone_start;
16947c478bd9Sstevel@tonic-gate 		goto recalc;
16957c478bd9Sstevel@tonic-gate 	}
16967c478bd9Sstevel@tonic-gate 
16977c478bd9Sstevel@tonic-gate 	min = next_ge(0, e->of.ct.minute);
16987c478bd9Sstevel@tonic-gate 	hr = next_ge(0, e->of.ct.hour);
16997c478bd9Sstevel@tonic-gate 
17007c478bd9Sstevel@tonic-gate 	/*
17017c478bd9Sstevel@tonic-gate 	 * calculate the date of the next occurance of this event, which
17027c478bd9Sstevel@tonic-gate 	 * will be on a different day than the current
17037c478bd9Sstevel@tonic-gate 	 */
17047c478bd9Sstevel@tonic-gate 
17057c478bd9Sstevel@tonic-gate 	/* check monthly day specification	*/
17064c4c9110Sbasabi 	d1 = tm->tm_mday + 1;
17074c4c9110Sbasabi 	day1 = next_ge((d1-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1,
17084c4c9110Sbasabi 	    e->of.ct.daymon);
17097c478bd9Sstevel@tonic-gate 	carry1 = (day1 < d1) ? 1 : 0;
17107c478bd9Sstevel@tonic-gate 
17117c478bd9Sstevel@tonic-gate 	/* check weekly day specification	*/
17124c4c9110Sbasabi 	d2 = tm->tm_wday + 1;
17137c478bd9Sstevel@tonic-gate 	wday = next_ge(d2%7, e->of.ct.dayweek);
17147c478bd9Sstevel@tonic-gate 	if (wday < d2)
17157c478bd9Sstevel@tonic-gate 		daysahead = 7 - d2 + wday;
17167c478bd9Sstevel@tonic-gate 	else
17177c478bd9Sstevel@tonic-gate 		daysahead = wday - d2;
17184c4c9110Sbasabi 	day2 = (d1 + daysahead-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1;
17197c478bd9Sstevel@tonic-gate 	carry2 = (day2 < d1) ? 1 : 0;
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate 	/*
17227c478bd9Sstevel@tonic-gate 	 *	based on their respective specifications, day1, and day2 give
17237c478bd9Sstevel@tonic-gate 	 *	the day of the month for the next occurance of this event.
17247c478bd9Sstevel@tonic-gate 	 */
17257c478bd9Sstevel@tonic-gate 	if ((strcmp(e->of.ct.daymon, "*") == 0) &&
17267c478bd9Sstevel@tonic-gate 	    (strcmp(e->of.ct.dayweek, "*") != 0)) {
17277c478bd9Sstevel@tonic-gate 		day1 = day2;
17287c478bd9Sstevel@tonic-gate 		carry1 = carry2;
17297c478bd9Sstevel@tonic-gate 	}
17307c478bd9Sstevel@tonic-gate 	if ((strcmp(e->of.ct.daymon, "*") != 0) &&
17317c478bd9Sstevel@tonic-gate 	    (strcmp(e->of.ct.dayweek, "*") == 0)) {
17327c478bd9Sstevel@tonic-gate 		day2 = day1;
17337c478bd9Sstevel@tonic-gate 		carry2 = carry1;
17347c478bd9Sstevel@tonic-gate 	}
17357c478bd9Sstevel@tonic-gate 
17367c478bd9Sstevel@tonic-gate 	yr = tm->tm_year;
17377c478bd9Sstevel@tonic-gate 	if ((carry1 && carry2) || (tm->tm_mon != tm_mon)) {
17387c478bd9Sstevel@tonic-gate 		/* event does not occur in this month	*/
17394c4c9110Sbasabi 		m = tm->tm_mon + 1;
17404c4c9110Sbasabi 		mon = next_ge(m%12 + 1, e->of.ct.month) - 1;	/* 0..11 */
17417c478bd9Sstevel@tonic-gate 		carry = (mon < m) ? 1 : 0;
17427c478bd9Sstevel@tonic-gate 		yr += carry;
17437c478bd9Sstevel@tonic-gate 		/* recompute day1 and day2	*/
17447c478bd9Sstevel@tonic-gate 		day1 = next_ge(1, e->of.ct.daymon);
17457c478bd9Sstevel@tonic-gate 		db = days_btwn(tm->tm_mon, tm->tm_mday, tm->tm_year, mon,
17464c4c9110Sbasabi 		    1, yr) + 1;
17474c4c9110Sbasabi 		wd = (tm->tm_wday + db)%7;
17487c478bd9Sstevel@tonic-gate 		/* wd is the day of the week of the first of month mon	*/
17497c478bd9Sstevel@tonic-gate 		wday = next_ge(wd, e->of.ct.dayweek);
17507c478bd9Sstevel@tonic-gate 		if (wday < wd)
17517c478bd9Sstevel@tonic-gate 			day2 = 1 + 7 - wd + wday;
17527c478bd9Sstevel@tonic-gate 		else
17537c478bd9Sstevel@tonic-gate 			day2 = 1 + wday - wd;
17547c478bd9Sstevel@tonic-gate 		if ((strcmp(e->of.ct.daymon, "*") != 0) &&
17557c478bd9Sstevel@tonic-gate 		    (strcmp(e->of.ct.dayweek, "*") == 0))
17567c478bd9Sstevel@tonic-gate 			day2 = day1;
17577c478bd9Sstevel@tonic-gate 		if ((strcmp(e->of.ct.daymon, "*") == 0) &&
17587c478bd9Sstevel@tonic-gate 		    (strcmp(e->of.ct.dayweek, "*") != 0))
17597c478bd9Sstevel@tonic-gate 			day1 = day2;
17607c478bd9Sstevel@tonic-gate 		day = (day1 < day2) ? day1 : day2;
17617c478bd9Sstevel@tonic-gate 	} else {			/* event occurs in this month	*/
17627c478bd9Sstevel@tonic-gate 		mon = tm->tm_mon;
17637c478bd9Sstevel@tonic-gate 		if (!carry1 && !carry2)
17647c478bd9Sstevel@tonic-gate 			day = (day1 < day2) ? day1 : day2;
17657c478bd9Sstevel@tonic-gate 		else if (!carry1)
17667c478bd9Sstevel@tonic-gate 			day = day1;
17677c478bd9Sstevel@tonic-gate 		else
17687c478bd9Sstevel@tonic-gate 			day = day2;
17697c478bd9Sstevel@tonic-gate 	}
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 	/*
17727c478bd9Sstevel@tonic-gate 	 * now that we have the min, hr, day, mon, yr of the next event,
17737c478bd9Sstevel@tonic-gate 	 * figure out what time that turns out to be.
17747c478bd9Sstevel@tonic-gate 	 */
17757c478bd9Sstevel@tonic-gate 	tm_setup(&tmp, yr, mon, day, hr, min, -1);
17767c478bd9Sstevel@tonic-gate 	tmp2 = tmp;
17777c478bd9Sstevel@tonic-gate 	if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
17787c478bd9Sstevel@tonic-gate 		return (0);
17797c478bd9Sstevel@tonic-gate 	}
17807c478bd9Sstevel@tonic-gate 	if (tm_cmp(&tmp, &tmp2)) {
17817c478bd9Sstevel@tonic-gate 		/*
17827c478bd9Sstevel@tonic-gate 		 * mktime returns clock for the current time zone. If the
17837c478bd9Sstevel@tonic-gate 		 * target date was in fallback period, it needs to be adjusted
17847c478bd9Sstevel@tonic-gate 		 * to the time comes first.
17857c478bd9Sstevel@tonic-gate 		 * Suppose, we are at Jan and scheduling job at 1:30am10/26/03.
17867c478bd9Sstevel@tonic-gate 		 * mktime returns the time in PST, but 1:30am in PDT comes
17877c478bd9Sstevel@tonic-gate 		 * first. So reverse the tm_isdst, and see if we have such
17887c478bd9Sstevel@tonic-gate 		 * time/date.
17897c478bd9Sstevel@tonic-gate 		 */
17907c478bd9Sstevel@tonic-gate 		if (daylight) {
17917c478bd9Sstevel@tonic-gate 			int dst = tmp2.tm_isdst;
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate 			tmp2 = tmp;
17947c478bd9Sstevel@tonic-gate 			tmp2.tm_isdst = (dst > 0 ? 0 : 1);
17957c478bd9Sstevel@tonic-gate 			if ((t2 = xmktime(&tmp2)) == (time_t)-1) {
17967c478bd9Sstevel@tonic-gate 				return (0);
17977c478bd9Sstevel@tonic-gate 			}
17987c478bd9Sstevel@tonic-gate 			if (tm_cmp(&tmp, &tmp2)) {
17997c478bd9Sstevel@tonic-gate 				/*
18007c478bd9Sstevel@tonic-gate 				 * same time/date found in the opposite zone.
18017c478bd9Sstevel@tonic-gate 				 * check the clock to see which comes early.
18027c478bd9Sstevel@tonic-gate 				 */
18037c478bd9Sstevel@tonic-gate 				if (t2 > ref_t && t2 < t1) {
18047c478bd9Sstevel@tonic-gate 					t1 = t2;
18057c478bd9Sstevel@tonic-gate 				}
18067c478bd9Sstevel@tonic-gate 			}
18077c478bd9Sstevel@tonic-gate 		}
18087c478bd9Sstevel@tonic-gate 		return (t1);
18097c478bd9Sstevel@tonic-gate 	} else {
18107c478bd9Sstevel@tonic-gate 		/*
18117c478bd9Sstevel@tonic-gate 		 * mktime has set different time/date for the given date.
18127c478bd9Sstevel@tonic-gate 		 * This means that the next job is scheduled to be run on the
18137c478bd9Sstevel@tonic-gate 		 * invalid time. There are three possible invalid date/time.
18147c478bd9Sstevel@tonic-gate 		 * 1. Non existing day of the month. such as April 31th.
18157c478bd9Sstevel@tonic-gate 		 * 2. Feb 29th in the non-leap year.
18167c478bd9Sstevel@tonic-gate 		 * 3. Time gap during the DST switch over.
18177c478bd9Sstevel@tonic-gate 		 */
18187c478bd9Sstevel@tonic-gate 		d1 = days_in_mon(mon, yr);
18197c478bd9Sstevel@tonic-gate 		if ((mon != 1 && day > d1) || (mon == 1 && day > 29)) {
18207c478bd9Sstevel@tonic-gate 			/*
18217c478bd9Sstevel@tonic-gate 			 * see if we have got a specific date which
18227c478bd9Sstevel@tonic-gate 			 * is invalid.
18237c478bd9Sstevel@tonic-gate 			 */
18247c478bd9Sstevel@tonic-gate 			if (strcmp(e->of.ct.dayweek, "*") == 0 &&
18254c4c9110Sbasabi 			    mon == (next_ge((mon + 1)%12 + 1,
18264c4c9110Sbasabi 			    e->of.ct.month) - 1) &&
18277c478bd9Sstevel@tonic-gate 			    day <= next_ge(1, e->of.ct.daymon)) {
18287c478bd9Sstevel@tonic-gate 				/* job never run */
18297c478bd9Sstevel@tonic-gate 				return (0);
18307c478bd9Sstevel@tonic-gate 			}
18317c478bd9Sstevel@tonic-gate 			/*
18327c478bd9Sstevel@tonic-gate 			 * Since the day has gone invalid, we need to go to
18337c478bd9Sstevel@tonic-gate 			 * next month, and recalcuate the first occurrence.
18347c478bd9Sstevel@tonic-gate 			 * eg the cron tab such as:
18357c478bd9Sstevel@tonic-gate 			 * 0 0 1,15,31 1,2,3,4,5 * /usr/bin....
18367c478bd9Sstevel@tonic-gate 			 * 2/31 is invalid, so the next job is 3/1.
18377c478bd9Sstevel@tonic-gate 			 */
18387c478bd9Sstevel@tonic-gate 			tmp2 = tmp;
18397c478bd9Sstevel@tonic-gate 			tmp2.tm_min = 0;
18407c478bd9Sstevel@tonic-gate 			tmp2.tm_hour = 0;
18417c478bd9Sstevel@tonic-gate 			tmp2.tm_mday = 1; /* 1st day of the month */
18427c478bd9Sstevel@tonic-gate 			if (mon == 11) {
18437c478bd9Sstevel@tonic-gate 				tmp2.tm_mon = 0;
18447c478bd9Sstevel@tonic-gate 				tmp2.tm_year = yr + 1;
18457c478bd9Sstevel@tonic-gate 			} else {
18467c478bd9Sstevel@tonic-gate 				tmp2.tm_mon = mon + 1;
18477c478bd9Sstevel@tonic-gate 			}
18487c478bd9Sstevel@tonic-gate 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
18497c478bd9Sstevel@tonic-gate 				return (0);
18507c478bd9Sstevel@tonic-gate 			}
18517c478bd9Sstevel@tonic-gate 		} else if (mon == 1 && day > d1) {
18527c478bd9Sstevel@tonic-gate 			/*
18537c478bd9Sstevel@tonic-gate 			 * ie 29th in the non-leap year. Forwarding the
18547c478bd9Sstevel@tonic-gate 			 * clock to Feb 29th 00:00 (March 1st), and recalculate
18557c478bd9Sstevel@tonic-gate 			 * the next time.
18567c478bd9Sstevel@tonic-gate 			 */
18577c478bd9Sstevel@tonic-gate 			tmp2 = tmp;
18587c478bd9Sstevel@tonic-gate 			tmp2.tm_min = 0;
18597c478bd9Sstevel@tonic-gate 			tmp2.tm_hour = 0;
18607c478bd9Sstevel@tonic-gate 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
18617c478bd9Sstevel@tonic-gate 				return (0);
18627c478bd9Sstevel@tonic-gate 			}
18637c478bd9Sstevel@tonic-gate 		} else if (daylight) {
18647c478bd9Sstevel@tonic-gate 			/*
18657c478bd9Sstevel@tonic-gate 			 * Non existing time, eg 2am PST during summer time
18667c478bd9Sstevel@tonic-gate 			 * switch.
18677c478bd9Sstevel@tonic-gate 			 * We need to get the correct isdst which we are
18687c478bd9Sstevel@tonic-gate 			 * swithing to, by adding time difference to make sure
18697c478bd9Sstevel@tonic-gate 			 * that t2 is in the zone being switched.
18707c478bd9Sstevel@tonic-gate 			 */
18717c478bd9Sstevel@tonic-gate 			t2 = t1;
18727c478bd9Sstevel@tonic-gate 			t2 += abs(timezone - altzone);
18737c478bd9Sstevel@tonic-gate 			(void) localtime_r(&t2, &tmp2);
18747c478bd9Sstevel@tonic-gate 			zone_start = get_switching_time(tmp2.tm_isdst,
18754c4c9110Sbasabi 			    t1 - abs(timezone - altzone));
18767c478bd9Sstevel@tonic-gate 			if (zone_start == (time_t)-1) {
18777c478bd9Sstevel@tonic-gate 				return (0);
18787c478bd9Sstevel@tonic-gate 			}
18797c478bd9Sstevel@tonic-gate 			t = zone_start;
18807c478bd9Sstevel@tonic-gate 		} else {
18817c478bd9Sstevel@tonic-gate 			/*
18827c478bd9Sstevel@tonic-gate 			 * This should never happen, but fall back to the
18837c478bd9Sstevel@tonic-gate 			 * old code.
18847c478bd9Sstevel@tonic-gate 			 */
18857c478bd9Sstevel@tonic-gate 			days = days_btwn(tm->tm_mon,
18864c4c9110Sbasabi 			    tm->tm_mday, tm->tm_year, mon, day, yr);
18877c478bd9Sstevel@tonic-gate 			t += (time_t)(23-tm->tm_hour)*HOUR
18884c4c9110Sbasabi 			    + (time_t)(60-tm->tm_min)*MINUTE
18894c4c9110Sbasabi 			    + (time_t)hr*HOUR + (time_t)min*MINUTE
18904c4c9110Sbasabi 			    + (time_t)days*DAY;
18917c478bd9Sstevel@tonic-gate 			t1 = t;
18927c478bd9Sstevel@tonic-gate 			t -= (time_t)tm->tm_sec;
18937c478bd9Sstevel@tonic-gate 			(void) localtime_r(&t, &tmp);
18947c478bd9Sstevel@tonic-gate 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
18957c478bd9Sstevel@tonic-gate 				t -= (timezone - altzone);
18967c478bd9Sstevel@tonic-gate 			return (t <= ref_t ? t1 : t);
18977c478bd9Sstevel@tonic-gate 		}
18987c478bd9Sstevel@tonic-gate 		goto recalc;
18997c478bd9Sstevel@tonic-gate 	}
19007c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
19017c478bd9Sstevel@tonic-gate }
19027c478bd9Sstevel@tonic-gate 
1903*5b08e637SChris Gerhard static time_t
1904*5b08e637SChris Gerhard next_time(struct event *e, time_t tflag)
1905*5b08e637SChris Gerhard {
1906*5b08e637SChris Gerhard 	if (e->of.ct.tz != NULL) {
1907*5b08e637SChris Gerhard 		time_t ret;
1908*5b08e637SChris Gerhard 
1909*5b08e637SChris Gerhard 		(void) putenv((char *)get_obj(e->of.ct.tz));
1910*5b08e637SChris Gerhard 		tzset();
1911*5b08e637SChris Gerhard 		ret = tz_next_time(e, tflag);
1912*5b08e637SChris Gerhard 		(void) putenv(tzone);
1913*5b08e637SChris Gerhard 		tzset();
1914*5b08e637SChris Gerhard 		return (ret);
1915*5b08e637SChris Gerhard 	} else {
1916*5b08e637SChris Gerhard 		return (tz_next_time(e, tflag));
1917*5b08e637SChris Gerhard 	}
1918*5b08e637SChris Gerhard }
1919*5b08e637SChris Gerhard 
19207c478bd9Sstevel@tonic-gate /*
19217c478bd9Sstevel@tonic-gate  * This returns TOD in time_t that zone switch will happen, and this
19227c478bd9Sstevel@tonic-gate  * will be called when clock fallback is about to happen.
19237c478bd9Sstevel@tonic-gate  * (ie 30minutes before the time of PST -> PDT switch. 2:00 AM PST
19247c478bd9Sstevel@tonic-gate  * will fall back to 1:00 PDT. So this function will be called only
19257c478bd9Sstevel@tonic-gate  * for the time between 1:00 AM PST and 2:00 PST(1:00 PST)).
19267c478bd9Sstevel@tonic-gate  * First goes through the common time differences to see if zone
19277c478bd9Sstevel@tonic-gate  * switch happens at those minutes later. If not, check every minutes
19287c478bd9Sstevel@tonic-gate  * until 6 hours ahead see if it happens(We might have 45minutes
19297c478bd9Sstevel@tonic-gate  * fallback).
19307c478bd9Sstevel@tonic-gate  */
19317c478bd9Sstevel@tonic-gate static time_t
19327c478bd9Sstevel@tonic-gate get_switching_time(int to_dst, time_t t_ref)
19337c478bd9Sstevel@tonic-gate {
19347c478bd9Sstevel@tonic-gate 	time_t t, t1;
19357c478bd9Sstevel@tonic-gate 	struct tm tmp, tmp1;
19367c478bd9Sstevel@tonic-gate 	int hints[] = { 60, 120, 30, 90, 0}; /* minutes */
19377c478bd9Sstevel@tonic-gate 	int i;
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate 	(void) localtime_r(&t_ref, &tmp);
19407c478bd9Sstevel@tonic-gate 	tmp1 = tmp;
19417c478bd9Sstevel@tonic-gate 	tmp1.tm_sec = 0;
19427c478bd9Sstevel@tonic-gate 	tmp1.tm_min = 0;
19437c478bd9Sstevel@tonic-gate 	if ((t = xmktime(&tmp1)) == (time_t)-1)
19447c478bd9Sstevel@tonic-gate 		return ((time_t)-1);
19457c478bd9Sstevel@tonic-gate 
19467c478bd9Sstevel@tonic-gate 	/* fast path */
19477c478bd9Sstevel@tonic-gate 	for (i = 0; hints[i] != 0; i++) {
19487c478bd9Sstevel@tonic-gate 		t1 = t + hints[i] * 60;
19497c478bd9Sstevel@tonic-gate 		(void) localtime_r(&t1, &tmp1);
19507c478bd9Sstevel@tonic-gate 		if (tmp1.tm_isdst == to_dst) {
19517c478bd9Sstevel@tonic-gate 			t1--;
19527c478bd9Sstevel@tonic-gate 			(void) localtime_r(&t1, &tmp1);
19537c478bd9Sstevel@tonic-gate 			if (tmp1.tm_isdst != to_dst) {
19547c478bd9Sstevel@tonic-gate 				return (t1 + 1);
19557c478bd9Sstevel@tonic-gate 			}
19567c478bd9Sstevel@tonic-gate 		}
19577c478bd9Sstevel@tonic-gate 	}
19587c478bd9Sstevel@tonic-gate 
19597c478bd9Sstevel@tonic-gate 	/* ugly, but don't know other than this. */
19607c478bd9Sstevel@tonic-gate 	tmp1 = tmp;
19617c478bd9Sstevel@tonic-gate 	tmp1.tm_sec = 0;
19627c478bd9Sstevel@tonic-gate 	if ((t = xmktime(&tmp1)) == (time_t)-1)
19637c478bd9Sstevel@tonic-gate 		return ((time_t)-1);
19647c478bd9Sstevel@tonic-gate 	while (t < (t_ref + 6*60*60)) { /* 6 hours should be enough */
19657c478bd9Sstevel@tonic-gate 		t += 60; /* at least one minute, I assume */
19667c478bd9Sstevel@tonic-gate 		(void) localtime_r(&t, &tmp);
19677c478bd9Sstevel@tonic-gate 		if (tmp.tm_isdst == to_dst)
19687c478bd9Sstevel@tonic-gate 			return (t);
19697c478bd9Sstevel@tonic-gate 	}
19707c478bd9Sstevel@tonic-gate 	return ((time_t)-1);
19717c478bd9Sstevel@tonic-gate }
19727c478bd9Sstevel@tonic-gate 
19737c478bd9Sstevel@tonic-gate static time_t
19747c478bd9Sstevel@tonic-gate xmktime(struct tm *tmp)
19757c478bd9Sstevel@tonic-gate {
19767c478bd9Sstevel@tonic-gate 	time_t ret;
19777c478bd9Sstevel@tonic-gate 
19787c478bd9Sstevel@tonic-gate 	if ((ret = mktime(tmp)) == (time_t)-1) {
19797c478bd9Sstevel@tonic-gate 		if (errno == EOVERFLOW) {
19807c478bd9Sstevel@tonic-gate 			return ((time_t)-1);
19817c478bd9Sstevel@tonic-gate 		}
19827c478bd9Sstevel@tonic-gate 		crabort("internal error: mktime failed",
19834c4c9110Sbasabi 		    REMOVE_FIFO|CONSOLE_MSG);
19847c478bd9Sstevel@tonic-gate 	}
19857c478bd9Sstevel@tonic-gate 	return (ret);
19867c478bd9Sstevel@tonic-gate }
19877c478bd9Sstevel@tonic-gate 
19887c478bd9Sstevel@tonic-gate #define	DUMMY	100
19897c478bd9Sstevel@tonic-gate 
19907c478bd9Sstevel@tonic-gate static int
19917c478bd9Sstevel@tonic-gate next_ge(int current, char *list)
19927c478bd9Sstevel@tonic-gate {
19937c478bd9Sstevel@tonic-gate 	/*
19947c478bd9Sstevel@tonic-gate 	 * list is a character field as in a crontab file;
19957c478bd9Sstevel@tonic-gate 	 * for example: "40, 20, 50-10"
19967c478bd9Sstevel@tonic-gate 	 * next_ge returns the next number in the list that is
19977c478bd9Sstevel@tonic-gate 	 * greater than  or equal to current. if no numbers of list
19987c478bd9Sstevel@tonic-gate 	 * are >= current, the smallest element of list is returned.
19997c478bd9Sstevel@tonic-gate 	 * NOTE: current must be in the appropriate range.
20007c478bd9Sstevel@tonic-gate 	 */
20017c478bd9Sstevel@tonic-gate 
20027c478bd9Sstevel@tonic-gate 	char *ptr;
20037c478bd9Sstevel@tonic-gate 	int n, n2, min, min_gt;
20047c478bd9Sstevel@tonic-gate 
20057c478bd9Sstevel@tonic-gate 	if (strcmp(list, "*") == 0)
20067c478bd9Sstevel@tonic-gate 		return (current);
20077c478bd9Sstevel@tonic-gate 	ptr = list;
20087c478bd9Sstevel@tonic-gate 	min = DUMMY;
20097c478bd9Sstevel@tonic-gate 	min_gt = DUMMY;
20107c478bd9Sstevel@tonic-gate 	for (;;) {
20117c478bd9Sstevel@tonic-gate 		if ((n = (int)num(&ptr)) == current)
20127c478bd9Sstevel@tonic-gate 			return (current);
20137c478bd9Sstevel@tonic-gate 		if (n < min)
20147c478bd9Sstevel@tonic-gate 			min = n;
20157c478bd9Sstevel@tonic-gate 		if ((n > current) && (n < min_gt))
20167c478bd9Sstevel@tonic-gate 			min_gt = n;
20177c478bd9Sstevel@tonic-gate 		if (*ptr == '-') {
20187c478bd9Sstevel@tonic-gate 			ptr++;
20197c478bd9Sstevel@tonic-gate 			if ((n2 = (int)num(&ptr)) > n) {
20207c478bd9Sstevel@tonic-gate 				if ((current > n) && (current <= n2))
20217c478bd9Sstevel@tonic-gate 					return (current);
20227c478bd9Sstevel@tonic-gate 			} else {	/* range that wraps around */
20237c478bd9Sstevel@tonic-gate 				if (current > n)
20247c478bd9Sstevel@tonic-gate 					return (current);
20257c478bd9Sstevel@tonic-gate 				if (current <= n2)
20267c478bd9Sstevel@tonic-gate 					return (current);
20277c478bd9Sstevel@tonic-gate 			}
20287c478bd9Sstevel@tonic-gate 		}
20297c478bd9Sstevel@tonic-gate 		if (*ptr == '\0')
20307c478bd9Sstevel@tonic-gate 			break;
20317c478bd9Sstevel@tonic-gate 		ptr += 1;
20327c478bd9Sstevel@tonic-gate 	}
20337c478bd9Sstevel@tonic-gate 	if (min_gt != DUMMY)
20347c478bd9Sstevel@tonic-gate 		return (min_gt);
20357c478bd9Sstevel@tonic-gate 	else
20367c478bd9Sstevel@tonic-gate 		return (min);
20377c478bd9Sstevel@tonic-gate }
20387c478bd9Sstevel@tonic-gate 
20397c478bd9Sstevel@tonic-gate static void
20407c478bd9Sstevel@tonic-gate free_if_unused(struct usr *u)
20417c478bd9Sstevel@tonic-gate {
20427c478bd9Sstevel@tonic-gate 	struct usr *cur, *prev;
20437c478bd9Sstevel@tonic-gate 	/*
20447c478bd9Sstevel@tonic-gate 	 *	To make sure a usr structure is idle we must check that
20457c478bd9Sstevel@tonic-gate 	 *	there are no at jobs queued for the user; the user does
20467c478bd9Sstevel@tonic-gate 	 *	not have a crontab, and also that there are no running at
20477c478bd9Sstevel@tonic-gate 	 *	or cron jobs (since the runinfo structure also has a
20487c478bd9Sstevel@tonic-gate 	 *	pointer to the usr structure).
20497c478bd9Sstevel@tonic-gate 	 */
20507c478bd9Sstevel@tonic-gate 	if (!u->ctexists && u->atevents == NULL &&
20517c478bd9Sstevel@tonic-gate 	    u->cruncnt == 0 && u->aruncnt == 0) {
20527c478bd9Sstevel@tonic-gate #ifdef DEBUG
20537c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "%s removed from usr list\n", u->name);
20547c478bd9Sstevel@tonic-gate #endif
20557c478bd9Sstevel@tonic-gate 		for (cur = uhead, prev = NULL;
20564c4c9110Sbasabi 		    cur != u;
20574c4c9110Sbasabi 		    prev = cur, cur = cur->nextusr) {
20587c478bd9Sstevel@tonic-gate 			if (cur == NULL) {
20597c478bd9Sstevel@tonic-gate 				return;
20607c478bd9Sstevel@tonic-gate 			}
20617c478bd9Sstevel@tonic-gate 		}
20627c478bd9Sstevel@tonic-gate 
20637c478bd9Sstevel@tonic-gate 		if (prev == NULL)
20647c478bd9Sstevel@tonic-gate 			uhead = u->nextusr;
20657c478bd9Sstevel@tonic-gate 		else
20667c478bd9Sstevel@tonic-gate 			prev->nextusr = u->nextusr;
20677c478bd9Sstevel@tonic-gate 		free(u->name);
20687c478bd9Sstevel@tonic-gate 		free(u->home);
20697c478bd9Sstevel@tonic-gate 		free(u);
20707c478bd9Sstevel@tonic-gate 	}
20717c478bd9Sstevel@tonic-gate }
20727c478bd9Sstevel@tonic-gate 
20737c478bd9Sstevel@tonic-gate static void
20747c478bd9Sstevel@tonic-gate del_atjob(char *name, char *usrname)
20757c478bd9Sstevel@tonic-gate {
20767c478bd9Sstevel@tonic-gate 
20777c478bd9Sstevel@tonic-gate 	struct	event	*e, *eprev;
20787c478bd9Sstevel@tonic-gate 	struct	usr	*u;
20797c478bd9Sstevel@tonic-gate 
20807c478bd9Sstevel@tonic-gate 	if ((u = find_usr(usrname)) == NULL)
20817c478bd9Sstevel@tonic-gate 		return;
20827c478bd9Sstevel@tonic-gate 	e = u->atevents;
20837c478bd9Sstevel@tonic-gate 	eprev = NULL;
20847c478bd9Sstevel@tonic-gate 	while (e != NULL) {
20857c478bd9Sstevel@tonic-gate 		if (strcmp(name, e->cmd) == 0) {
20867c478bd9Sstevel@tonic-gate 			if (next_event == e)
20877c478bd9Sstevel@tonic-gate 				next_event = NULL;
20887c478bd9Sstevel@tonic-gate 			if (eprev == NULL)
20897c478bd9Sstevel@tonic-gate 				u->atevents = e->link;
20907c478bd9Sstevel@tonic-gate 			else
20917c478bd9Sstevel@tonic-gate 				eprev->link = e->link;
20927c478bd9Sstevel@tonic-gate 			el_remove(e->of.at.eventid, 1);
20937c478bd9Sstevel@tonic-gate 			free(e->cmd);
20947c478bd9Sstevel@tonic-gate 			free(e);
20957c478bd9Sstevel@tonic-gate 			break;
20967c478bd9Sstevel@tonic-gate 		} else {
20977c478bd9Sstevel@tonic-gate 			eprev = e;
20987c478bd9Sstevel@tonic-gate 			e = e->link;
20997c478bd9Sstevel@tonic-gate 		}
21007c478bd9Sstevel@tonic-gate 	}
21017c478bd9Sstevel@tonic-gate 
21027c478bd9Sstevel@tonic-gate 	free_if_unused(u);
21037c478bd9Sstevel@tonic-gate }
21047c478bd9Sstevel@tonic-gate 
21057c478bd9Sstevel@tonic-gate static void
21067c478bd9Sstevel@tonic-gate del_ctab(char *name)
21077c478bd9Sstevel@tonic-gate {
21087c478bd9Sstevel@tonic-gate 
21097c478bd9Sstevel@tonic-gate 	struct	usr *u;
21107c478bd9Sstevel@tonic-gate 
21117c478bd9Sstevel@tonic-gate 	if ((u = find_usr(name)) == NULL)
21127c478bd9Sstevel@tonic-gate 		return;
21137c478bd9Sstevel@tonic-gate 	rm_ctevents(u);
21147c478bd9Sstevel@tonic-gate 	el_remove(u->ctid, 0);
21157c478bd9Sstevel@tonic-gate 	u->ctid = 0;
21167c478bd9Sstevel@tonic-gate 	u->ctexists = 0;
21177c478bd9Sstevel@tonic-gate 
21187c478bd9Sstevel@tonic-gate 	free_if_unused(u);
21197c478bd9Sstevel@tonic-gate }
21207c478bd9Sstevel@tonic-gate 
21217c478bd9Sstevel@tonic-gate static void
21227c478bd9Sstevel@tonic-gate rm_ctevents(struct usr *u)
21237c478bd9Sstevel@tonic-gate {
21247c478bd9Sstevel@tonic-gate 	struct event *e2, *e3;
21257c478bd9Sstevel@tonic-gate 
21267c478bd9Sstevel@tonic-gate 	/*
21277c478bd9Sstevel@tonic-gate 	 * see if the next event (to be run by cron) is a cronevent
21287c478bd9Sstevel@tonic-gate 	 * owned by this user.
21297c478bd9Sstevel@tonic-gate 	 */
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate 	if ((next_event != NULL) &&
21327c478bd9Sstevel@tonic-gate 	    (next_event->etype == CRONEVENT) &&
21337c478bd9Sstevel@tonic-gate 	    (next_event->u == u)) {
21347c478bd9Sstevel@tonic-gate 		next_event = NULL;
21357c478bd9Sstevel@tonic-gate 	}
21367c478bd9Sstevel@tonic-gate 	e2 = u->ctevents;
21377c478bd9Sstevel@tonic-gate 	while (e2 != NULL) {
21387c478bd9Sstevel@tonic-gate 		free(e2->cmd);
2139*5b08e637SChris Gerhard 		rel_shared(e2->of.ct.tz);
2140*5b08e637SChris Gerhard 		rel_shared(e2->of.ct.shell);
2141*5b08e637SChris Gerhard 		rel_shared(e2->of.ct.home);
21427c478bd9Sstevel@tonic-gate 		free(e2->of.ct.minute);
21437c478bd9Sstevel@tonic-gate 		free(e2->of.ct.hour);
21447c478bd9Sstevel@tonic-gate 		free(e2->of.ct.daymon);
21457c478bd9Sstevel@tonic-gate 		free(e2->of.ct.month);
21467c478bd9Sstevel@tonic-gate 		free(e2->of.ct.dayweek);
21477c478bd9Sstevel@tonic-gate 		if (e2->of.ct.input != NULL)
21487c478bd9Sstevel@tonic-gate 			free(e2->of.ct.input);
21497c478bd9Sstevel@tonic-gate 		e3 = e2->link;
21507c478bd9Sstevel@tonic-gate 		free(e2);
21517c478bd9Sstevel@tonic-gate 		e2 = e3;
21527c478bd9Sstevel@tonic-gate 	}
21537c478bd9Sstevel@tonic-gate 	u->ctevents = NULL;
21547c478bd9Sstevel@tonic-gate }
21557c478bd9Sstevel@tonic-gate 
21567c478bd9Sstevel@tonic-gate 
21577c478bd9Sstevel@tonic-gate static struct usr *
21587c478bd9Sstevel@tonic-gate find_usr(char *uname)
21597c478bd9Sstevel@tonic-gate {
21607c478bd9Sstevel@tonic-gate 	struct usr *u;
21617c478bd9Sstevel@tonic-gate 
21627c478bd9Sstevel@tonic-gate 	u = uhead;
21637c478bd9Sstevel@tonic-gate 	while (u != NULL) {
21647c478bd9Sstevel@tonic-gate 		if (strcmp(u->name, uname) == 0)
21657c478bd9Sstevel@tonic-gate 			return (u);
21667c478bd9Sstevel@tonic-gate 		u = u->nextusr;
21677c478bd9Sstevel@tonic-gate 	}
21687c478bd9Sstevel@tonic-gate 	return (NULL);
21697c478bd9Sstevel@tonic-gate }
21707c478bd9Sstevel@tonic-gate 
21717c478bd9Sstevel@tonic-gate /*
21727c478bd9Sstevel@tonic-gate  * Execute cron command or at/batch job.
21737c478bd9Sstevel@tonic-gate  * If ever a premature return is added to this function pay attention to
21747c478bd9Sstevel@tonic-gate  * free at_cmdfile and outfile plus jobname buffers of the runinfo structure.
21757c478bd9Sstevel@tonic-gate  */
21767c478bd9Sstevel@tonic-gate static int
21777c478bd9Sstevel@tonic-gate ex(struct event *e)
21787c478bd9Sstevel@tonic-gate {
21797c478bd9Sstevel@tonic-gate 	int r;
21807c478bd9Sstevel@tonic-gate 	int fd;
21817c478bd9Sstevel@tonic-gate 	pid_t rfork;
21827c478bd9Sstevel@tonic-gate 	FILE *atcmdfp;
21837c478bd9Sstevel@tonic-gate 	char mailvar[4];
21847c478bd9Sstevel@tonic-gate 	char *at_cmdfile = NULL;
21857c478bd9Sstevel@tonic-gate 	struct stat buf;
21867c478bd9Sstevel@tonic-gate 	struct queue *qp;
21877c478bd9Sstevel@tonic-gate 	struct runinfo *rp;
21887c478bd9Sstevel@tonic-gate 	struct project proj, *pproj = NULL;
2189*5b08e637SChris Gerhard 	union {
2190*5b08e637SChris Gerhard 		struct {
2191*5b08e637SChris Gerhard 			char buf[PROJECT_BUFSZ];
2192*5b08e637SChris Gerhard 			char buf2[PROJECT_BUFSZ];
2193*5b08e637SChris Gerhard 		} p;
2194*5b08e637SChris Gerhard 		char error[CANT_STR_LEN + PATH_MAX];
2195*5b08e637SChris Gerhard 	} bufs;
21967c478bd9Sstevel@tonic-gate 	char *tmpfile;
21977c478bd9Sstevel@tonic-gate 	FILE *fptr;
21987c478bd9Sstevel@tonic-gate 	time_t dhltime;
21997c478bd9Sstevel@tonic-gate 	projid_t projid;
22007c478bd9Sstevel@tonic-gate 	int projflag = 0;
2201*5b08e637SChris Gerhard 	char *home;
2202*5b08e637SChris Gerhard 	char *sh;
22037c478bd9Sstevel@tonic-gate 
22047c478bd9Sstevel@tonic-gate 	qp = &qt[e->etype];	/* set pointer to queue defs */
22057c478bd9Sstevel@tonic-gate 	if (qp->nrun >= qp->njob) {
22064c4c9110Sbasabi 		msg("%c queue max run limit reached", e->etype + 'a');
22077c478bd9Sstevel@tonic-gate 		resched(qp->nwait);
22087c478bd9Sstevel@tonic-gate 		return (0);
22097c478bd9Sstevel@tonic-gate 	}
22109f163834Sbasabi 	rp = rinfo_get(0); /* allocating a new runinfo struct */
22117c478bd9Sstevel@tonic-gate 
22127c478bd9Sstevel@tonic-gate 
22137c478bd9Sstevel@tonic-gate 	/*
22147c478bd9Sstevel@tonic-gate 	 * the tempnam() function uses malloc(3C) to allocate space for the
22157c478bd9Sstevel@tonic-gate 	 * constructed file name, and returns a pointer to this area, which
22167c478bd9Sstevel@tonic-gate 	 * is assigned to rp->outfile. Here rp->outfile is not overwritten.
22177c478bd9Sstevel@tonic-gate 	 */
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate 	rp->outfile = tempnam(TMPDIR, PFX);
22207c478bd9Sstevel@tonic-gate 	rp->jobtype = e->etype;
22217c478bd9Sstevel@tonic-gate 	if (e->etype == CRONEVENT) {
22224c4c9110Sbasabi 		rp->jobname = xmalloc(strlen(e->cmd) + 1);
22237c478bd9Sstevel@tonic-gate 		(void) strcpy(rp->jobname, e->cmd);
22247c478bd9Sstevel@tonic-gate 		/* "cron" jobs only produce mail if there's output */
22257c478bd9Sstevel@tonic-gate 		rp->mailwhendone = 0;
22267c478bd9Sstevel@tonic-gate 	} else {
22274c4c9110Sbasabi 		at_cmdfile = xmalloc(strlen(ATDIR) + strlen(e->cmd) + 2);
22287c478bd9Sstevel@tonic-gate 		(void) sprintf(at_cmdfile, "%s/%s", ATDIR, e->cmd);
22297c478bd9Sstevel@tonic-gate 		if ((atcmdfp = fopen(at_cmdfile, "r")) == NULL) {
22307c478bd9Sstevel@tonic-gate 			if (errno == ENAMETOOLONG) {
22317c478bd9Sstevel@tonic-gate 				if (chdir(ATDIR) == 0)
22327c478bd9Sstevel@tonic-gate 					cron_unlink(e->cmd);
22337c478bd9Sstevel@tonic-gate 			} else {
22347c478bd9Sstevel@tonic-gate 				cron_unlink(at_cmdfile);
22357c478bd9Sstevel@tonic-gate 			}
22367c478bd9Sstevel@tonic-gate 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECAT);
22377c478bd9Sstevel@tonic-gate 			free(at_cmdfile);
22387c478bd9Sstevel@tonic-gate 			rinfo_free(rp);
22397c478bd9Sstevel@tonic-gate 			return (0);
22407c478bd9Sstevel@tonic-gate 		}
22414c4c9110Sbasabi 		rp->jobname = xmalloc(strlen(at_cmdfile) + 1);
22427c478bd9Sstevel@tonic-gate 		(void) strcpy(rp->jobname, at_cmdfile);
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate 		/*
22457c478bd9Sstevel@tonic-gate 		 * Skip over the first two lines.
22467c478bd9Sstevel@tonic-gate 		 */
22477c478bd9Sstevel@tonic-gate 		(void) fscanf(atcmdfp, "%*[^\n]\n");
22487c478bd9Sstevel@tonic-gate 		(void) fscanf(atcmdfp, "%*[^\n]\n");
22497c478bd9Sstevel@tonic-gate 		if (fscanf(atcmdfp, ": notify by mail: %3s%*[^\n]\n",
22504c4c9110Sbasabi 		    mailvar) == 1) {
22517c478bd9Sstevel@tonic-gate 			/*
22527c478bd9Sstevel@tonic-gate 			 * Check to see if we should always send mail
22537c478bd9Sstevel@tonic-gate 			 * to the owner.
22547c478bd9Sstevel@tonic-gate 			 */
22557c478bd9Sstevel@tonic-gate 			rp->mailwhendone = (strcmp(mailvar, "yes") == 0);
22567c478bd9Sstevel@tonic-gate 		} else {
22577c478bd9Sstevel@tonic-gate 			rp->mailwhendone = 0;
22587c478bd9Sstevel@tonic-gate 		}
22597c478bd9Sstevel@tonic-gate 
22607c478bd9Sstevel@tonic-gate 		if (fscanf(atcmdfp, "\n: project: %d\n", &projid) == 1) {
22617c478bd9Sstevel@tonic-gate 			projflag = 1;
22627c478bd9Sstevel@tonic-gate 		}
22637c478bd9Sstevel@tonic-gate 		(void) fclose(atcmdfp);
22647c478bd9Sstevel@tonic-gate 	}
22657c478bd9Sstevel@tonic-gate 
22667c478bd9Sstevel@tonic-gate 	/*
22677c478bd9Sstevel@tonic-gate 	 * we make sure that the system time
22687c478bd9Sstevel@tonic-gate 	 * hasn't drifted backwards. if it has, el_add() is now
22697c478bd9Sstevel@tonic-gate 	 * called, to make sure that the event queue is back in order,
22707c478bd9Sstevel@tonic-gate 	 * and we set the delayed flag. cron will pick up the request
22717c478bd9Sstevel@tonic-gate 	 * later on at the proper time.
22727c478bd9Sstevel@tonic-gate 	 */
22737c478bd9Sstevel@tonic-gate 	dhltime = time(NULL);
22747c478bd9Sstevel@tonic-gate 	if ((dhltime - e->time) < 0) {
22757c478bd9Sstevel@tonic-gate 		msg("clock time drifted backwards!\n");
22767c478bd9Sstevel@tonic-gate 		if (next_event->etype == CRONEVENT) {
22777c478bd9Sstevel@tonic-gate 			msg("correcting cron event\n");
22787c478bd9Sstevel@tonic-gate 			next_event->time = next_time(next_event, dhltime);
2279e2553d68SSerge Dussud 			switch (el_add(next_event, next_event->time,
2280e2553d68SSerge Dussud 			    (next_event->u)->ctid)) {
2281e2553d68SSerge Dussud 			case -1:
2282e2553d68SSerge Dussud 				ignore_msg("ex", "cron", next_event);
2283e2553d68SSerge Dussud 				break;
2284e2553d68SSerge Dussud 			case -2: /* event time lower than init time */
2285e2553d68SSerge Dussud 				reset_needed = 1;
2286e2553d68SSerge Dussud 				break;
2287e2553d68SSerge Dussud 			}
22887c478bd9Sstevel@tonic-gate 		} else { /* etype == ATEVENT */
22897c478bd9Sstevel@tonic-gate 			msg("correcting batch event\n");
2290e2553d68SSerge Dussud 			if (el_add(next_event, next_event->time,
2291e2553d68SSerge Dussud 			    next_event->of.at.eventid) < 0) {
2292e2553d68SSerge Dussud 				ignore_msg("ex", "at", next_event);
2293e2553d68SSerge Dussud 			}
22947c478bd9Sstevel@tonic-gate 		}
22957c478bd9Sstevel@tonic-gate 		delayed++;
22967c478bd9Sstevel@tonic-gate 		t_old = time(NULL);
22977c478bd9Sstevel@tonic-gate 		free(at_cmdfile);
22987c478bd9Sstevel@tonic-gate 		rinfo_free(rp);
22997c478bd9Sstevel@tonic-gate 		return (0);
23007c478bd9Sstevel@tonic-gate 	}
23017c478bd9Sstevel@tonic-gate 
23027c478bd9Sstevel@tonic-gate 	if ((rfork = fork()) == (pid_t)-1) {
23037c478bd9Sstevel@tonic-gate 		reap_child();
23047c478bd9Sstevel@tonic-gate 		if ((rfork = fork()) == (pid_t)-1) {
23057c478bd9Sstevel@tonic-gate 			msg("cannot fork");
23067c478bd9Sstevel@tonic-gate 			free(at_cmdfile);
23077c478bd9Sstevel@tonic-gate 			rinfo_free(rp);
23087c478bd9Sstevel@tonic-gate 			resched(60);
23097c478bd9Sstevel@tonic-gate 			(void) sleep(30);
23107c478bd9Sstevel@tonic-gate 			return (0);
23117c478bd9Sstevel@tonic-gate 		}
23127c478bd9Sstevel@tonic-gate 	}
23137c478bd9Sstevel@tonic-gate 	if (rfork) {		/* parent process */
23147c478bd9Sstevel@tonic-gate 		contract_abandon_latest(rfork);
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate 		++qp->nrun;
23177c478bd9Sstevel@tonic-gate 		rp->pid = rfork;
23187c478bd9Sstevel@tonic-gate 		rp->que = e->etype;
23197c478bd9Sstevel@tonic-gate 		if (e->etype != CRONEVENT)
23207c478bd9Sstevel@tonic-gate 			(e->u)->aruncnt++;
23217c478bd9Sstevel@tonic-gate 		else
23227c478bd9Sstevel@tonic-gate 			(e->u)->cruncnt++;
23237c478bd9Sstevel@tonic-gate 		rp->rusr = (e->u);
23247c478bd9Sstevel@tonic-gate 		logit(BCHAR, rp, 0);
23257c478bd9Sstevel@tonic-gate 		free(at_cmdfile);
23267c478bd9Sstevel@tonic-gate 
23277c478bd9Sstevel@tonic-gate 		return (0);
23287c478bd9Sstevel@tonic-gate 	}
23297c478bd9Sstevel@tonic-gate 
23307c478bd9Sstevel@tonic-gate 	child_sigreset();
23317c478bd9Sstevel@tonic-gate 	contract_clear_template();
23327c478bd9Sstevel@tonic-gate 
23337c478bd9Sstevel@tonic-gate 	if (e->etype != CRONEVENT) {
23347c478bd9Sstevel@tonic-gate 		/* open jobfile as stdin to shell */
23357c478bd9Sstevel@tonic-gate 		if (stat(at_cmdfile, &buf)) {
23367c478bd9Sstevel@tonic-gate 			if (errno == ENAMETOOLONG) {
23377c478bd9Sstevel@tonic-gate 				if (chdir(ATDIR) == 0)
23387c478bd9Sstevel@tonic-gate 					cron_unlink(e->cmd);
23397c478bd9Sstevel@tonic-gate 			} else
23407c478bd9Sstevel@tonic-gate 				cron_unlink(at_cmdfile);
23417c478bd9Sstevel@tonic-gate 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
23427c478bd9Sstevel@tonic-gate 			exit(1);
23437c478bd9Sstevel@tonic-gate 		}
23447c478bd9Sstevel@tonic-gate 		if (!(buf.st_mode&ISUID)) {
23457c478bd9Sstevel@tonic-gate 			/*
23467c478bd9Sstevel@tonic-gate 			 * if setuid bit off, original owner has
23477c478bd9Sstevel@tonic-gate 			 * given this file to someone else
23487c478bd9Sstevel@tonic-gate 			 */
23497c478bd9Sstevel@tonic-gate 			cron_unlink(at_cmdfile);
23507c478bd9Sstevel@tonic-gate 			exit(1);
23517c478bd9Sstevel@tonic-gate 		}
23527c478bd9Sstevel@tonic-gate 		if ((fd = open(at_cmdfile, O_RDONLY)) == -1) {
23537c478bd9Sstevel@tonic-gate 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
23547c478bd9Sstevel@tonic-gate 			cron_unlink(at_cmdfile);
23557c478bd9Sstevel@tonic-gate 			exit(1);
23567c478bd9Sstevel@tonic-gate 		}
23577c478bd9Sstevel@tonic-gate 		if (fd != 0) {
23587c478bd9Sstevel@tonic-gate 			(void) dup2(fd, 0);
23597c478bd9Sstevel@tonic-gate 			(void) close(fd);
23607c478bd9Sstevel@tonic-gate 		}
23617c478bd9Sstevel@tonic-gate 		/*
23627c478bd9Sstevel@tonic-gate 		 * retrieve the project id of the at job and convert it
23637c478bd9Sstevel@tonic-gate 		 * to a project name.  fail if it's not a valid project
23647c478bd9Sstevel@tonic-gate 		 * or if the user isn't a member of the project.
23657c478bd9Sstevel@tonic-gate 		 */
23667c478bd9Sstevel@tonic-gate 		if (projflag == 1) {
23677c478bd9Sstevel@tonic-gate 			if ((pproj = getprojbyid(projid, &proj,
2368*5b08e637SChris Gerhard 			    (void *)&bufs.p.buf,
2369*5b08e637SChris Gerhard 			    sizeof (bufs.p.buf))) == NULL ||
23707c478bd9Sstevel@tonic-gate 			    !inproj(e->u->name, pproj->pj_name,
2371*5b08e637SChris Gerhard 			    bufs.p.buf2, sizeof (bufs.p.buf2))) {
23727c478bd9Sstevel@tonic-gate 				cron_unlink(at_cmdfile);
23737c478bd9Sstevel@tonic-gate 				mail((e->u)->name, BADPROJID, ERR_CANTEXECAT);
23747c478bd9Sstevel@tonic-gate 				exit(1);
23757c478bd9Sstevel@tonic-gate 			}
23767c478bd9Sstevel@tonic-gate 		}
23777c478bd9Sstevel@tonic-gate 	}
23787c478bd9Sstevel@tonic-gate 
23797c478bd9Sstevel@tonic-gate 	/*
23807c478bd9Sstevel@tonic-gate 	 * Put process in a new session, and create a new task.
23817c478bd9Sstevel@tonic-gate 	 */
23827c478bd9Sstevel@tonic-gate 	if (setsid() < 0) {
23837c478bd9Sstevel@tonic-gate 		msg("setsid failed with errno = %d. job failed (%s)"
23847c478bd9Sstevel@tonic-gate 		    " for user %s", errno, e->cmd, e->u->name);
23857c478bd9Sstevel@tonic-gate 		if (e->etype != CRONEVENT)
23867c478bd9Sstevel@tonic-gate 			cron_unlink(at_cmdfile);
23877c478bd9Sstevel@tonic-gate 		exit(1);
23887c478bd9Sstevel@tonic-gate 	}
23897c478bd9Sstevel@tonic-gate 
23907c478bd9Sstevel@tonic-gate 	/*
23917c478bd9Sstevel@tonic-gate 	 * set correct user identification and check his account
23927c478bd9Sstevel@tonic-gate 	 */
23937c478bd9Sstevel@tonic-gate 	r = set_user_cred(e->u, pproj);
23947c478bd9Sstevel@tonic-gate 	if (r == VUC_EXPIRED) {
23957c478bd9Sstevel@tonic-gate 		msg("user (%s) account is expired", e->u->name);
23967c478bd9Sstevel@tonic-gate 		audit_cron_user_acct_expired(e->u->name);
23977c478bd9Sstevel@tonic-gate 		clean_out_user(e->u);
23987c478bd9Sstevel@tonic-gate 		exit(1);
23997c478bd9Sstevel@tonic-gate 	}
24007c478bd9Sstevel@tonic-gate 	if (r == VUC_NEW_AUTH) {
24017c478bd9Sstevel@tonic-gate 		msg("user (%s) password has expired", e->u->name);
24027c478bd9Sstevel@tonic-gate 		audit_cron_user_acct_expired(e->u->name);
24037c478bd9Sstevel@tonic-gate 		clean_out_user(e->u);
24047c478bd9Sstevel@tonic-gate 		exit(1);
24057c478bd9Sstevel@tonic-gate 	}
24067c478bd9Sstevel@tonic-gate 	if (r != VUC_OK) {
24077c478bd9Sstevel@tonic-gate 		msg("bad user (%s)", e->u->name);
24087c478bd9Sstevel@tonic-gate 		audit_cron_bad_user(e->u->name);
24097c478bd9Sstevel@tonic-gate 		clean_out_user(e->u);
24107c478bd9Sstevel@tonic-gate 		exit(1);
24117c478bd9Sstevel@tonic-gate 	}
24127c478bd9Sstevel@tonic-gate 	/*
24137c478bd9Sstevel@tonic-gate 	 * check user and initialize the supplementary group access list.
24147c478bd9Sstevel@tonic-gate 	 * bugid 1230784: deleted from parent to avoid cron hang. Now
24157c478bd9Sstevel@tonic-gate 	 * only child handles the call.
24167c478bd9Sstevel@tonic-gate 	 */
24177c478bd9Sstevel@tonic-gate 
24187c478bd9Sstevel@tonic-gate 	if (verify_user_cred(e->u) != VUC_OK ||
24197c478bd9Sstevel@tonic-gate 	    setgid(e->u->gid) == -1 ||
24207c478bd9Sstevel@tonic-gate 	    initgroups(e->u->name, e->u->gid) == -1) {
24217c478bd9Sstevel@tonic-gate 		msg("bad user (%s) or setgid failed (%s)",
24224c4c9110Sbasabi 		    e->u->name, e->u->name);
24237c478bd9Sstevel@tonic-gate 		audit_cron_bad_user(e->u->name);
24247c478bd9Sstevel@tonic-gate 		clean_out_user(e->u);
24257c478bd9Sstevel@tonic-gate 		exit(1);
24267c478bd9Sstevel@tonic-gate 	}
24277c478bd9Sstevel@tonic-gate 
242863d5d94cSjc 	if ((e->u)->uid == 0) { /* set default path */
242963d5d94cSjc 		/* path settable in defaults file */
243063d5d94cSjc 		envinit[2] = supath;
243163d5d94cSjc 	} else {
243263d5d94cSjc 		envinit[2] = path;
243363d5d94cSjc 	}
243463d5d94cSjc 
24357c478bd9Sstevel@tonic-gate 	if (e->etype != CRONEVENT) {
24367c478bd9Sstevel@tonic-gate 		r = audit_cron_session(e->u->name, NULL,
24374c4c9110Sbasabi 		    e->u->uid, e->u->gid, at_cmdfile);
24387c478bd9Sstevel@tonic-gate 		cron_unlink(at_cmdfile);
24397c478bd9Sstevel@tonic-gate 	} else {
24407c478bd9Sstevel@tonic-gate 		r = audit_cron_session(e->u->name, CRONDIR,
24414c4c9110Sbasabi 		    e->u->uid, e->u->gid, NULL);
24427c478bd9Sstevel@tonic-gate 	}
24437c478bd9Sstevel@tonic-gate 	if (r != 0) {
24447c478bd9Sstevel@tonic-gate 		msg("cron audit problem. job failed (%s) for user %s",
24454c4c9110Sbasabi 		    e->cmd, e->u->name);
24467c478bd9Sstevel@tonic-gate 		exit(1);
24477c478bd9Sstevel@tonic-gate 	}
24487c478bd9Sstevel@tonic-gate 
24497c478bd9Sstevel@tonic-gate 	audit_cron_new_job(e->cmd, e->etype, (void *)e);
24507c478bd9Sstevel@tonic-gate 
24517c478bd9Sstevel@tonic-gate 	if (setuid(e->u->uid) == -1)  {
24527c478bd9Sstevel@tonic-gate 		msg("setuid failed (%s)", e->u->name);
24537c478bd9Sstevel@tonic-gate 		clean_out_user(e->u);
24547c478bd9Sstevel@tonic-gate 		exit(1);
24557c478bd9Sstevel@tonic-gate 	}
24567c478bd9Sstevel@tonic-gate 
24577c478bd9Sstevel@tonic-gate 	if (e->etype == CRONEVENT) {
24587c478bd9Sstevel@tonic-gate 		/* check for standard input to command	*/
24597c478bd9Sstevel@tonic-gate 		if (e->of.ct.input != NULL) {
24607c478bd9Sstevel@tonic-gate 			if ((tmpfile = strdup(TMPINFILE)) == NULL) {
24617c478bd9Sstevel@tonic-gate 				mail((e->u)->name, MALLOCERR,
24624c4c9110Sbasabi 				    ERR_CANTEXECCRON);
24637c478bd9Sstevel@tonic-gate 				exit(1);
24647c478bd9Sstevel@tonic-gate 			}
24657c478bd9Sstevel@tonic-gate 			if ((fd = mkstemp(tmpfile)) == -1 ||
24664c4c9110Sbasabi 			    (fptr = fdopen(fd, "w")) == NULL) {
24677c478bd9Sstevel@tonic-gate 				mail((e->u)->name, NOSTDIN,
24684c4c9110Sbasabi 				    ERR_CANTEXECCRON);
24697c478bd9Sstevel@tonic-gate 				cron_unlink(tmpfile);
24707c478bd9Sstevel@tonic-gate 				free(tmpfile);
24717c478bd9Sstevel@tonic-gate 				exit(1);
24727c478bd9Sstevel@tonic-gate 			}
24737c478bd9Sstevel@tonic-gate 			if ((fwrite(e->of.ct.input, sizeof (char),
24744c4c9110Sbasabi 			    strlen(e->of.ct.input), fptr)) !=
24754c4c9110Sbasabi 			    strlen(e->of.ct.input)) {
24767c478bd9Sstevel@tonic-gate 				mail((e->u)->name, NOSTDIN, ERR_CANTEXECCRON);
24777c478bd9Sstevel@tonic-gate 				cron_unlink(tmpfile);
24787c478bd9Sstevel@tonic-gate 				free(tmpfile);
24797c478bd9Sstevel@tonic-gate 				(void) close(fd);
24807c478bd9Sstevel@tonic-gate 				(void) fclose(fptr);
24817c478bd9Sstevel@tonic-gate 				exit(1);
24827c478bd9Sstevel@tonic-gate 			}
24837c478bd9Sstevel@tonic-gate 			if (fseek(fptr, (off_t)0, SEEK_SET) != -1) {
24847c478bd9Sstevel@tonic-gate 				if (fd != 0) {
24857c478bd9Sstevel@tonic-gate 					(void) dup2(fd, 0);
24867c478bd9Sstevel@tonic-gate 					(void) close(fd);
24877c478bd9Sstevel@tonic-gate 				}
24887c478bd9Sstevel@tonic-gate 			}
24897c478bd9Sstevel@tonic-gate 			cron_unlink(tmpfile);
24907c478bd9Sstevel@tonic-gate 			free(tmpfile);
24917c478bd9Sstevel@tonic-gate 			(void) fclose(fptr);
24927c478bd9Sstevel@tonic-gate 		} else if ((fd = open("/dev/null", O_RDONLY)) > 0) {
24937c478bd9Sstevel@tonic-gate 			(void) dup2(fd, 0);
24947c478bd9Sstevel@tonic-gate 			(void) close(fd);
24957c478bd9Sstevel@tonic-gate 		}
24967c478bd9Sstevel@tonic-gate 	}
24977c478bd9Sstevel@tonic-gate 
24987c478bd9Sstevel@tonic-gate 	/* redirect stdout and stderr for the shell	*/
24997c478bd9Sstevel@tonic-gate 	if ((fd = open(rp->outfile, O_WRONLY|O_CREAT|O_EXCL, OUTMODE)) == 1)
25007c478bd9Sstevel@tonic-gate 		fd = open("/dev/null", O_WRONLY);
25017c478bd9Sstevel@tonic-gate 
25027c478bd9Sstevel@tonic-gate 	if (fd >= 0 && fd != 1)
25037c478bd9Sstevel@tonic-gate 		(void) dup2(fd, 1);
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate 	if (fd >= 0 && fd != 2) {
25067c478bd9Sstevel@tonic-gate 		(void) dup2(fd, 2);
25077c478bd9Sstevel@tonic-gate 		if (fd != 1)
25087c478bd9Sstevel@tonic-gate 			(void) close(fd);
25097c478bd9Sstevel@tonic-gate 	}
25107c478bd9Sstevel@tonic-gate 
2511*5b08e637SChris Gerhard 	if (e->etype == CRONEVENT && e->of.ct.home != NULL) {
2512*5b08e637SChris Gerhard 		home = (char *)get_obj(e->of.ct.home);
2513*5b08e637SChris Gerhard 	} else {
2514*5b08e637SChris Gerhard 		home = (e->u)->home;
2515*5b08e637SChris Gerhard 	}
2516*5b08e637SChris Gerhard 	(void) strlcat(homedir, home, sizeof (homedir));
25177c478bd9Sstevel@tonic-gate 	(void) strlcat(logname, (e->u)->name, sizeof (logname));
25187c478bd9Sstevel@tonic-gate 	environ = envinit;
2519*5b08e637SChris Gerhard 	if (chdir(home) == -1) {
2520*5b08e637SChris Gerhard 		snprintf(bufs.error, sizeof (bufs.error), CANTCDHOME, home);
2521*5b08e637SChris Gerhard 		mail((e->u)->name, bufs.error,
25224c4c9110Sbasabi 		    e->etype == CRONEVENT ? ERR_CANTEXECCRON :
25234c4c9110Sbasabi 		    ERR_CANTEXECAT);
25247c478bd9Sstevel@tonic-gate 		exit(1);
25257c478bd9Sstevel@tonic-gate 	}
25267c478bd9Sstevel@tonic-gate #ifdef TESTING
25277c478bd9Sstevel@tonic-gate 	exit(1);
25287c478bd9Sstevel@tonic-gate #endif
25297c478bd9Sstevel@tonic-gate 	/*
25307c478bd9Sstevel@tonic-gate 	 * make sure that all file descriptors EXCEPT 0, 1 and 2
25317c478bd9Sstevel@tonic-gate 	 * will be closed.
25327c478bd9Sstevel@tonic-gate 	 */
25337c478bd9Sstevel@tonic-gate 	closefrom(3);
25347c478bd9Sstevel@tonic-gate 
25357c478bd9Sstevel@tonic-gate 	if ((e->u)->uid != 0)
25367c478bd9Sstevel@tonic-gate 		(void) nice(qp->nice);
2537*5b08e637SChris Gerhard 	if (e->etype == CRONEVENT) {
2538*5b08e637SChris Gerhard 		if (e->of.ct.tz) {
2539*5b08e637SChris Gerhard 			(void) putenv((char *)get_obj(e->of.ct.tz));
2540*5b08e637SChris Gerhard 		}
2541*5b08e637SChris Gerhard 		if (e->of.ct.shell) {
2542*5b08e637SChris Gerhard 			char *name;
2543*5b08e637SChris Gerhard 
2544*5b08e637SChris Gerhard 			sh = (char *)get_obj(e->of.ct.shell);
2545*5b08e637SChris Gerhard 			name = strrchr(sh, '/');
2546*5b08e637SChris Gerhard 			if (name == NULL)
2547*5b08e637SChris Gerhard 				name = sh;
2548*5b08e637SChris Gerhard 			else
2549*5b08e637SChris Gerhard 				name++;
2550*5b08e637SChris Gerhard 
2551*5b08e637SChris Gerhard 			(void) putenv(sh);
2552*5b08e637SChris Gerhard 			sh += strlen(ENV_SHELL);
2553*5b08e637SChris Gerhard 			(void) execl(sh, name, "-c", e->cmd, 0);
2554*5b08e637SChris Gerhard 		} else {
2555*5b08e637SChris Gerhard 			(void) execl(SHELL, "sh", "-c", e->cmd, 0);
2556*5b08e637SChris Gerhard 			sh = SHELL;
2557*5b08e637SChris Gerhard 		}
2558*5b08e637SChris Gerhard 	} else {		/* type == ATEVENT */
25597c478bd9Sstevel@tonic-gate 		(void) execl(SHELL, "sh", 0);
2560*5b08e637SChris Gerhard 		sh = SHELL;
2561*5b08e637SChris Gerhard 	}
2562*5b08e637SChris Gerhard 	snprintf(bufs.error, sizeof (bufs.error), CANTEXECSH, sh);
2563*5b08e637SChris Gerhard 	mail((e->u)->name, bufs.error,
25644c4c9110Sbasabi 	    e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT);
25657c478bd9Sstevel@tonic-gate 	exit(1);
25667c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
25677c478bd9Sstevel@tonic-gate }
25687c478bd9Sstevel@tonic-gate 
25697c478bd9Sstevel@tonic-gate static int
25707c478bd9Sstevel@tonic-gate idle(long t)
25717c478bd9Sstevel@tonic-gate {
25727c478bd9Sstevel@tonic-gate 	time_t	now;
25737c478bd9Sstevel@tonic-gate 
25747c478bd9Sstevel@tonic-gate 	while (t > 0L) {
25757c478bd9Sstevel@tonic-gate 
25767c478bd9Sstevel@tonic-gate 		if (msg_wait(t) != 0) {
25777c478bd9Sstevel@tonic-gate 			/* we need to run next job immediately */
25787c478bd9Sstevel@tonic-gate 			return (0);
25797c478bd9Sstevel@tonic-gate 		}
25807c478bd9Sstevel@tonic-gate 
25817c478bd9Sstevel@tonic-gate 		reap_child();
25827c478bd9Sstevel@tonic-gate 
25837c478bd9Sstevel@tonic-gate 		now = time(NULL);
25847c478bd9Sstevel@tonic-gate 		if (last_time > now) {
25857c478bd9Sstevel@tonic-gate 			/* clock has been reset */
25867c478bd9Sstevel@tonic-gate 			return (1);
25877c478bd9Sstevel@tonic-gate 		}
25887c478bd9Sstevel@tonic-gate 
25897c478bd9Sstevel@tonic-gate 		if (next_event == NULL && !el_empty()) {
25907c478bd9Sstevel@tonic-gate 			next_event = (struct event *)el_first();
25917c478bd9Sstevel@tonic-gate 		}
25927c478bd9Sstevel@tonic-gate 		if (next_event == NULL)
25937c478bd9Sstevel@tonic-gate 			t = INFINITY;
25947c478bd9Sstevel@tonic-gate 		else
25957c478bd9Sstevel@tonic-gate 			t = (long)next_event->time - now;
25967c478bd9Sstevel@tonic-gate 	}
25977c478bd9Sstevel@tonic-gate 	return (0);
25987c478bd9Sstevel@tonic-gate }
25997c478bd9Sstevel@tonic-gate 
26007c478bd9Sstevel@tonic-gate /*
26017c478bd9Sstevel@tonic-gate  * This used to be in the idle(), but moved to the separate function.
26027c478bd9Sstevel@tonic-gate  * This called from various place when cron needs to reap the
26037c478bd9Sstevel@tonic-gate  * child. It includes the situation that cron hit maxrun, and needs
26047c478bd9Sstevel@tonic-gate  * to reschedule the job.
26057c478bd9Sstevel@tonic-gate  */
26067c478bd9Sstevel@tonic-gate static void
26077c478bd9Sstevel@tonic-gate reap_child()
26087c478bd9Sstevel@tonic-gate {
26097c478bd9Sstevel@tonic-gate 	pid_t	pid;
26107c478bd9Sstevel@tonic-gate 	int	prc;
26117c478bd9Sstevel@tonic-gate 	struct	runinfo	*rp;
26127c478bd9Sstevel@tonic-gate 
26137c478bd9Sstevel@tonic-gate 	for (;;) {
26147c478bd9Sstevel@tonic-gate 		pid = waitpid((pid_t)-1, &prc, WNOHANG);
26157c478bd9Sstevel@tonic-gate 		if (pid <= 0)
26167c478bd9Sstevel@tonic-gate 			break;
26177c478bd9Sstevel@tonic-gate #ifdef DEBUG
26187c478bd9Sstevel@tonic-gate 		fprintf(stderr,
26194c4c9110Sbasabi 		    "wait returned %x for process %d\n", prc, pid);
26207c478bd9Sstevel@tonic-gate #endif
26217c478bd9Sstevel@tonic-gate 		if ((rp = rinfo_get(pid)) == NULL) {
26227c478bd9Sstevel@tonic-gate 			if (miscpid_delete(pid) == 0) {
26237c478bd9Sstevel@tonic-gate 				/* not found in anywhere */
26247c478bd9Sstevel@tonic-gate 				msg(PIDERR, pid);
26257c478bd9Sstevel@tonic-gate 			}
26267c478bd9Sstevel@tonic-gate 		} else if (rp->que == ZOMB) {
26277c478bd9Sstevel@tonic-gate 			(void) unlink(rp->outfile);
26287c478bd9Sstevel@tonic-gate 			rinfo_free(rp);
26297c478bd9Sstevel@tonic-gate 		} else {
26307c478bd9Sstevel@tonic-gate 			cleanup(rp, prc);
26317c478bd9Sstevel@tonic-gate 		}
26327c478bd9Sstevel@tonic-gate 	}
26337c478bd9Sstevel@tonic-gate }
26347c478bd9Sstevel@tonic-gate 
26357c478bd9Sstevel@tonic-gate static void
26367c478bd9Sstevel@tonic-gate cleanup(struct runinfo *pr, int rc)
26377c478bd9Sstevel@tonic-gate {
26387c478bd9Sstevel@tonic-gate 	int	nextfork = 1;
26397c478bd9Sstevel@tonic-gate 	struct	usr	*p;
26407c478bd9Sstevel@tonic-gate 	struct	stat	buf;
26417c478bd9Sstevel@tonic-gate 
26427c478bd9Sstevel@tonic-gate 	logit(ECHAR, pr, rc);
26437c478bd9Sstevel@tonic-gate 	--qt[pr->que].nrun;
26447c478bd9Sstevel@tonic-gate 	p = pr->rusr;
26457c478bd9Sstevel@tonic-gate 	if (pr->que != CRONEVENT)
26467c478bd9Sstevel@tonic-gate 		--p->aruncnt;
26477c478bd9Sstevel@tonic-gate 	else
26487c478bd9Sstevel@tonic-gate 		--p->cruncnt;
26497c478bd9Sstevel@tonic-gate 
26504bc0a2efScasper 	if (lstat(pr->outfile, &buf) == 0) {
26514bc0a2efScasper 		if (!S_ISLNK(buf.st_mode) &&
26527c478bd9Sstevel@tonic-gate 		    (buf.st_size > 0 || pr->mailwhendone)) {
26537c478bd9Sstevel@tonic-gate 			/* mail user stdout and stderr */
26547c478bd9Sstevel@tonic-gate 			for (;;) {
26557c478bd9Sstevel@tonic-gate 				if ((pr->pid = fork()) < 0) {
26567c478bd9Sstevel@tonic-gate 					/*
26577c478bd9Sstevel@tonic-gate 					 * if fork fails try forever in doubling
26587c478bd9Sstevel@tonic-gate 					 * retry times, up to 16 seconds
26597c478bd9Sstevel@tonic-gate 					 */
26607c478bd9Sstevel@tonic-gate 					(void) sleep(nextfork);
26617c478bd9Sstevel@tonic-gate 					if (nextfork < 16)
26627c478bd9Sstevel@tonic-gate 						nextfork += nextfork;
26637c478bd9Sstevel@tonic-gate 					continue;
26647c478bd9Sstevel@tonic-gate 				} else if (pr->pid == 0) {
26657c478bd9Sstevel@tonic-gate 					child_sigreset();
26667c478bd9Sstevel@tonic-gate 					contract_clear_template();
26677c478bd9Sstevel@tonic-gate 
26687c478bd9Sstevel@tonic-gate 					mail_result(p, pr, buf.st_size);
26697c478bd9Sstevel@tonic-gate 					/* NOTREACHED */
26707c478bd9Sstevel@tonic-gate 				} else {
26717c478bd9Sstevel@tonic-gate 					contract_abandon_latest(pr->pid);
26727c478bd9Sstevel@tonic-gate 					pr->que = ZOMB;
26737c478bd9Sstevel@tonic-gate 					break;
26747c478bd9Sstevel@tonic-gate 				}
26757c478bd9Sstevel@tonic-gate 			}
26767c478bd9Sstevel@tonic-gate 		} else {
26777c478bd9Sstevel@tonic-gate 			(void) unlink(pr->outfile);
26787c478bd9Sstevel@tonic-gate 			rinfo_free(pr);
26797c478bd9Sstevel@tonic-gate 		}
26807c478bd9Sstevel@tonic-gate 	} else {
26817c478bd9Sstevel@tonic-gate 		rinfo_free(pr);
26827c478bd9Sstevel@tonic-gate 	}
26837c478bd9Sstevel@tonic-gate 
26847c478bd9Sstevel@tonic-gate 	free_if_unused(p);
26857c478bd9Sstevel@tonic-gate }
26867c478bd9Sstevel@tonic-gate 
26877c478bd9Sstevel@tonic-gate /*
26887c478bd9Sstevel@tonic-gate  * Mail stdout and stderr of a job to user. Get uid for real user and become
26897c478bd9Sstevel@tonic-gate  * that person. We do this so that mail won't come from root since this
26907c478bd9Sstevel@tonic-gate  * could be a security hole. If failure, quit - don't send mail as root.
26917c478bd9Sstevel@tonic-gate  */
26927c478bd9Sstevel@tonic-gate static void
26937c478bd9Sstevel@tonic-gate mail_result(struct usr *p, struct runinfo *pr, size_t filesize)
26947c478bd9Sstevel@tonic-gate {
26957c478bd9Sstevel@tonic-gate 	struct	passwd	*ruser_ids;
26967c478bd9Sstevel@tonic-gate 	FILE	*mailpipe;
26977c478bd9Sstevel@tonic-gate 	FILE	*st;
26987c478bd9Sstevel@tonic-gate 	struct utsname	name;
26997c478bd9Sstevel@tonic-gate 	int	nbytes;
27007c478bd9Sstevel@tonic-gate 	char	iobuf[BUFSIZ];
27017c478bd9Sstevel@tonic-gate 	char	*cmd;
27027c478bd9Sstevel@tonic-gate 
27037c478bd9Sstevel@tonic-gate 	(void) uname(&name);
27047c478bd9Sstevel@tonic-gate 	if ((ruser_ids = getpwnam(p->name)) == NULL)
27057c478bd9Sstevel@tonic-gate 		exit(0);
27067c478bd9Sstevel@tonic-gate 	(void) setuid(ruser_ids->pw_uid);
27077c478bd9Sstevel@tonic-gate 
27084c4c9110Sbasabi 	cmd = xmalloc(strlen(MAIL) + strlen(p->name)+2);
27097c478bd9Sstevel@tonic-gate 	(void) sprintf(cmd, "%s %s", MAIL, p->name);
27107c478bd9Sstevel@tonic-gate 	mailpipe = popen(cmd, "w");
27117c478bd9Sstevel@tonic-gate 	free(cmd);
27127c478bd9Sstevel@tonic-gate 	if (mailpipe == NULL)
27137c478bd9Sstevel@tonic-gate 		exit(127);
27147c478bd9Sstevel@tonic-gate 	(void) fprintf(mailpipe, "To: %s\n", p->name);
27157c478bd9Sstevel@tonic-gate 	if (pr->jobtype == CRONEVENT) {
27167c478bd9Sstevel@tonic-gate 		(void) fprintf(mailpipe, CRONOUT);
27177c478bd9Sstevel@tonic-gate 		(void) fprintf(mailpipe, "Your \"cron\" job on %s\n",
27184c4c9110Sbasabi 		    name.nodename);
27197c478bd9Sstevel@tonic-gate 		if (pr->jobname != NULL) {
27207c478bd9Sstevel@tonic-gate 			(void) fprintf(mailpipe, "%s\n\n", pr->jobname);
27217c478bd9Sstevel@tonic-gate 		}
27227c478bd9Sstevel@tonic-gate 	} else {
27237c478bd9Sstevel@tonic-gate 		(void) fprintf(mailpipe, "Subject: Output from \"at\" job\n\n");
27247c478bd9Sstevel@tonic-gate 		(void) fprintf(mailpipe, "Your \"at\" job on %s\n",
27254c4c9110Sbasabi 		    name.nodename);
27267c478bd9Sstevel@tonic-gate 		if (pr->jobname != NULL) {
27277c478bd9Sstevel@tonic-gate 			(void) fprintf(mailpipe, "\"%s\"\n\n", pr->jobname);
27287c478bd9Sstevel@tonic-gate 		}
27297c478bd9Sstevel@tonic-gate 	}
27307c478bd9Sstevel@tonic-gate 	/* Tmp. file is fopen'ed w/ "r",  secure open */
27317c478bd9Sstevel@tonic-gate 	if (filesize > 0 &&
27327c478bd9Sstevel@tonic-gate 	    (st = fopen(pr->outfile, "r")) != NULL) {
27337c478bd9Sstevel@tonic-gate 		(void) fprintf(mailpipe,
27344c4c9110Sbasabi 		    "produced the following output:\n\n");
27357c478bd9Sstevel@tonic-gate 		while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0)
27367c478bd9Sstevel@tonic-gate 			(void) fwrite(iobuf, sizeof (char), nbytes, mailpipe);
27377c478bd9Sstevel@tonic-gate 		(void) fclose(st);
27387c478bd9Sstevel@tonic-gate 	} else {
27397c478bd9Sstevel@tonic-gate 		(void) fprintf(mailpipe, "completed.\n");
27407c478bd9Sstevel@tonic-gate 	}
27417c478bd9Sstevel@tonic-gate 	(void) pclose(mailpipe);
27427c478bd9Sstevel@tonic-gate 	exit(0);
27437c478bd9Sstevel@tonic-gate }
27447c478bd9Sstevel@tonic-gate 
27457c478bd9Sstevel@tonic-gate static int
27467c478bd9Sstevel@tonic-gate msg_wait(long tim)
27477c478bd9Sstevel@tonic-gate {
27487c478bd9Sstevel@tonic-gate 	struct	message	msg;
27497c478bd9Sstevel@tonic-gate 	int	cnt;
27507c478bd9Sstevel@tonic-gate 	time_t	reftime;
27517c478bd9Sstevel@tonic-gate 	struct	pollfd pfd[2];
27527c478bd9Sstevel@tonic-gate 	int64_t	tl;
27537c478bd9Sstevel@tonic-gate 	int	timeout;
27547c478bd9Sstevel@tonic-gate 	static int	pending_msg;
27557c478bd9Sstevel@tonic-gate 	static time_t	pending_reftime;
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate 	if (pending_msg) {
27587c478bd9Sstevel@tonic-gate 		process_msg(&msgbuf, pending_reftime);
27597c478bd9Sstevel@tonic-gate 		pending_msg = 0;
27607c478bd9Sstevel@tonic-gate 		return (0);
27617c478bd9Sstevel@tonic-gate 	}
27627c478bd9Sstevel@tonic-gate 
27637c478bd9Sstevel@tonic-gate 	/*
27647c478bd9Sstevel@tonic-gate 	 * We are opening the signal mask to receive SIGCLD. The notifypipe
27657c478bd9Sstevel@tonic-gate 	 * is used to avoid race condition between SIGCLD and poll system
27667c478bd9Sstevel@tonic-gate 	 * call.
27677c478bd9Sstevel@tonic-gate 	 * If SIGCLD is delivered in poll(), poll will be interrupted, and
27687c478bd9Sstevel@tonic-gate 	 * we will return to idle() to reap the dead children.
27697c478bd9Sstevel@tonic-gate 	 * If SIGCLD is delivered between sigprocmask() below and poll(),
27707c478bd9Sstevel@tonic-gate 	 * there is no way we can detect the SIGCLD because poll() won't
27717c478bd9Sstevel@tonic-gate 	 * be interrupted. In such case, the dead children can't be wait'ed
27727c478bd9Sstevel@tonic-gate 	 * until poll returns by timeout or a new job. To avoid this race
27737c478bd9Sstevel@tonic-gate 	 * condition, child_handler write to the notifypipe, so that
27747c478bd9Sstevel@tonic-gate 	 * poll() will be able to return with POLLIN which indicates that
27757c478bd9Sstevel@tonic-gate 	 * we have received SIGCLD.
27767c478bd9Sstevel@tonic-gate 	 *
27777c478bd9Sstevel@tonic-gate 	 * Since the notifypipe is used to just let poll return from
27787c478bd9Sstevel@tonic-gate 	 * system call, the data in the pipe won't be read. Therefore,
27797c478bd9Sstevel@tonic-gate 	 * any data in the pipe needs to be flushed before opening signal
27807c478bd9Sstevel@tonic-gate 	 * mask.
27817c478bd9Sstevel@tonic-gate 	 *
27827c478bd9Sstevel@tonic-gate 	 * Note that we can probably re-write this code with pselect()
27837c478bd9Sstevel@tonic-gate 	 * which can handle this situation easily.
27847c478bd9Sstevel@tonic-gate 	 */
27857c478bd9Sstevel@tonic-gate 	(void) ioctl(notifypipe[0], I_FLUSH, FLUSHW);
27867c478bd9Sstevel@tonic-gate 
27877c478bd9Sstevel@tonic-gate 	pfd[0].fd = msgfd;
27887c478bd9Sstevel@tonic-gate 	pfd[0].events = POLLIN;
27897c478bd9Sstevel@tonic-gate 	pfd[1].fd = notifypipe[1];
27907c478bd9Sstevel@tonic-gate 	pfd[1].events = POLLIN;
27917c478bd9Sstevel@tonic-gate 
27927c478bd9Sstevel@tonic-gate #ifdef CRON_MAXSLEEP
27937c478bd9Sstevel@tonic-gate 	/*
27947c478bd9Sstevel@tonic-gate 	 * CRON_MAXSLEEP can be defined to have cron periodically wake
27957c478bd9Sstevel@tonic-gate 	 * up, so that cron can detect a change of TOD and adjust the
27967c478bd9Sstevel@tonic-gate 	 * sleep time accordingly.
27977c478bd9Sstevel@tonic-gate 	 */
27987c478bd9Sstevel@tonic-gate 	tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim;
27997c478bd9Sstevel@tonic-gate #endif
28007c478bd9Sstevel@tonic-gate 	tl = (tim == INFINITY) ? -1ll : (int64_t)tim * 1000;
28017c478bd9Sstevel@tonic-gate 
28027c478bd9Sstevel@tonic-gate 	accept_sigcld = 1;
28037c478bd9Sstevel@tonic-gate 	(void) sigprocmask(SIG_UNBLOCK, &childmask, NULL);
28047c478bd9Sstevel@tonic-gate 	do {
28057c478bd9Sstevel@tonic-gate 		timeout = (tl > INT_MAX ? INT_MAX : (int)tl);
28067c478bd9Sstevel@tonic-gate 		tl -= timeout;
28077c478bd9Sstevel@tonic-gate 		cnt = poll(pfd, 2, timeout);
28087c478bd9Sstevel@tonic-gate 		if (cnt == -1 && errno != EINTR) {
28097c478bd9Sstevel@tonic-gate 			perror("! poll");
28107c478bd9Sstevel@tonic-gate 		}
28117c478bd9Sstevel@tonic-gate 	} while (tl > 0 && cnt == 0);
28127c478bd9Sstevel@tonic-gate 	(void) sigprocmask(SIG_BLOCK, &childmask, NULL);
28137c478bd9Sstevel@tonic-gate 	accept_sigcld = 0;
28147c478bd9Sstevel@tonic-gate 
28157c478bd9Sstevel@tonic-gate 	/*
28167c478bd9Sstevel@tonic-gate 	 * poll timeout or interrupted.
28177c478bd9Sstevel@tonic-gate 	 */
28187c478bd9Sstevel@tonic-gate 	if (cnt <= 0)
28197c478bd9Sstevel@tonic-gate 		return (0);
28207c478bd9Sstevel@tonic-gate 
28217c478bd9Sstevel@tonic-gate 	/*
28227c478bd9Sstevel@tonic-gate 	 * Not the timeout or new job, but a SIGCLD has been delivered.
28237c478bd9Sstevel@tonic-gate 	 */
28247c478bd9Sstevel@tonic-gate 	if ((pfd[0].revents & POLLIN) == 0)
28257c478bd9Sstevel@tonic-gate 		return (0);
28267c478bd9Sstevel@tonic-gate 
28277c478bd9Sstevel@tonic-gate 	errno = 0;
28287c478bd9Sstevel@tonic-gate 	if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) {
28297c478bd9Sstevel@tonic-gate 		if (cnt != -1 || errno != EAGAIN)
28307c478bd9Sstevel@tonic-gate 			perror("! read");
28317c478bd9Sstevel@tonic-gate 		return (0);
28327c478bd9Sstevel@tonic-gate 	}
28337c478bd9Sstevel@tonic-gate 	reftime = time(NULL);
28347c478bd9Sstevel@tonic-gate 	if (next_event != NULL && reftime >= next_event->time) {
28357c478bd9Sstevel@tonic-gate 		/*
28367c478bd9Sstevel@tonic-gate 		 * we need to run the job before reloading crontab.
28377c478bd9Sstevel@tonic-gate 		 */
28387c478bd9Sstevel@tonic-gate 		(void) memcpy(&msgbuf, &msg, sizeof (msg));
28397c478bd9Sstevel@tonic-gate 		pending_msg = 1;
28407c478bd9Sstevel@tonic-gate 		pending_reftime = reftime;
28417c478bd9Sstevel@tonic-gate 		return (1);
28427c478bd9Sstevel@tonic-gate 	}
28437c478bd9Sstevel@tonic-gate 	process_msg(&msg, reftime);
28447c478bd9Sstevel@tonic-gate 	return (0);
28457c478bd9Sstevel@tonic-gate }
28467c478bd9Sstevel@tonic-gate 
28477c478bd9Sstevel@tonic-gate /*
28487c478bd9Sstevel@tonic-gate  * process the message supplied via pipe. This will be called either
28497c478bd9Sstevel@tonic-gate  * immediately after cron read the message from pipe, or idle time
28507c478bd9Sstevel@tonic-gate  * if the message was pending due to the job execution.
28517c478bd9Sstevel@tonic-gate  */
28527c478bd9Sstevel@tonic-gate static void
28537c478bd9Sstevel@tonic-gate process_msg(struct message *pmsg, time_t reftime)
28547c478bd9Sstevel@tonic-gate {
28557c478bd9Sstevel@tonic-gate 	if (pmsg->etype == NULL)
28567c478bd9Sstevel@tonic-gate 		return;
28577c478bd9Sstevel@tonic-gate 
28587c478bd9Sstevel@tonic-gate 	switch (pmsg->etype) {
28597c478bd9Sstevel@tonic-gate 	case AT:
28607c478bd9Sstevel@tonic-gate 		if (pmsg->action == DELETE)
28617c478bd9Sstevel@tonic-gate 			del_atjob(pmsg->fname, pmsg->logname);
28627c478bd9Sstevel@tonic-gate 		else
28637c478bd9Sstevel@tonic-gate 			mod_atjob(pmsg->fname, (time_t)0);
28647c478bd9Sstevel@tonic-gate 		break;
28657c478bd9Sstevel@tonic-gate 	case CRON:
28667c478bd9Sstevel@tonic-gate 		if (pmsg->action == DELETE)
28677c478bd9Sstevel@tonic-gate 			del_ctab(pmsg->fname);
28687c478bd9Sstevel@tonic-gate 		else
28697c478bd9Sstevel@tonic-gate 			mod_ctab(pmsg->fname, reftime);
28707c478bd9Sstevel@tonic-gate 		break;
28717c478bd9Sstevel@tonic-gate 	default:
28727c478bd9Sstevel@tonic-gate 		msg("message received - bad format");
28737c478bd9Sstevel@tonic-gate 		break;
28747c478bd9Sstevel@tonic-gate 	}
28757c478bd9Sstevel@tonic-gate 	if (next_event != NULL) {
2876e2553d68SSerge Dussud 		if (next_event->etype == CRONEVENT) {
2877e2553d68SSerge Dussud 			switch (el_add(next_event, next_event->time,
2878e2553d68SSerge Dussud 			    (next_event->u)->ctid)) {
2879e2553d68SSerge Dussud 			case -1:
2880e2553d68SSerge Dussud 				ignore_msg("process_msg", "cron", next_event);
2881e2553d68SSerge Dussud 				break;
2882e2553d68SSerge Dussud 			case -2: /* event time lower than init time */
2883e2553d68SSerge Dussud 				reset_needed = 1;
2884e2553d68SSerge Dussud 				break;
2885e2553d68SSerge Dussud 			}
2886e2553d68SSerge Dussud 		} else { /* etype == ATEVENT */
2887e2553d68SSerge Dussud 			if (el_add(next_event, next_event->time,
2888e2553d68SSerge Dussud 			    next_event->of.at.eventid) < 0) {
2889e2553d68SSerge Dussud 				ignore_msg("process_msg", "at", next_event);
2890e2553d68SSerge Dussud 			}
2891e2553d68SSerge Dussud 		}
28927c478bd9Sstevel@tonic-gate 		next_event = NULL;
28937c478bd9Sstevel@tonic-gate 	}
28947c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
28957c478bd9Sstevel@tonic-gate 	pmsg->etype = NULL;
28967c478bd9Sstevel@tonic-gate }
28977c478bd9Sstevel@tonic-gate 
28989f163834Sbasabi /*
28999f163834Sbasabi  * Allocate a new or find an existing runinfo structure
29009f163834Sbasabi  */
29017c478bd9Sstevel@tonic-gate static struct runinfo *
29027c478bd9Sstevel@tonic-gate rinfo_get(pid_t pid)
29037c478bd9Sstevel@tonic-gate {
29047c478bd9Sstevel@tonic-gate 	struct runinfo *rp;
29057c478bd9Sstevel@tonic-gate 
29069f163834Sbasabi 	if (pid == 0) {		/* allocate a new entry */
29079f163834Sbasabi 		rp = xcalloc(1, sizeof (struct runinfo));
29089f163834Sbasabi 		rp->next = rthead;	/* link the entry into the list */
29099f163834Sbasabi 		rthead = rp;
29109f163834Sbasabi 		return (rp);
29119f163834Sbasabi 	}
29129f163834Sbasabi 	/* search the list for an existing entry */
29139f163834Sbasabi 	for (rp = rthead; rp != NULL; rp = rp->next) {
29147c478bd9Sstevel@tonic-gate 		if (rp->pid == pid)
29157c478bd9Sstevel@tonic-gate 			break;
29167c478bd9Sstevel@tonic-gate 	}
29179f163834Sbasabi 	return (rp);
29187c478bd9Sstevel@tonic-gate }
29197c478bd9Sstevel@tonic-gate 
29207c478bd9Sstevel@tonic-gate /*
29219f163834Sbasabi  * Free a runinfo structure and its associated memory
29227c478bd9Sstevel@tonic-gate  */
29237c478bd9Sstevel@tonic-gate static void
29249f163834Sbasabi rinfo_free(struct runinfo *entry)
29257c478bd9Sstevel@tonic-gate {
29269f163834Sbasabi 	struct runinfo **rpp;
29279f163834Sbasabi 	struct runinfo *rp;
29289f163834Sbasabi 
29299f163834Sbasabi #ifdef DEBUG
29309f163834Sbasabi 	(void) fprintf(stderr, "freeing job %s\n", entry->jobname);
29319f163834Sbasabi #endif
29329f163834Sbasabi 	for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) {
29339f163834Sbasabi 		if (rp == entry) {
29349f163834Sbasabi 			*rpp = rp->next;	/* unlink the entry */
29359f163834Sbasabi 			free(rp->outfile);
29369f163834Sbasabi 			free(rp->jobname);
29379f163834Sbasabi 			free(rp);
29389f163834Sbasabi 			break;
29399f163834Sbasabi 		}
29409f163834Sbasabi 	}
29417c478bd9Sstevel@tonic-gate }
29427c478bd9Sstevel@tonic-gate 
29437c478bd9Sstevel@tonic-gate /* ARGSUSED */
29447c478bd9Sstevel@tonic-gate static void
29457c478bd9Sstevel@tonic-gate thaw_handler(int sig)
29467c478bd9Sstevel@tonic-gate {
29477c478bd9Sstevel@tonic-gate 	;
29487c478bd9Sstevel@tonic-gate }
29497c478bd9Sstevel@tonic-gate 
29507c478bd9Sstevel@tonic-gate 
29517c478bd9Sstevel@tonic-gate /* ARGSUSED */
29527c478bd9Sstevel@tonic-gate static void
29537c478bd9Sstevel@tonic-gate cronend(int sig)
29547c478bd9Sstevel@tonic-gate {
29557c478bd9Sstevel@tonic-gate 	crabort("SIGTERM", REMOVE_FIFO);
29567c478bd9Sstevel@tonic-gate }
29577c478bd9Sstevel@tonic-gate 
29587c478bd9Sstevel@tonic-gate /*ARGSUSED*/
29597c478bd9Sstevel@tonic-gate static void
29607c478bd9Sstevel@tonic-gate child_handler(int sig)
29617c478bd9Sstevel@tonic-gate {
29627c478bd9Sstevel@tonic-gate 	/*
29637c478bd9Sstevel@tonic-gate 	 * Just in case someone changes the signal mask.
29647c478bd9Sstevel@tonic-gate 	 * we don't want to notify the SIGCLD.
29657c478bd9Sstevel@tonic-gate 	 */
29667c478bd9Sstevel@tonic-gate 	if (accept_sigcld) {
29677c478bd9Sstevel@tonic-gate 		(void) write(notifypipe[0], &sig, 1);
29687c478bd9Sstevel@tonic-gate 	}
29697c478bd9Sstevel@tonic-gate }
29707c478bd9Sstevel@tonic-gate 
29717c478bd9Sstevel@tonic-gate static void
29727c478bd9Sstevel@tonic-gate child_sigreset(void)
29737c478bd9Sstevel@tonic-gate {
29747c478bd9Sstevel@tonic-gate 	(void) signal(SIGCLD, SIG_DFL);
29757c478bd9Sstevel@tonic-gate 	(void) sigprocmask(SIG_SETMASK, &defmask, NULL);
29767c478bd9Sstevel@tonic-gate }
29777c478bd9Sstevel@tonic-gate 
29787c478bd9Sstevel@tonic-gate /*
29797c478bd9Sstevel@tonic-gate  * crabort() - handle exits out of cron
29807c478bd9Sstevel@tonic-gate  */
29817c478bd9Sstevel@tonic-gate static void
29827c478bd9Sstevel@tonic-gate crabort(char *mssg, int action)
29837c478bd9Sstevel@tonic-gate {
29847c478bd9Sstevel@tonic-gate 	int	c;
29857c478bd9Sstevel@tonic-gate 
29867c478bd9Sstevel@tonic-gate 	if (action & REMOVE_FIFO) {
29877c478bd9Sstevel@tonic-gate 		/* FIFO vanishes when cron finishes */
29887c478bd9Sstevel@tonic-gate 		if (unlink(FIFO) < 0)
29897c478bd9Sstevel@tonic-gate 			perror("cron could not unlink FIFO");
29907c478bd9Sstevel@tonic-gate 	}
29917c478bd9Sstevel@tonic-gate 
29927c478bd9Sstevel@tonic-gate 	if (action & CONSOLE_MSG) {
29937c478bd9Sstevel@tonic-gate 		/* write error msg to console */
29947c478bd9Sstevel@tonic-gate 		if ((c = open(CONSOLE, O_WRONLY)) >= 0) {
29957c478bd9Sstevel@tonic-gate 			(void) write(c, "cron aborted: ", 14);
29967c478bd9Sstevel@tonic-gate 			(void) write(c, mssg, strlen(mssg));
29977c478bd9Sstevel@tonic-gate 			(void) write(c, "\n", 1);
29987c478bd9Sstevel@tonic-gate 			(void) close(c);
29997c478bd9Sstevel@tonic-gate 		}
30007c478bd9Sstevel@tonic-gate 	}
30017c478bd9Sstevel@tonic-gate 
30027c478bd9Sstevel@tonic-gate 	/* always log the message */
30037c478bd9Sstevel@tonic-gate 	msg(mssg);
30047c478bd9Sstevel@tonic-gate 	msg("******* CRON ABORTED ********");
30057c478bd9Sstevel@tonic-gate 	exit(1);
30067c478bd9Sstevel@tonic-gate }
30077c478bd9Sstevel@tonic-gate 
30087c478bd9Sstevel@tonic-gate /*
30097c478bd9Sstevel@tonic-gate  * msg() - time-stamped error reporting function
30107c478bd9Sstevel@tonic-gate  */
30117c478bd9Sstevel@tonic-gate /*PRINTFLIKE1*/
30127c478bd9Sstevel@tonic-gate static void
30137c478bd9Sstevel@tonic-gate msg(char *fmt, ...)
30147c478bd9Sstevel@tonic-gate {
30157c478bd9Sstevel@tonic-gate 	va_list args;
30167c478bd9Sstevel@tonic-gate 	time_t	t;
30177c478bd9Sstevel@tonic-gate 
30187c478bd9Sstevel@tonic-gate 	t = time(NULL);
30197c478bd9Sstevel@tonic-gate 
30207c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
30217c478bd9Sstevel@tonic-gate 
30227c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "! ");
30237c478bd9Sstevel@tonic-gate 
30247c478bd9Sstevel@tonic-gate 	va_start(args, fmt);
30257c478bd9Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, args);
30267c478bd9Sstevel@tonic-gate 	va_end(args);
30277c478bd9Sstevel@tonic-gate 
30287c478bd9Sstevel@tonic-gate 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
30297c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, " %s\n", timebuf);
30307c478bd9Sstevel@tonic-gate 
30317c478bd9Sstevel@tonic-gate 	(void) fflush(stderr);
30327c478bd9Sstevel@tonic-gate }
30337c478bd9Sstevel@tonic-gate 
3034e2553d68SSerge Dussud static void
3035e2553d68SSerge Dussud ignore_msg(char *func_name, char *job_type, struct event *event)
3036e2553d68SSerge Dussud {
3037e2553d68SSerge Dussud 	msg("%s: ignoring %s job (user: %s, cmd: %s, time: %ld)",
3038e2553d68SSerge Dussud 	    func_name, job_type,
3039e2553d68SSerge Dussud 	    event->u->name ? event->u->name : "unknown",
3040e2553d68SSerge Dussud 	    event->cmd ? event->cmd : "unknown",
3041e2553d68SSerge Dussud 	    event->time);
3042e2553d68SSerge Dussud }
3043e2553d68SSerge Dussud 
30447c478bd9Sstevel@tonic-gate static void
30457c478bd9Sstevel@tonic-gate logit(int cc, struct runinfo *rp, int rc)
30467c478bd9Sstevel@tonic-gate {
30477c478bd9Sstevel@tonic-gate 	time_t t;
30487c478bd9Sstevel@tonic-gate 	int    ret;
30497c478bd9Sstevel@tonic-gate 
30507c478bd9Sstevel@tonic-gate 	if (!log)
30517c478bd9Sstevel@tonic-gate 		return;
30527c478bd9Sstevel@tonic-gate 
30537c478bd9Sstevel@tonic-gate 	t = time(NULL);
30547c478bd9Sstevel@tonic-gate 	if (cc == BCHAR)
30557c478bd9Sstevel@tonic-gate 		(void) printf("%c  CMD: %s\n", cc, next_event->cmd);
30567c478bd9Sstevel@tonic-gate 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
30577c478bd9Sstevel@tonic-gate 	(void) printf("%c  %.8s %u %c %s",
30584c4c9110Sbasabi 	    cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf);
30597c478bd9Sstevel@tonic-gate 	if ((ret = TSTAT(rc)) != 0)
30607c478bd9Sstevel@tonic-gate 		(void) printf(" ts=%d", ret);
30617c478bd9Sstevel@tonic-gate 	if ((ret = RCODE(rc)) != 0)
30627c478bd9Sstevel@tonic-gate 		(void) printf(" rc=%d", ret);
30637c478bd9Sstevel@tonic-gate 	(void) putchar('\n');
30647c478bd9Sstevel@tonic-gate 	(void) fflush(stdout);
30657c478bd9Sstevel@tonic-gate }
30667c478bd9Sstevel@tonic-gate 
30677c478bd9Sstevel@tonic-gate static void
30687c478bd9Sstevel@tonic-gate resched(int delay)
30697c478bd9Sstevel@tonic-gate {
30707c478bd9Sstevel@tonic-gate 	time_t	nt;
30717c478bd9Sstevel@tonic-gate 
30727c478bd9Sstevel@tonic-gate 	/* run job at a later time */
30737c478bd9Sstevel@tonic-gate 	nt = next_event->time + delay;
30747c478bd9Sstevel@tonic-gate 	if (next_event->etype == CRONEVENT) {
30757c478bd9Sstevel@tonic-gate 		next_event->time = next_time(next_event, (time_t)0);
30767c478bd9Sstevel@tonic-gate 		if (nt < next_event->time)
30777c478bd9Sstevel@tonic-gate 			next_event->time = nt;
3078e2553d68SSerge Dussud 		switch (el_add(next_event, next_event->time,
3079e2553d68SSerge Dussud 		    (next_event->u)->ctid)) {
3080e2553d68SSerge Dussud 		case -1:
3081e2553d68SSerge Dussud 			ignore_msg("resched", "cron", next_event);
3082e2553d68SSerge Dussud 			break;
3083e2553d68SSerge Dussud 		case -2: /* event time lower than init time */
3084e2553d68SSerge Dussud 			reset_needed = 1;
3085e2553d68SSerge Dussud 			break;
3086e2553d68SSerge Dussud 		}
30877c478bd9Sstevel@tonic-gate 		delayed = 1;
30887c478bd9Sstevel@tonic-gate 		msg("rescheduling a cron job");
30897c478bd9Sstevel@tonic-gate 		return;
30907c478bd9Sstevel@tonic-gate 	}
30917c478bd9Sstevel@tonic-gate 	add_atevent(next_event->u, next_event->cmd, nt, next_event->etype);
30927c478bd9Sstevel@tonic-gate 	msg("rescheduling at job");
30937c478bd9Sstevel@tonic-gate }
30947c478bd9Sstevel@tonic-gate 
30957c478bd9Sstevel@tonic-gate static void
30967c478bd9Sstevel@tonic-gate quedefs(int action)
30977c478bd9Sstevel@tonic-gate {
30987c478bd9Sstevel@tonic-gate 	int	i;
30997c478bd9Sstevel@tonic-gate 	int	j;
31007c478bd9Sstevel@tonic-gate 	char	qbuf[QBUFSIZ];
31017c478bd9Sstevel@tonic-gate 	FILE	*fd;
31027c478bd9Sstevel@tonic-gate 
31037c478bd9Sstevel@tonic-gate 	/* set up default queue definitions */
31047c478bd9Sstevel@tonic-gate 	for (i = 0; i < NQUEUE; i++) {
31057c478bd9Sstevel@tonic-gate 		qt[i].njob = qd.njob;
31067c478bd9Sstevel@tonic-gate 		qt[i].nice = qd.nice;
31077c478bd9Sstevel@tonic-gate 		qt[i].nwait = qd.nwait;
31087c478bd9Sstevel@tonic-gate 	}
31097c478bd9Sstevel@tonic-gate 	if (action == DEFAULT)
31107c478bd9Sstevel@tonic-gate 		return;
31117c478bd9Sstevel@tonic-gate 	if ((fd = fopen(QUEDEFS, "r")) == NULL) {
31127c478bd9Sstevel@tonic-gate 		msg("cannot open quedefs file");
31137c478bd9Sstevel@tonic-gate 		msg("using default queue definitions");
31147c478bd9Sstevel@tonic-gate 		return;
31157c478bd9Sstevel@tonic-gate 	}
31167c478bd9Sstevel@tonic-gate 	while (fgets(qbuf, QBUFSIZ, fd) != NULL) {
31177c478bd9Sstevel@tonic-gate 		if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.')
31187c478bd9Sstevel@tonic-gate 			continue;
31197c478bd9Sstevel@tonic-gate 		parsqdef(&qbuf[2]);
31207c478bd9Sstevel@tonic-gate 		qt[j].njob = qq.njob;
31217c478bd9Sstevel@tonic-gate 		qt[j].nice = qq.nice;
31227c478bd9Sstevel@tonic-gate 		qt[j].nwait = qq.nwait;
31237c478bd9Sstevel@tonic-gate 	}
31247c478bd9Sstevel@tonic-gate 	(void) fclose(fd);
31257c478bd9Sstevel@tonic-gate }
31267c478bd9Sstevel@tonic-gate 
31277c478bd9Sstevel@tonic-gate static void
31287c478bd9Sstevel@tonic-gate parsqdef(char *name)
31297c478bd9Sstevel@tonic-gate {
31307c478bd9Sstevel@tonic-gate 	int i;
31317c478bd9Sstevel@tonic-gate 
31327c478bd9Sstevel@tonic-gate 	qq = qd;
31337c478bd9Sstevel@tonic-gate 	while (*name) {
31347c478bd9Sstevel@tonic-gate 		i = 0;
31357c478bd9Sstevel@tonic-gate 		while (isdigit(*name)) {
31367c478bd9Sstevel@tonic-gate 			i *= 10;
31377c478bd9Sstevel@tonic-gate 			i += *name++ - '0';
31387c478bd9Sstevel@tonic-gate 		}
31397c478bd9Sstevel@tonic-gate 		switch (*name++) {
31407c478bd9Sstevel@tonic-gate 		case JOBF:
31417c478bd9Sstevel@tonic-gate 			qq.njob = i;
31427c478bd9Sstevel@tonic-gate 			break;
31437c478bd9Sstevel@tonic-gate 		case NICEF:
31447c478bd9Sstevel@tonic-gate 			qq.nice = i;
31457c478bd9Sstevel@tonic-gate 			break;
31467c478bd9Sstevel@tonic-gate 		case WAITF:
31477c478bd9Sstevel@tonic-gate 			qq.nwait = i;
31487c478bd9Sstevel@tonic-gate 			break;
31497c478bd9Sstevel@tonic-gate 		}
31507c478bd9Sstevel@tonic-gate 	}
31517c478bd9Sstevel@tonic-gate }
31527c478bd9Sstevel@tonic-gate 
31537c478bd9Sstevel@tonic-gate /*
31547c478bd9Sstevel@tonic-gate  * defaults - read defaults from /etc/default/cron
31557c478bd9Sstevel@tonic-gate  */
31567c478bd9Sstevel@tonic-gate static void
31577c478bd9Sstevel@tonic-gate defaults()
31587c478bd9Sstevel@tonic-gate {
31597c478bd9Sstevel@tonic-gate 	int  flags;
31607c478bd9Sstevel@tonic-gate 	char *deflog;
31617c478bd9Sstevel@tonic-gate 	char *hz, *tz;
31627c478bd9Sstevel@tonic-gate 
31637c478bd9Sstevel@tonic-gate 	/*
31647c478bd9Sstevel@tonic-gate 	 * get HZ value for environment
31657c478bd9Sstevel@tonic-gate 	 */
31667c478bd9Sstevel@tonic-gate 	if ((hz = getenv("HZ")) == (char *)NULL)
31677c478bd9Sstevel@tonic-gate 		(void) sprintf(hzname, "HZ=%d", HZ);
31687c478bd9Sstevel@tonic-gate 	else
31697c478bd9Sstevel@tonic-gate 		(void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz);
31707c478bd9Sstevel@tonic-gate 	/*
31717c478bd9Sstevel@tonic-gate 	 * get TZ value for environment
31727c478bd9Sstevel@tonic-gate 	 */
31737c478bd9Sstevel@tonic-gate 	(void) snprintf(tzone, sizeof (tzone), "TZ=%s",
31744c4c9110Sbasabi 	    ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ);
31757c478bd9Sstevel@tonic-gate 
31767c478bd9Sstevel@tonic-gate 	if (defopen(DEFFILE) == 0) {
31777c478bd9Sstevel@tonic-gate 		/* ignore case */
31787c478bd9Sstevel@tonic-gate 		flags = defcntl(DC_GETFLAGS, 0);
31797c478bd9Sstevel@tonic-gate 		TURNOFF(flags, DC_CASE);
31807c478bd9Sstevel@tonic-gate 		(void) defcntl(DC_SETFLAGS, flags);
31817c478bd9Sstevel@tonic-gate 
31827c478bd9Sstevel@tonic-gate 		if (((deflog = defread("CRONLOG=")) == NULL) ||
31837c478bd9Sstevel@tonic-gate 		    (*deflog == 'N') || (*deflog == 'n'))
31847c478bd9Sstevel@tonic-gate 			log = 0;
31857c478bd9Sstevel@tonic-gate 		else
31867c478bd9Sstevel@tonic-gate 			log = 1;
31877c478bd9Sstevel@tonic-gate 		/* fix for 1087611 - allow paths to be set in defaults file */
31887c478bd9Sstevel@tonic-gate 		if ((Def_path = defread("PATH=")) != NULL) {
31897c478bd9Sstevel@tonic-gate 			(void) strlcat(path, Def_path, LINE_MAX);
31907c478bd9Sstevel@tonic-gate 		} else {
31917c478bd9Sstevel@tonic-gate 			(void) strlcpy(path, NONROOTPATH, LINE_MAX);
31927c478bd9Sstevel@tonic-gate 		}
31937c478bd9Sstevel@tonic-gate 		if ((Def_supath = defread("SUPATH=")) != NULL) {
31947c478bd9Sstevel@tonic-gate 			(void) strlcat(supath, Def_supath, LINE_MAX);
31957c478bd9Sstevel@tonic-gate 		} else {
31967c478bd9Sstevel@tonic-gate 			(void) strlcpy(supath, ROOTPATH, LINE_MAX);
31977c478bd9Sstevel@tonic-gate 		}
31987c478bd9Sstevel@tonic-gate 		(void) defopen(NULL);
31997c478bd9Sstevel@tonic-gate 	}
32007c478bd9Sstevel@tonic-gate }
32017c478bd9Sstevel@tonic-gate 
32027c478bd9Sstevel@tonic-gate /*
32037c478bd9Sstevel@tonic-gate  * Determine if a user entry for a job is still ok.  The method used here
32047c478bd9Sstevel@tonic-gate  * is a lot (about 75x) faster than using setgrent() / getgrent()
32057c478bd9Sstevel@tonic-gate  * endgrent().  It should be safe because we use the sysconf to determine
32067c478bd9Sstevel@tonic-gate  * the max, and it tolerates the max being 0.
32077c478bd9Sstevel@tonic-gate  */
32087c478bd9Sstevel@tonic-gate 
32097c478bd9Sstevel@tonic-gate static int
32104c4c9110Sbasabi verify_user_cred(struct usr *u)
32117c478bd9Sstevel@tonic-gate {
32127c478bd9Sstevel@tonic-gate 	struct passwd *pw;
32137c478bd9Sstevel@tonic-gate 	size_t numUsrGrps = 0;
32147c478bd9Sstevel@tonic-gate 	size_t numOrigGrps = 0;
32157c478bd9Sstevel@tonic-gate 	size_t i;
32167c478bd9Sstevel@tonic-gate 	int retval;
32177c478bd9Sstevel@tonic-gate 
32187c478bd9Sstevel@tonic-gate 	/*
32197c478bd9Sstevel@tonic-gate 	 * Maximum number of groups a user may be in concurrently.  This
32207c478bd9Sstevel@tonic-gate 	 * is a value which we obtain at runtime through a sysconf()
32217c478bd9Sstevel@tonic-gate 	 * call.
32227c478bd9Sstevel@tonic-gate 	 */
32237c478bd9Sstevel@tonic-gate 
32247c478bd9Sstevel@tonic-gate 	static size_t nGroupsMax = (size_t)-1;
32257c478bd9Sstevel@tonic-gate 
32267c478bd9Sstevel@tonic-gate 	/*
32277c478bd9Sstevel@tonic-gate 	 * Arrays for cron user's group list, constructed at startup to
32287c478bd9Sstevel@tonic-gate 	 * be nGroupsMax elements long, used for verifying user
32297c478bd9Sstevel@tonic-gate 	 * credentials prior to execution.
32307c478bd9Sstevel@tonic-gate 	 */
32317c478bd9Sstevel@tonic-gate 
32327c478bd9Sstevel@tonic-gate 	static gid_t *UsrGrps;
32337c478bd9Sstevel@tonic-gate 	static gid_t *OrigGrps;
32347c478bd9Sstevel@tonic-gate 
32354c4c9110Sbasabi 	if ((pw = getpwnam(u->name)) == NULL)
32367c478bd9Sstevel@tonic-gate 		return (VUC_BADUSER);
32374c4c9110Sbasabi 	if (u->home != NULL) {
32384c4c9110Sbasabi 		if (strcmp(u->home, pw->pw_dir) != 0) {
32394c4c9110Sbasabi 			free(u->home);
32404c4c9110Sbasabi 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
32414c4c9110Sbasabi 			(void) strcpy(u->home, pw->pw_dir);
32424c4c9110Sbasabi 		}
32434c4c9110Sbasabi 	} else {
32444c4c9110Sbasabi 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
32454c4c9110Sbasabi 		(void) strcpy(u->home, pw->pw_dir);
32467c478bd9Sstevel@tonic-gate 	}
32474c4c9110Sbasabi 	if (u->uid != pw->pw_uid)
32484c4c9110Sbasabi 		u->uid = pw->pw_uid;
32494c4c9110Sbasabi 	if (u->gid != pw->pw_gid)
32504c4c9110Sbasabi 		u->gid  = pw->pw_gid;
32517c478bd9Sstevel@tonic-gate 
32527c478bd9Sstevel@tonic-gate 	/*
32537c478bd9Sstevel@tonic-gate 	 * Create the group id lists needed for job credential
32547c478bd9Sstevel@tonic-gate 	 * verification.
32557c478bd9Sstevel@tonic-gate 	 */
32567c478bd9Sstevel@tonic-gate 
32577c478bd9Sstevel@tonic-gate 	if (nGroupsMax == (size_t)-1) {
32587c478bd9Sstevel@tonic-gate 		if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) {
32597c478bd9Sstevel@tonic-gate 			UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t));
32607c478bd9Sstevel@tonic-gate 			OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t));
32617c478bd9Sstevel@tonic-gate 		}
32627c478bd9Sstevel@tonic-gate 
32637c478bd9Sstevel@tonic-gate #ifdef DEBUG
32647c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax);
32657c478bd9Sstevel@tonic-gate #endif
32667c478bd9Sstevel@tonic-gate 	}
32677c478bd9Sstevel@tonic-gate 
32687c478bd9Sstevel@tonic-gate #ifdef DEBUG
32697c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name,
32707c478bd9Sstevel@tonic-gate 	    pw->pw_uid);
32717c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, "
32727c478bd9Sstevel@tonic-gate 	    "u->gid = %d\n", pw->pw_gid, u->gid);
32737c478bd9Sstevel@tonic-gate #endif
32747c478bd9Sstevel@tonic-gate 
32757c478bd9Sstevel@tonic-gate 	retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP;
32767c478bd9Sstevel@tonic-gate 
32777c478bd9Sstevel@tonic-gate 	if (nGroupsMax > 0) {
32787c478bd9Sstevel@tonic-gate 		numOrigGrps = getgroups(nGroupsMax, OrigGrps);
32797c478bd9Sstevel@tonic-gate 
32807c478bd9Sstevel@tonic-gate 		(void) initgroups(pw->pw_name, pw->pw_gid);
32817c478bd9Sstevel@tonic-gate 		numUsrGrps = getgroups(nGroupsMax, UsrGrps);
32827c478bd9Sstevel@tonic-gate 
32837c478bd9Sstevel@tonic-gate 		for (i = 0; i < numUsrGrps; i++) {
32847c478bd9Sstevel@tonic-gate 			if (UsrGrps[i] == u->gid) {
32857c478bd9Sstevel@tonic-gate 				retval = VUC_OK;
32867c478bd9Sstevel@tonic-gate 				break;
32877c478bd9Sstevel@tonic-gate 			}
32887c478bd9Sstevel@tonic-gate 		}
32897c478bd9Sstevel@tonic-gate 
32907c478bd9Sstevel@tonic-gate 		if (OrigGrps) {
32917c478bd9Sstevel@tonic-gate 			(void) setgroups(numOrigGrps, OrigGrps);
32927c478bd9Sstevel@tonic-gate 		}
32937c478bd9Sstevel@tonic-gate 	}
32947c478bd9Sstevel@tonic-gate 
32957c478bd9Sstevel@tonic-gate #ifdef DEBUG
32967c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval);
32977c478bd9Sstevel@tonic-gate #endif
32987c478bd9Sstevel@tonic-gate 
32997c478bd9Sstevel@tonic-gate 	return (retval);
33007c478bd9Sstevel@tonic-gate }
33017c478bd9Sstevel@tonic-gate 
33027c478bd9Sstevel@tonic-gate static int
33037c478bd9Sstevel@tonic-gate set_user_cred(const struct usr *u, struct project *pproj)
33047c478bd9Sstevel@tonic-gate {
33057c478bd9Sstevel@tonic-gate 	static char *progname = "cron";
33067c478bd9Sstevel@tonic-gate 	int r = 0, rval = 0;
33077c478bd9Sstevel@tonic-gate 
33087c478bd9Sstevel@tonic-gate 	if ((r = pam_start(progname, u->name, &pam_conv, &pamh))
33094c4c9110Sbasabi 	    != PAM_SUCCESS) {
33107c478bd9Sstevel@tonic-gate #ifdef DEBUG
33117c478bd9Sstevel@tonic-gate 		msg("pam_start returns %d\n", r);
33127c478bd9Sstevel@tonic-gate #endif
33137c478bd9Sstevel@tonic-gate 		rval = VUC_BADUSER;
33147c478bd9Sstevel@tonic-gate 		goto set_eser_cred_exit;
33157c478bd9Sstevel@tonic-gate 	}
33167c478bd9Sstevel@tonic-gate 
33177c478bd9Sstevel@tonic-gate 	r = pam_acct_mgmt(pamh, 0);
33187c478bd9Sstevel@tonic-gate #ifdef DEBUG
33197c478bd9Sstevel@tonic-gate 	msg("pam_acc_mgmt returns %d\n", r);
33207c478bd9Sstevel@tonic-gate #endif
33217c478bd9Sstevel@tonic-gate 	if (r == PAM_ACCT_EXPIRED) {
33227c478bd9Sstevel@tonic-gate 		rval = VUC_EXPIRED;
33237c478bd9Sstevel@tonic-gate 		goto set_eser_cred_exit;
33247c478bd9Sstevel@tonic-gate 	}
33257c478bd9Sstevel@tonic-gate 	if (r == PAM_NEW_AUTHTOK_REQD) {
33267c478bd9Sstevel@tonic-gate 		rval = VUC_NEW_AUTH;
33277c478bd9Sstevel@tonic-gate 		goto set_eser_cred_exit;
33287c478bd9Sstevel@tonic-gate 	}
33297c478bd9Sstevel@tonic-gate 	if (r != PAM_SUCCESS) {
33307c478bd9Sstevel@tonic-gate 		rval = VUC_BADUSER;
33317c478bd9Sstevel@tonic-gate 		goto set_eser_cred_exit;
33327c478bd9Sstevel@tonic-gate 	}
33337c478bd9Sstevel@tonic-gate 
33347c478bd9Sstevel@tonic-gate 	if (pproj != NULL) {
33357c478bd9Sstevel@tonic-gate 		size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name);
33367c478bd9Sstevel@tonic-gate 		char *buf = alloca(sz);
33377c478bd9Sstevel@tonic-gate 
33387c478bd9Sstevel@tonic-gate 		(void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name);
33397c478bd9Sstevel@tonic-gate 		(void) pam_set_item(pamh, PAM_RESOURCE, buf);
33407c478bd9Sstevel@tonic-gate 	}
33417c478bd9Sstevel@tonic-gate 
33427c478bd9Sstevel@tonic-gate 	r = pam_setcred(pamh, PAM_ESTABLISH_CRED);
33437c478bd9Sstevel@tonic-gate 	if (r != PAM_SUCCESS)
33447c478bd9Sstevel@tonic-gate 		rval = VUC_BADUSER;
33457c478bd9Sstevel@tonic-gate 
33467c478bd9Sstevel@tonic-gate set_eser_cred_exit:
33477c478bd9Sstevel@tonic-gate 	(void) pam_end(pamh, r);
33487c478bd9Sstevel@tonic-gate 	return (rval);
33497c478bd9Sstevel@tonic-gate }
33507c478bd9Sstevel@tonic-gate 
33517c478bd9Sstevel@tonic-gate static void
33527c478bd9Sstevel@tonic-gate clean_out_user(struct usr *u)
33537c478bd9Sstevel@tonic-gate {
33547c478bd9Sstevel@tonic-gate 	if (next_event->u == u) {
33557c478bd9Sstevel@tonic-gate 		next_event = NULL;
33567c478bd9Sstevel@tonic-gate 	}
33577c478bd9Sstevel@tonic-gate 
33587c478bd9Sstevel@tonic-gate 	clean_out_ctab(u);
33597c478bd9Sstevel@tonic-gate 	clean_out_atjobs(u);
33607c478bd9Sstevel@tonic-gate 	free_if_unused(u);
33617c478bd9Sstevel@tonic-gate }
33627c478bd9Sstevel@tonic-gate 
33637c478bd9Sstevel@tonic-gate static void
33647c478bd9Sstevel@tonic-gate clean_out_atjobs(struct usr *u)
33657c478bd9Sstevel@tonic-gate {
33667c478bd9Sstevel@tonic-gate 	struct event *ev, *pv;
33677c478bd9Sstevel@tonic-gate 
33687c478bd9Sstevel@tonic-gate 	for (pv = NULL, ev = u->atevents;
33694c4c9110Sbasabi 	    ev != NULL;
33704c4c9110Sbasabi 	    pv = ev, ev = ev->link, free(pv)) {
33717c478bd9Sstevel@tonic-gate 		el_remove(ev->of.at.eventid, 1);
33727c478bd9Sstevel@tonic-gate 		if (cwd == AT)
33737c478bd9Sstevel@tonic-gate 			cron_unlink(ev->cmd);
33747c478bd9Sstevel@tonic-gate 		else {
33757c478bd9Sstevel@tonic-gate 			char buf[PATH_MAX];
33767c478bd9Sstevel@tonic-gate 			if (strlen(ATDIR) + strlen(ev->cmd) + 2
33774c4c9110Sbasabi 			    < PATH_MAX) {
33787c478bd9Sstevel@tonic-gate 				(void) sprintf(buf, "%s/%s", ATDIR, ev->cmd);
33797c478bd9Sstevel@tonic-gate 				cron_unlink(buf);
33807c478bd9Sstevel@tonic-gate 			}
33817c478bd9Sstevel@tonic-gate 		}
33827c478bd9Sstevel@tonic-gate 		free(ev->cmd);
33837c478bd9Sstevel@tonic-gate 	}
33847c478bd9Sstevel@tonic-gate 
33857c478bd9Sstevel@tonic-gate 	u->atevents = NULL;
33867c478bd9Sstevel@tonic-gate }
33877c478bd9Sstevel@tonic-gate 
33887c478bd9Sstevel@tonic-gate static void
33897c478bd9Sstevel@tonic-gate clean_out_ctab(struct usr *u)
33907c478bd9Sstevel@tonic-gate {
33917c478bd9Sstevel@tonic-gate 	rm_ctevents(u);
33927c478bd9Sstevel@tonic-gate 	el_remove(u->ctid, 0);
33937c478bd9Sstevel@tonic-gate 	u->ctid = 0;
33947c478bd9Sstevel@tonic-gate 	u->ctexists = 0;
33957c478bd9Sstevel@tonic-gate }
33967c478bd9Sstevel@tonic-gate 
33977c478bd9Sstevel@tonic-gate static void
33987c478bd9Sstevel@tonic-gate cron_unlink(char *name)
33997c478bd9Sstevel@tonic-gate {
34007c478bd9Sstevel@tonic-gate 	int r;
34017c478bd9Sstevel@tonic-gate 
34027c478bd9Sstevel@tonic-gate 	r = unlink(name);
34037c478bd9Sstevel@tonic-gate 	if (r == 0 || (r == -1 && errno == ENOENT)) {
34047c478bd9Sstevel@tonic-gate 		(void) audit_cron_delete_anc_file(name, NULL);
34057c478bd9Sstevel@tonic-gate 	}
34067c478bd9Sstevel@tonic-gate }
34077c478bd9Sstevel@tonic-gate 
34087c478bd9Sstevel@tonic-gate static void
34097c478bd9Sstevel@tonic-gate create_anc_ctab(struct event *e)
34107c478bd9Sstevel@tonic-gate {
34117c478bd9Sstevel@tonic-gate 	if (audit_cron_create_anc_file(e->u->name,
34124c4c9110Sbasabi 	    (cwd == CRON) ? NULL:CRONDIR,
34134c4c9110Sbasabi 	    e->u->name, e->u->uid) == -1) {
34147c478bd9Sstevel@tonic-gate 		process_anc_files(CRON_ANC_DELETE);
34157c478bd9Sstevel@tonic-gate 		crabort("cannot create ancillary files for crontabs",
34164c4c9110Sbasabi 		    REMOVE_FIFO|CONSOLE_MSG);
34177c478bd9Sstevel@tonic-gate 	}
34187c478bd9Sstevel@tonic-gate }
34197c478bd9Sstevel@tonic-gate 
34207c478bd9Sstevel@tonic-gate static void
34217c478bd9Sstevel@tonic-gate delete_anc_ctab(struct event *e)
34227c478bd9Sstevel@tonic-gate {
34237c478bd9Sstevel@tonic-gate 	(void) audit_cron_delete_anc_file(e->u->name,
34244c4c9110Sbasabi 	    (cwd == CRON) ? NULL:CRONDIR);
34257c478bd9Sstevel@tonic-gate }
34267c478bd9Sstevel@tonic-gate 
34277c478bd9Sstevel@tonic-gate static void
34287c478bd9Sstevel@tonic-gate create_anc_atjob(struct event *e)
34297c478bd9Sstevel@tonic-gate {
34307c478bd9Sstevel@tonic-gate 	if (!e->of.at.exists)
34317c478bd9Sstevel@tonic-gate 		return;
34327c478bd9Sstevel@tonic-gate 
34337c478bd9Sstevel@tonic-gate 	if (audit_cron_create_anc_file(e->cmd,
34344c4c9110Sbasabi 	    (cwd == AT) ? NULL:ATDIR,
34354c4c9110Sbasabi 	    e->u->name, e->u->uid) == -1) {
34367c478bd9Sstevel@tonic-gate 		process_anc_files(CRON_ANC_DELETE);
34377c478bd9Sstevel@tonic-gate 		crabort("cannot create ancillary files for atjobs",
34384c4c9110Sbasabi 		    REMOVE_FIFO|CONSOLE_MSG);
34397c478bd9Sstevel@tonic-gate 	}
34407c478bd9Sstevel@tonic-gate }
34417c478bd9Sstevel@tonic-gate 
34427c478bd9Sstevel@tonic-gate static void
34437c478bd9Sstevel@tonic-gate delete_anc_atjob(struct event *e)
34447c478bd9Sstevel@tonic-gate {
34457c478bd9Sstevel@tonic-gate 	if (!e->of.at.exists)
34467c478bd9Sstevel@tonic-gate 		return;
34477c478bd9Sstevel@tonic-gate 
34487c478bd9Sstevel@tonic-gate 	(void) audit_cron_delete_anc_file(e->cmd,
34494c4c9110Sbasabi 	    (cwd == AT) ? NULL:ATDIR);
34507c478bd9Sstevel@tonic-gate }
34517c478bd9Sstevel@tonic-gate 
34527c478bd9Sstevel@tonic-gate 
34537c478bd9Sstevel@tonic-gate static void
34547c478bd9Sstevel@tonic-gate process_anc_files(int del)
34557c478bd9Sstevel@tonic-gate {
34567c478bd9Sstevel@tonic-gate 	struct usr	*u = uhead;
34577c478bd9Sstevel@tonic-gate 	struct event	*e;
34587c478bd9Sstevel@tonic-gate 
34597c478bd9Sstevel@tonic-gate 	if (!audit_cron_mode())
34607c478bd9Sstevel@tonic-gate 		return;
34617c478bd9Sstevel@tonic-gate 
34627c478bd9Sstevel@tonic-gate 	for (;;) {
34637c478bd9Sstevel@tonic-gate 		if (u->ctexists && u->ctevents != NULL) {
34647c478bd9Sstevel@tonic-gate 			e = u->ctevents;
34657c478bd9Sstevel@tonic-gate 			for (;;) {
34667c478bd9Sstevel@tonic-gate 				if (del)
34677c478bd9Sstevel@tonic-gate 					delete_anc_ctab(e);
34687c478bd9Sstevel@tonic-gate 				else
34697c478bd9Sstevel@tonic-gate 					create_anc_ctab(e);
34707c478bd9Sstevel@tonic-gate 				if ((e = e->link) == NULL)
34717c478bd9Sstevel@tonic-gate 					break;
34727c478bd9Sstevel@tonic-gate 			}
34737c478bd9Sstevel@tonic-gate 		}
34747c478bd9Sstevel@tonic-gate 
34757c478bd9Sstevel@tonic-gate 		if (u->atevents != NULL) {
34767c478bd9Sstevel@tonic-gate 			e = u->atevents;
34777c478bd9Sstevel@tonic-gate 			for (;;) {
34787c478bd9Sstevel@tonic-gate 				if (del)
34797c478bd9Sstevel@tonic-gate 					delete_anc_atjob(e);
34807c478bd9Sstevel@tonic-gate 				else
34817c478bd9Sstevel@tonic-gate 					create_anc_atjob(e);
34827c478bd9Sstevel@tonic-gate 				if ((e = e->link) == NULL)
34837c478bd9Sstevel@tonic-gate 					break;
34847c478bd9Sstevel@tonic-gate 			}
34857c478bd9Sstevel@tonic-gate 		}
34867c478bd9Sstevel@tonic-gate 
34877c478bd9Sstevel@tonic-gate 		if ((u = u->nextusr)  == NULL)
34887c478bd9Sstevel@tonic-gate 			break;
34897c478bd9Sstevel@tonic-gate 	}
34907c478bd9Sstevel@tonic-gate }
34917c478bd9Sstevel@tonic-gate 
34927c478bd9Sstevel@tonic-gate /*ARGSUSED*/
34937c478bd9Sstevel@tonic-gate static int
34947c478bd9Sstevel@tonic-gate cron_conv(int num_msg, struct pam_message **msgs,
34957c478bd9Sstevel@tonic-gate     struct pam_response **response, void *appdata_ptr)
34967c478bd9Sstevel@tonic-gate {
34977c478bd9Sstevel@tonic-gate 	struct pam_message	**m = msgs;
34987c478bd9Sstevel@tonic-gate 	int i;
34997c478bd9Sstevel@tonic-gate 
35007c478bd9Sstevel@tonic-gate 	for (i = 0; i < num_msg; i++) {
35017c478bd9Sstevel@tonic-gate 		switch (m[i]->msg_style) {
35027c478bd9Sstevel@tonic-gate 		case PAM_ERROR_MSG:
35037c478bd9Sstevel@tonic-gate 		case PAM_TEXT_INFO:
35047c478bd9Sstevel@tonic-gate 			if (m[i]->msg != NULL) {
35057c478bd9Sstevel@tonic-gate 				(void) msg("%s\n", m[i]->msg);
35067c478bd9Sstevel@tonic-gate 			}
35077c478bd9Sstevel@tonic-gate 			break;
35087c478bd9Sstevel@tonic-gate 
35097c478bd9Sstevel@tonic-gate 		default:
35107c478bd9Sstevel@tonic-gate 			break;
35117c478bd9Sstevel@tonic-gate 		}
35127c478bd9Sstevel@tonic-gate 	}
35137c478bd9Sstevel@tonic-gate 	return (0);
35147c478bd9Sstevel@tonic-gate }
35157c478bd9Sstevel@tonic-gate 
35167c478bd9Sstevel@tonic-gate /*
35177c478bd9Sstevel@tonic-gate  * Cron creates process for other than job. Mail process is the
35187c478bd9Sstevel@tonic-gate  * one which rinfo does not cover. Therefore, miscpid will keep
35197c478bd9Sstevel@tonic-gate  * track of the pids executed from cron. Otherwise, we will see
35207c478bd9Sstevel@tonic-gate  * "unexpected pid returned.." messages appear in the log file.
35217c478bd9Sstevel@tonic-gate  */
35227c478bd9Sstevel@tonic-gate static void
35237c478bd9Sstevel@tonic-gate miscpid_insert(pid_t pid)
35247c478bd9Sstevel@tonic-gate {
35257c478bd9Sstevel@tonic-gate 	struct miscpid *mp;
35267c478bd9Sstevel@tonic-gate 
35277c478bd9Sstevel@tonic-gate 	mp = xmalloc(sizeof (*mp));
35287c478bd9Sstevel@tonic-gate 	mp->pid = pid;
35297c478bd9Sstevel@tonic-gate 	mp->next = miscpid_head;
35307c478bd9Sstevel@tonic-gate 	miscpid_head = mp;
35317c478bd9Sstevel@tonic-gate }
35327c478bd9Sstevel@tonic-gate 
35337c478bd9Sstevel@tonic-gate static int
35347c478bd9Sstevel@tonic-gate miscpid_delete(pid_t pid)
35357c478bd9Sstevel@tonic-gate {
35367c478bd9Sstevel@tonic-gate 	struct miscpid *mp, *omp;
35377c478bd9Sstevel@tonic-gate 	int found = 0;
35387c478bd9Sstevel@tonic-gate 
35397c478bd9Sstevel@tonic-gate 	omp = NULL;
35407c478bd9Sstevel@tonic-gate 	for (mp = miscpid_head; mp != NULL; mp = mp->next) {
35417c478bd9Sstevel@tonic-gate 		if (mp->pid == pid) {
35427c478bd9Sstevel@tonic-gate 			found = 1;
35437c478bd9Sstevel@tonic-gate 			break;
35447c478bd9Sstevel@tonic-gate 		}
35457c478bd9Sstevel@tonic-gate 		omp = mp;
35467c478bd9Sstevel@tonic-gate 	}
35477c478bd9Sstevel@tonic-gate 	if (found) {
35487c478bd9Sstevel@tonic-gate 		if (omp != NULL)
35497c478bd9Sstevel@tonic-gate 			omp->next = mp->next;
35507c478bd9Sstevel@tonic-gate 		else
35517c478bd9Sstevel@tonic-gate 			miscpid_head = NULL;
35527c478bd9Sstevel@tonic-gate 		free(mp);
35537c478bd9Sstevel@tonic-gate 	}
35547c478bd9Sstevel@tonic-gate 	return (found);
35557c478bd9Sstevel@tonic-gate }
35567c478bd9Sstevel@tonic-gate 
35577c478bd9Sstevel@tonic-gate /*
35587c478bd9Sstevel@tonic-gate  * Establish contract terms such that all children are in abandoned
35597c478bd9Sstevel@tonic-gate  * process contracts.
35607c478bd9Sstevel@tonic-gate  */
3561032624d5Sbasabi static void
3562032624d5Sbasabi contract_set_template(void)
35637c478bd9Sstevel@tonic-gate {
35647c478bd9Sstevel@tonic-gate 	int fd;
35657c478bd9Sstevel@tonic-gate 
35667c478bd9Sstevel@tonic-gate 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
35677c478bd9Sstevel@tonic-gate 		crabort("cannot open process contract template",
35687c478bd9Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35697c478bd9Sstevel@tonic-gate 
35707c478bd9Sstevel@tonic-gate 	if (ct_pr_tmpl_set_param(fd, 0) ||
35717c478bd9Sstevel@tonic-gate 	    ct_tmpl_set_informative(fd, 0) ||
35727c478bd9Sstevel@tonic-gate 	    ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR))
35737c478bd9Sstevel@tonic-gate 		crabort("cannot establish contract template terms",
35747c478bd9Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35757c478bd9Sstevel@tonic-gate 
35767c478bd9Sstevel@tonic-gate 	if (ct_tmpl_activate(fd))
35777c478bd9Sstevel@tonic-gate 		crabort("cannot activate contract template",
35787c478bd9Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35797c478bd9Sstevel@tonic-gate 
35807c478bd9Sstevel@tonic-gate 	(void) close(fd);
35817c478bd9Sstevel@tonic-gate }
35827c478bd9Sstevel@tonic-gate 
35837c478bd9Sstevel@tonic-gate /*
35847c478bd9Sstevel@tonic-gate  * Clear active process contract template.
35857c478bd9Sstevel@tonic-gate  */
3586032624d5Sbasabi static void
3587032624d5Sbasabi contract_clear_template(void)
35887c478bd9Sstevel@tonic-gate {
35897c478bd9Sstevel@tonic-gate 	int fd;
35907c478bd9Sstevel@tonic-gate 
35917c478bd9Sstevel@tonic-gate 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
35927c478bd9Sstevel@tonic-gate 		crabort("cannot open process contract template",
35937c478bd9Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35947c478bd9Sstevel@tonic-gate 
35957c478bd9Sstevel@tonic-gate 	if (ct_tmpl_clear(fd))
35967c478bd9Sstevel@tonic-gate 		crabort("cannot clear contract template",
35977c478bd9Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35987c478bd9Sstevel@tonic-gate 
35997c478bd9Sstevel@tonic-gate 	(void) close(fd);
36007c478bd9Sstevel@tonic-gate }
36017c478bd9Sstevel@tonic-gate 
36027c478bd9Sstevel@tonic-gate /*
36037c478bd9Sstevel@tonic-gate  * Abandon latest process contract unconditionally.  If we have leaked [some
36047c478bd9Sstevel@tonic-gate  * critical amount], exit such that the kernel reaps our contracts.
36057c478bd9Sstevel@tonic-gate  */
36067c478bd9Sstevel@tonic-gate static void
36077c478bd9Sstevel@tonic-gate contract_abandon_latest(pid_t pid)
36087c478bd9Sstevel@tonic-gate {
36097c478bd9Sstevel@tonic-gate 	int r;
36107c478bd9Sstevel@tonic-gate 	ctid_t id;
36117c478bd9Sstevel@tonic-gate 	static uint_t cts_lost;
36127c478bd9Sstevel@tonic-gate 
36137c478bd9Sstevel@tonic-gate 	if (cts_lost > MAX_LOST_CONTRACTS)
36147c478bd9Sstevel@tonic-gate 		crabort("repeated failure to abandon contracts",
36157c478bd9Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
36167c478bd9Sstevel@tonic-gate 
36177c478bd9Sstevel@tonic-gate 	if (r = contract_latest(&id)) {
361855aa09b1Sbasabi 		msg("could not obtain latest contract for "
361955aa09b1Sbasabi 		    "PID %ld: %s", pid, strerror(r));
36207c478bd9Sstevel@tonic-gate 		cts_lost++;
36217c478bd9Sstevel@tonic-gate 		return;
36227c478bd9Sstevel@tonic-gate 	}
36237c478bd9Sstevel@tonic-gate 
36247c478bd9Sstevel@tonic-gate 	if (r = contract_abandon_id(id)) {
36257c478bd9Sstevel@tonic-gate 		msg("could not abandon latest contract %ld: %s", id,
36267c478bd9Sstevel@tonic-gate 		    strerror(r));
36277c478bd9Sstevel@tonic-gate 		cts_lost++;
36287c478bd9Sstevel@tonic-gate 		return;
36297c478bd9Sstevel@tonic-gate 	}
36307c478bd9Sstevel@tonic-gate }
3631*5b08e637SChris Gerhard 
3632*5b08e637SChris Gerhard static struct shared *
3633*5b08e637SChris Gerhard create_shared(void *obj, void * (*obj_alloc)(void *obj),
3634*5b08e637SChris Gerhard 	void (*obj_free)(void *))
3635*5b08e637SChris Gerhard {
3636*5b08e637SChris Gerhard 	struct shared *out;
3637*5b08e637SChris Gerhard 
3638*5b08e637SChris Gerhard 	if ((out = xmalloc(sizeof (struct shared))) == NULL) {
3639*5b08e637SChris Gerhard 		return (NULL);
3640*5b08e637SChris Gerhard 	}
3641*5b08e637SChris Gerhard 	if ((out->obj = obj_alloc(obj)) == NULL) {
3642*5b08e637SChris Gerhard 		free(out);
3643*5b08e637SChris Gerhard 		return (NULL);
3644*5b08e637SChris Gerhard 	}
3645*5b08e637SChris Gerhard 	out->count = 1;
3646*5b08e637SChris Gerhard 	out->free = obj_free;
3647*5b08e637SChris Gerhard 
3648*5b08e637SChris Gerhard 	return (out);
3649*5b08e637SChris Gerhard }
3650*5b08e637SChris Gerhard 
3651*5b08e637SChris Gerhard static struct shared *
3652*5b08e637SChris Gerhard create_shared_str(char *str)
3653*5b08e637SChris Gerhard {
3654*5b08e637SChris Gerhard 	return (create_shared(str, (void *(*)(void *))strdup, free));
3655*5b08e637SChris Gerhard }
3656*5b08e637SChris Gerhard 
3657*5b08e637SChris Gerhard static struct shared *
3658*5b08e637SChris Gerhard dup_shared(struct shared *obj)
3659*5b08e637SChris Gerhard {
3660*5b08e637SChris Gerhard 	if (obj != NULL) {
3661*5b08e637SChris Gerhard 		obj->count++;
3662*5b08e637SChris Gerhard 	}
3663*5b08e637SChris Gerhard 	return (obj);
3664*5b08e637SChris Gerhard }
3665*5b08e637SChris Gerhard 
3666*5b08e637SChris Gerhard static void
3667*5b08e637SChris Gerhard rel_shared(struct shared *obj)
3668*5b08e637SChris Gerhard {
3669*5b08e637SChris Gerhard 	if (obj && (--obj->count) == 0) {
3670*5b08e637SChris Gerhard 		obj->free(obj->obj);
3671*5b08e637SChris Gerhard 		free(obj);
3672*5b08e637SChris Gerhard 	}
3673*5b08e637SChris Gerhard }
3674*5b08e637SChris Gerhard 
3675*5b08e637SChris Gerhard static void *
3676*5b08e637SChris Gerhard get_obj(struct shared *obj)
3677*5b08e637SChris Gerhard {
3678*5b08e637SChris Gerhard 	return (obj->obj);
3679*5b08e637SChris Gerhard }
3680