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