xref: /onnv-gate/usr/src/cmd/cron/cron.c (revision 11115:bcfb2bb98fca)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
55558Sbasabi  * Common Development and Distribution License (the "License").
65558Sbasabi  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*11115SNobutomo.Nakano@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
300Sstevel@tonic-gate /*	  All Rights Reserved	*/
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #ifdef lint
330Sstevel@tonic-gate /* make lint happy */
340Sstevel@tonic-gate #define	__EXTENSIONS__
350Sstevel@tonic-gate #endif
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #include <sys/contract/process.h>
380Sstevel@tonic-gate #include <sys/ctfs.h>
390Sstevel@tonic-gate #include <sys/param.h>
400Sstevel@tonic-gate #include <sys/resource.h>
410Sstevel@tonic-gate #include <sys/stat.h>
420Sstevel@tonic-gate #include <sys/task.h>
430Sstevel@tonic-gate #include <sys/time.h>
440Sstevel@tonic-gate #include <sys/types.h>
450Sstevel@tonic-gate #include <sys/utsname.h>
460Sstevel@tonic-gate #include <sys/wait.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate #include <security/pam_appl.h>
490Sstevel@tonic-gate 
500Sstevel@tonic-gate #include <alloca.h>
510Sstevel@tonic-gate #include <ctype.h>
520Sstevel@tonic-gate #include <deflt.h>
530Sstevel@tonic-gate #include <dirent.h>
540Sstevel@tonic-gate #include <errno.h>
550Sstevel@tonic-gate #include <fcntl.h>
560Sstevel@tonic-gate #include <grp.h>
570Sstevel@tonic-gate #include <libcontract.h>
580Sstevel@tonic-gate #include <libcontract_priv.h>
590Sstevel@tonic-gate #include <limits.h>
600Sstevel@tonic-gate #include <locale.h>
610Sstevel@tonic-gate #include <poll.h>
620Sstevel@tonic-gate #include <project.h>
630Sstevel@tonic-gate #include <pwd.h>
640Sstevel@tonic-gate #include <signal.h>
650Sstevel@tonic-gate #include <stdarg.h>
660Sstevel@tonic-gate #include <stdio.h>
670Sstevel@tonic-gate #include <stdlib.h>
680Sstevel@tonic-gate #include <string.h>
690Sstevel@tonic-gate #include <stropts.h>
700Sstevel@tonic-gate #include <time.h>
710Sstevel@tonic-gate #include <unistd.h>
728439SChris.Gerhard@sun.com #include <libzoneinfo.h>
730Sstevel@tonic-gate 
740Sstevel@tonic-gate #include "cron.h"
750Sstevel@tonic-gate 
760Sstevel@tonic-gate /*
770Sstevel@tonic-gate  * #define	DEBUG
780Sstevel@tonic-gate  */
790Sstevel@tonic-gate 
800Sstevel@tonic-gate #define	MAIL		"/usr/bin/mail"	/* mail program to use */
810Sstevel@tonic-gate #define	CONSOLE		"/dev/console"	/* where messages go when cron dies */
820Sstevel@tonic-gate 
830Sstevel@tonic-gate #define	TMPINFILE	"/tmp/crinXXXXXX"  /* file to put stdin in for cmd  */
840Sstevel@tonic-gate #define	TMPDIR		"/tmp"
850Sstevel@tonic-gate #define	PFX		"crout"
860Sstevel@tonic-gate #define	TMPOUTFILE	"/tmp/croutXXXXXX" /* file to place stdout, stderr */
870Sstevel@tonic-gate 
880Sstevel@tonic-gate #define	INMODE		00400		/* mode for stdin file	*/
890Sstevel@tonic-gate #define	OUTMODE		00600		/* mode for stdout file */
900Sstevel@tonic-gate #define	ISUID		S_ISUID		/* mode for verifing at jobs */
910Sstevel@tonic-gate 
920Sstevel@tonic-gate #define	INFINITY	2147483647L	/* upper bound on time	*/
930Sstevel@tonic-gate #define	CUSHION		180L
940Sstevel@tonic-gate #define	ZOMB		100		/* proc slot used for mailing output */
950Sstevel@tonic-gate 
960Sstevel@tonic-gate #define	JOBF		'j'
970Sstevel@tonic-gate #define	NICEF		'n'
980Sstevel@tonic-gate #define	USERF		'u'
990Sstevel@tonic-gate #define	WAITF		'w'
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate #define	BCHAR		'>'
1020Sstevel@tonic-gate #define	ECHAR		'<'
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate #define	DEFAULT		0
1050Sstevel@tonic-gate #define	LOAD		1
1060Sstevel@tonic-gate #define	QBUFSIZ		80
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate /* Defined actions for crabort() routine */
1090Sstevel@tonic-gate #define	NO_ACTION	000
1100Sstevel@tonic-gate #define	REMOVE_FIFO	001
1110Sstevel@tonic-gate #define	CONSOLE_MSG	002
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate #define	BADCD		"can't change directory to the crontab directory."
1140Sstevel@tonic-gate #define	NOREADDIR	"can't read the crontab directory."
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate #define	BADJOBOPEN	"unable to read your at job."
1170Sstevel@tonic-gate #define	BADSHELL	"because your login shell \
1180Sstevel@tonic-gate isn't /usr/bin/sh, you can't use cron."
1190Sstevel@tonic-gate 
1205558Sbasabi #define	BADSTAT		"can't access your crontab or at-job file. Resubmit it."
1210Sstevel@tonic-gate #define	BADPROJID	"can't set project id for your job."
1228439SChris.Gerhard@sun.com #define	CANTCDHOME	"can't change directory to %s.\
1230Sstevel@tonic-gate \nYour commands will not be executed."
1248439SChris.Gerhard@sun.com #define	CANTEXECSH	"unable to exec the shell, %s, for one of your \
1258439SChris.Gerhard@sun.com commands."
1268439SChris.Gerhard@sun.com #define	CANT_STR_LEN (sizeof (CANTEXECSH) > sizeof (CANTCDHOME) ? \
1278439SChris.Gerhard@sun.com 	sizeof (CANTEXECSH) : sizeof (CANTCDHOME))
1280Sstevel@tonic-gate #define	NOREAD		"can't read your crontab file.  Resubmit it."
1295558Sbasabi #define	BADTYPE		"crontab or at-job file is not a regular file.\n"
1300Sstevel@tonic-gate #define	NOSTDIN		"unable to create a standard input file for \
1310Sstevel@tonic-gate one of your crontab commands. \
1320Sstevel@tonic-gate \nThat command was not executed."
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate #define	NOTALLOWED	"you are not authorized to use cron.  Sorry."
1350Sstevel@tonic-gate #define	STDERRMSG	"\n\n********************************************\
1360Sstevel@tonic-gate *****\nCron: The previous message is the \
1370Sstevel@tonic-gate standard output and standard error \
1380Sstevel@tonic-gate \nof one of your cron commands.\n"
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate #define	STDOUTERR	"one of your commands generated output or errors, \
1410Sstevel@tonic-gate but cron was unable to mail you this output.\
1420Sstevel@tonic-gate \nRemember to redirect standard output and standard \
1430Sstevel@tonic-gate error for each of your commands."
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate #define	CLOCK_DRIFT	"clock time drifted backwards after event!\n"
1460Sstevel@tonic-gate #define	PIDERR		"unexpected pid returned %d (ignored)"
1470Sstevel@tonic-gate #define	CRONTABERR	"Subject: Your crontab file has an error in it\n\n"
1480Sstevel@tonic-gate #define	CRONOUT		"Subject: Output from \"cron\" command\n\n"
1490Sstevel@tonic-gate #define	MALLOCERR	"out of space, cannot create new string\n"
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate #define	DIDFORK didfork
1520Sstevel@tonic-gate #define	NOFORK !didfork
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate #define	MAILBUFLEN	(8*1024)
1550Sstevel@tonic-gate #define	LINELIMIT	80
1560Sstevel@tonic-gate #define	MAILBINITFREE	(MAILBUFLEN - (sizeof (cte_intro) - 1) \
1570Sstevel@tonic-gate 	    - (sizeof (cte_trail1) - 1) - (sizeof (cte_trail2) - 1) - 1)
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate #define	ERR_CRONTABENT	0	/* error in crontab file entry */
1600Sstevel@tonic-gate #define	ERR_UNIXERR	1	/* error in some system call */
1610Sstevel@tonic-gate #define	ERR_CANTEXECCRON 2	/* error setting up "cron" job environment */
1620Sstevel@tonic-gate #define	ERR_CANTEXECAT	3	/* error setting up "at" job environment */
1635558Sbasabi #define	ERR_NOTREG	4	/* error not a regular file */
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate #define	PROJECT		"project="
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate #define	MAX_LOST_CONTRACTS	2048	/* reset if this many failed abandons */
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate #define	FORMAT	"%a %b %e %H:%M:%S %Y"
1700Sstevel@tonic-gate static char	timebuf[80];
1710Sstevel@tonic-gate 
172*11115SNobutomo.Nakano@Sun.COM static struct message msgbuf;
173*11115SNobutomo.Nakano@Sun.COM 
1748439SChris.Gerhard@sun.com struct shared {
1758439SChris.Gerhard@sun.com 	int count;			/* usage count */
1768439SChris.Gerhard@sun.com 	void (*free)(void *obj);	/* routine that will free obj */
1778439SChris.Gerhard@sun.com 	void *obj;			/* object */
1788439SChris.Gerhard@sun.com };
1798439SChris.Gerhard@sun.com 
1800Sstevel@tonic-gate struct event {
1810Sstevel@tonic-gate 	time_t time;	/* time of the event	*/
1820Sstevel@tonic-gate 	short etype;	/* what type of event; 0=cron, 1=at	*/
1830Sstevel@tonic-gate 	char *cmd;	/* command for cron, job name for at	*/
1840Sstevel@tonic-gate 	struct usr *u;	/* ptr to the owner (usr) of this event	*/
1850Sstevel@tonic-gate 	struct event *link;	/* ptr to another event for this user */
1860Sstevel@tonic-gate 	union {
1870Sstevel@tonic-gate 		struct { /* for crontab events */
1880Sstevel@tonic-gate 			char *minute;	/*  (these	*/
1890Sstevel@tonic-gate 			char *hour;	/*   fields	*/
1900Sstevel@tonic-gate 			char *daymon;	/*   are	*/
1910Sstevel@tonic-gate 			char *month;	/*   from	*/
1920Sstevel@tonic-gate 			char *dayweek;	/*   crontab)	*/
1930Sstevel@tonic-gate 			char *input;	/* ptr to stdin	*/
1948439SChris.Gerhard@sun.com 			struct shared *tz;	/* timezone of this event */
1958439SChris.Gerhard@sun.com 			struct shared *home;	/* directory for this event */
1968439SChris.Gerhard@sun.com 			struct shared *shell;	/* shell for this event */
1970Sstevel@tonic-gate 		} ct;
1980Sstevel@tonic-gate 		struct { /* for at events */
1990Sstevel@tonic-gate 			short exists;	/* for revising at events	*/
2000Sstevel@tonic-gate 			int eventid;	/* for el_remove-ing at events	*/
2010Sstevel@tonic-gate 		} at;
2020Sstevel@tonic-gate 	} of;
2030Sstevel@tonic-gate };
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate struct usr {
2060Sstevel@tonic-gate 	char *name;	/* name of user (e.g. "root")	*/
2070Sstevel@tonic-gate 	char *home;	/* home directory for user	*/
2080Sstevel@tonic-gate 	uid_t uid;	/* user id	*/
2090Sstevel@tonic-gate 	gid_t gid;	/* group id	*/
2100Sstevel@tonic-gate 	int aruncnt;	/* counter for running jobs per uid */
2110Sstevel@tonic-gate 	int cruncnt;	/* counter for running cron jobs per uid */
2120Sstevel@tonic-gate 	int ctid;	/* for el_remove-ing crontab events */
2130Sstevel@tonic-gate 	short ctexists;	/* for revising crontab events	*/
2140Sstevel@tonic-gate 	struct event *ctevents;	/* list of this usr's crontab events */
2150Sstevel@tonic-gate 	struct event *atevents;	/* list of this usr's at events */
2160Sstevel@tonic-gate 	struct usr *nextusr;
2170Sstevel@tonic-gate };	/* ptr to next user	*/
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate static struct	queue
2200Sstevel@tonic-gate {
2210Sstevel@tonic-gate 	int njob;	/* limit */
2220Sstevel@tonic-gate 	int nice;	/* nice for execution */
2230Sstevel@tonic-gate 	int nwait;	/* wait time to next execution attempt */
2240Sstevel@tonic-gate 	int nrun;	/* number running */
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate 	qd = {100, 2, 60},		/* default values for queue defs */
2270Sstevel@tonic-gate 	qt[NQUEUE];
2280Sstevel@tonic-gate static struct	queue	qq;
2290Sstevel@tonic-gate 
230801Sbasabi static struct runinfo
2310Sstevel@tonic-gate {
2320Sstevel@tonic-gate 	pid_t	pid;
2330Sstevel@tonic-gate 	short	que;
2340Sstevel@tonic-gate 	struct  usr *rusr;	/* pointer to usr struct */
2350Sstevel@tonic-gate 	char	*outfile;	/* file where stdout & stderr are trapped */
2360Sstevel@tonic-gate 	short	jobtype;	/* what type of event: 0=cron, 1=at */
2370Sstevel@tonic-gate 	char	*jobname;	/* command for "cron", jobname for "at" */
2380Sstevel@tonic-gate 	int	mailwhendone;	/* 1 = send mail even if no ouptut */
239801Sbasabi 	struct runinfo *next;
240801Sbasabi }	*rthead;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate static struct miscpid {
2430Sstevel@tonic-gate 	pid_t		pid;
2440Sstevel@tonic-gate 	struct miscpid	*next;
2450Sstevel@tonic-gate }	*miscpid_head;
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate static pid_t cron_pid;	/* own pid */
2480Sstevel@tonic-gate static char didfork = 0; /* flag to see if I'm process group leader */
2490Sstevel@tonic-gate static int msgfd;	/* file descriptor for fifo queue */
2500Sstevel@tonic-gate static int ecid = 1;	/* event class id for el_remove(); MUST be set to 1 */
2510Sstevel@tonic-gate static int delayed;	/* is job being rescheduled or did it run first time */
2520Sstevel@tonic-gate static int cwd;		/* current working directory */
2530Sstevel@tonic-gate static struct event *next_event;	/* the next event to execute	*/
2540Sstevel@tonic-gate static struct usr *uhead;		/* ptr to the list of users	*/
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate /* Variables for error handling at reading crontabs. */
2570Sstevel@tonic-gate static char cte_intro[] = "Line(s) with errors:\n\n";
2580Sstevel@tonic-gate static char cte_trail1[] = "\nMax number of errors encountered.";
2590Sstevel@tonic-gate static char cte_trail2[] = " Evaluation of crontab aborted.\n";
2600Sstevel@tonic-gate static int cte_free = MAILBINITFREE;	/* Free buffer space */
2610Sstevel@tonic-gate static char *cte_text = NULL;		/* Text buffer pointer */
2620Sstevel@tonic-gate static char *cte_lp;			/* Next free line in cte_text */
2630Sstevel@tonic-gate static int cte_nvalid;			/* Valid lines found */
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate /* user's default environment for the shell */
2660Sstevel@tonic-gate #define	ROOTPATH	"PATH=/usr/sbin:/usr/bin"
2670Sstevel@tonic-gate #define	NONROOTPATH	"PATH=/usr/bin:"
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate static char *Def_supath	= NULL;
2700Sstevel@tonic-gate static char *Def_path		= NULL;
2710Sstevel@tonic-gate static char path[LINE_MAX]	= "PATH=";
2720Sstevel@tonic-gate static char supath[LINE_MAX]	= "PATH=";
2738439SChris.Gerhard@sun.com static char homedir[LINE_MAX]	= ENV_HOME;
2740Sstevel@tonic-gate static char logname[LINE_MAX]	= "LOGNAME=";
2758439SChris.Gerhard@sun.com static char tzone[LINE_MAX]	= ENV_TZ;
2760Sstevel@tonic-gate static char *envinit[] = {
2770Sstevel@tonic-gate 	homedir,
2780Sstevel@tonic-gate 	logname,
2790Sstevel@tonic-gate 	ROOTPATH,
2800Sstevel@tonic-gate 	"SHELL=/usr/bin/sh",
2810Sstevel@tonic-gate 	tzone,
2820Sstevel@tonic-gate 	NULL
2830Sstevel@tonic-gate };
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate extern char **environ;
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate #define	DEFTZ		"GMT"
2880Sstevel@tonic-gate static	int	log = 0;
2890Sstevel@tonic-gate static	char	hzname[10];
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate static void cronend(int);
2920Sstevel@tonic-gate static void thaw_handler(int);
2930Sstevel@tonic-gate static void child_handler(int);
2940Sstevel@tonic-gate static void child_sigreset(void);
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate static void mod_ctab(char *, time_t);
2970Sstevel@tonic-gate static void mod_atjob(char *, time_t);
2980Sstevel@tonic-gate static void add_atevent(struct usr *, char *, time_t, int);
2990Sstevel@tonic-gate static void rm_ctevents(struct usr *);
3000Sstevel@tonic-gate static void cleanup(struct runinfo *rn, int r);
3010Sstevel@tonic-gate static void crabort(char *, int);
3020Sstevel@tonic-gate static void msg(char *fmt, ...);
3037879SSerge.Dussud@Sun.COM static void ignore_msg(char *, char *, struct event *);
3040Sstevel@tonic-gate static void logit(int, struct runinfo *, int);
3050Sstevel@tonic-gate static void parsqdef(char *);
3060Sstevel@tonic-gate static void defaults();
3070Sstevel@tonic-gate static void initialize(int);
3080Sstevel@tonic-gate static void quedefs(int);
3090Sstevel@tonic-gate static int idle(long);
3100Sstevel@tonic-gate static struct usr *find_usr(char *);
3110Sstevel@tonic-gate static int ex(struct event *e);
3125558Sbasabi static void read_dirs(int);
3130Sstevel@tonic-gate static void mail(char *, char *, int);
3140Sstevel@tonic-gate static char *next_field(int, int);
3150Sstevel@tonic-gate static void readcron(struct usr *, time_t);
3160Sstevel@tonic-gate static int next_ge(int, char *);
3170Sstevel@tonic-gate static void free_if_unused(struct usr *);
3180Sstevel@tonic-gate static void del_atjob(char *, char *);
3190Sstevel@tonic-gate static void del_ctab(char *);
3200Sstevel@tonic-gate static void resched(int);
3210Sstevel@tonic-gate static int msg_wait(long);
3220Sstevel@tonic-gate static struct runinfo *rinfo_get(pid_t);
3230Sstevel@tonic-gate static void rinfo_free(struct runinfo *rp);
3240Sstevel@tonic-gate static void mail_result(struct usr *p, struct runinfo *pr, size_t filesize);
3250Sstevel@tonic-gate static time_t next_time(struct event *, time_t);
3260Sstevel@tonic-gate static time_t get_switching_time(int, time_t);
3270Sstevel@tonic-gate static time_t xmktime(struct tm *);
3280Sstevel@tonic-gate static void process_msg(struct message *, time_t);
3290Sstevel@tonic-gate static void reap_child(void);
3300Sstevel@tonic-gate static void miscpid_insert(pid_t);
3310Sstevel@tonic-gate static int miscpid_delete(pid_t);
332523Sbasabi static void contract_set_template(void);
333523Sbasabi static void contract_clear_template(void);
3340Sstevel@tonic-gate static void contract_abandon_latest(pid_t);
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate static void cte_init(void);
3370Sstevel@tonic-gate static void cte_add(int, char *);
3380Sstevel@tonic-gate static void cte_valid(void);
3390Sstevel@tonic-gate static int cte_istoomany(void);
3400Sstevel@tonic-gate static void cte_sendmail(char *);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate static int set_user_cred(const struct usr *, struct project *);
3430Sstevel@tonic-gate 
3448439SChris.Gerhard@sun.com static struct shared *create_shared_str(char *str);
3458439SChris.Gerhard@sun.com static struct shared *dup_shared(struct shared *obj);
3468439SChris.Gerhard@sun.com static void rel_shared(struct shared *obj);
3478439SChris.Gerhard@sun.com static void *get_obj(struct shared *obj);
3480Sstevel@tonic-gate /*
3490Sstevel@tonic-gate  * last_time is set immediately prior to exection of an event (via ex())
3500Sstevel@tonic-gate  * to indicate the last time an event was executed.  This was (surely)
3510Sstevel@tonic-gate  * it's original intended use.
3520Sstevel@tonic-gate  */
3530Sstevel@tonic-gate static time_t last_time, init_time, t_old;
3547879SSerge.Dussud@Sun.COM static int reset_needed; /* set to 1 when cron(1M) needs to re-initialize */
3550Sstevel@tonic-gate 
356*11115SNobutomo.Nakano@Sun.COM static int		refresh;
357*11115SNobutomo.Nakano@Sun.COM static sigset_t		defmask, sigmask;
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate /*
3600Sstevel@tonic-gate  * BSM hooks
3610Sstevel@tonic-gate  */
3620Sstevel@tonic-gate extern int	audit_cron_session(char *, char *, uid_t, gid_t, char *);
3630Sstevel@tonic-gate extern void	audit_cron_new_job(char *, int, void *);
3640Sstevel@tonic-gate extern void	audit_cron_bad_user(char *);
3650Sstevel@tonic-gate extern void	audit_cron_user_acct_expired(char *);
3660Sstevel@tonic-gate extern int	audit_cron_create_anc_file(char *, char *, char *, uid_t);
3670Sstevel@tonic-gate extern int	audit_cron_delete_anc_file(char *, char *);
3680Sstevel@tonic-gate extern int	audit_cron_is_anc_name(char *);
3690Sstevel@tonic-gate extern int	audit_cron_mode();
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate static int cron_conv(int, struct pam_message **,
3720Sstevel@tonic-gate 		struct pam_response **, void *);
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate static struct pam_conv pam_conv = {cron_conv, NULL};
3750Sstevel@tonic-gate static pam_handle_t *pamh;	/* Authentication handle */
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate /*
3780Sstevel@tonic-gate  * Function to help check a user's credentials.
3790Sstevel@tonic-gate  */
3800Sstevel@tonic-gate 
3815558Sbasabi static int verify_user_cred(struct usr *u);
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate /*
3840Sstevel@tonic-gate  * Values returned by verify_user_cred and set_user_cred:
3850Sstevel@tonic-gate  */
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate #define	VUC_OK		0
3880Sstevel@tonic-gate #define	VUC_BADUSER	1
3890Sstevel@tonic-gate #define	VUC_NOTINGROUP	2
3900Sstevel@tonic-gate #define	VUC_EXPIRED	3
3910Sstevel@tonic-gate #define	VUC_NEW_AUTH	4
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate /*
3940Sstevel@tonic-gate  * Modes of process_anc_files function
3950Sstevel@tonic-gate  */
3960Sstevel@tonic-gate #define	CRON_ANC_DELETE	1
3970Sstevel@tonic-gate #define	CRON_ANC_CREATE	0
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate /*
4000Sstevel@tonic-gate  * Functions to remove a user or job completely from the running database.
4010Sstevel@tonic-gate  */
4020Sstevel@tonic-gate static void clean_out_atjobs(struct usr *u);
4030Sstevel@tonic-gate static void clean_out_ctab(struct usr *u);
4040Sstevel@tonic-gate static void clean_out_user(struct usr *u);
4050Sstevel@tonic-gate static void cron_unlink(char *name);
4060Sstevel@tonic-gate static void process_anc_files(int);
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate /*
4090Sstevel@tonic-gate  * functions in elm.c
4100Sstevel@tonic-gate  */
4110Sstevel@tonic-gate extern void el_init(int, time_t, time_t, int);
4127879SSerge.Dussud@Sun.COM extern int el_add(void *, time_t, int);
4130Sstevel@tonic-gate extern void el_remove(int, int);
4140Sstevel@tonic-gate extern int el_empty(void);
4150Sstevel@tonic-gate extern void *el_first(void);
4160Sstevel@tonic-gate extern void el_delete(void);
4170Sstevel@tonic-gate 
4185558Sbasabi static int valid_entry(char *, int);
4195558Sbasabi static struct usr *create_ulist(char *, int);
4205558Sbasabi static void init_cronevent(char *, int);
4215558Sbasabi static void init_atevent(char *, time_t, int, int);
4225558Sbasabi static void update_atevent(struct usr *, char *, time_t, int);
4235558Sbasabi 
4240Sstevel@tonic-gate int
main(int argc,char * argv[])4250Sstevel@tonic-gate main(int argc, char *argv[])
4260Sstevel@tonic-gate {
4270Sstevel@tonic-gate 	time_t t;
4280Sstevel@tonic-gate 	time_t ne_time;		/* amt of time until next event execution */
4290Sstevel@tonic-gate 	time_t newtime, lastmtime = 0L;
4300Sstevel@tonic-gate 	struct usr *u;
4310Sstevel@tonic-gate 	struct event *e, *e2, *eprev;
4320Sstevel@tonic-gate 	struct stat buf;
4330Sstevel@tonic-gate 	pid_t rfork;
4340Sstevel@tonic-gate 	struct sigaction act;
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	/*
4377879SSerge.Dussud@Sun.COM 	 * reset_needed is set to 1 whenever el_add() finds out that a cron
4387879SSerge.Dussud@Sun.COM 	 * job is scheduled to be run before the time when cron(1M) daemon
4397879SSerge.Dussud@Sun.COM 	 * initialized.
4407879SSerge.Dussud@Sun.COM 	 * Other cases where a reset is needed is when ex() finds that the
4417879SSerge.Dussud@Sun.COM 	 * event to be executed is being run at the wrong time, or when idle()
4427879SSerge.Dussud@Sun.COM 	 * determines that time was reset.
4430Sstevel@tonic-gate 	 * We immediately return to the top of the while (TRUE) loop in
4447879SSerge.Dussud@Sun.COM 	 * main() where the event list is cleared and rebuilt, and reset_needed
4450Sstevel@tonic-gate 	 * is set back to 0.
4460Sstevel@tonic-gate 	 */
4477879SSerge.Dussud@Sun.COM 	reset_needed = 0;
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 	/*
4500Sstevel@tonic-gate 	 * Only the privileged user can run this command.
4510Sstevel@tonic-gate 	 */
4520Sstevel@tonic-gate 	if (getuid() != 0)
4530Sstevel@tonic-gate 		crabort(NOTALLOWED, 0);
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate begin:
4560Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
4570Sstevel@tonic-gate 	/* fork unless 'nofork' is specified */
4580Sstevel@tonic-gate 	if ((argc <= 1) || (strcmp(argv[1], "nofork"))) {
4590Sstevel@tonic-gate 		if (rfork = fork()) {
4600Sstevel@tonic-gate 			if (rfork == (pid_t)-1) {
4610Sstevel@tonic-gate 				(void) sleep(30);
4620Sstevel@tonic-gate 				goto begin;
4630Sstevel@tonic-gate 			}
4640Sstevel@tonic-gate 			return (0);
4650Sstevel@tonic-gate 		}
4660Sstevel@tonic-gate 		didfork++;
4670Sstevel@tonic-gate 		(void) setpgrp();	/* detach cron from console */
4680Sstevel@tonic-gate 	}
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	(void) umask(022);
4710Sstevel@tonic-gate 	(void) signal(SIGHUP, SIG_IGN);
4720Sstevel@tonic-gate 	(void) signal(SIGINT, SIG_IGN);
4730Sstevel@tonic-gate 	(void) signal(SIGQUIT, SIG_IGN);
4740Sstevel@tonic-gate 	(void) signal(SIGTERM, cronend);
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 	defaults();
4770Sstevel@tonic-gate 	initialize(1);
4780Sstevel@tonic-gate 	quedefs(DEFAULT);	/* load default queue definitions */
4790Sstevel@tonic-gate 	cron_pid = getpid();
4800Sstevel@tonic-gate 	msg("*** cron started ***   pid = %d", cron_pid);
481*11115SNobutomo.Nakano@Sun.COM 
482*11115SNobutomo.Nakano@Sun.COM 	/* setup THAW handler */
483*11115SNobutomo.Nakano@Sun.COM 	act.sa_handler = thaw_handler;
484*11115SNobutomo.Nakano@Sun.COM 	act.sa_flags = 0;
485*11115SNobutomo.Nakano@Sun.COM 	(void) sigemptyset(&act.sa_mask);
486*11115SNobutomo.Nakano@Sun.COM 	(void) sigaction(SIGTHAW, &act, NULL);
487*11115SNobutomo.Nakano@Sun.COM 
488*11115SNobutomo.Nakano@Sun.COM 	/* setup CHLD handler */
4890Sstevel@tonic-gate 	act.sa_handler = child_handler;
4900Sstevel@tonic-gate 	act.sa_flags = 0;
4910Sstevel@tonic-gate 	(void) sigemptyset(&act.sa_mask);
4920Sstevel@tonic-gate 	(void) sigaddset(&act.sa_mask, SIGCLD);
4930Sstevel@tonic-gate 	(void) sigaction(SIGCLD, &act, NULL);
494*11115SNobutomo.Nakano@Sun.COM 
495*11115SNobutomo.Nakano@Sun.COM 	(void) sigemptyset(&defmask);
496*11115SNobutomo.Nakano@Sun.COM 	(void) sigemptyset(&sigmask);
497*11115SNobutomo.Nakano@Sun.COM 	(void) sigaddset(&sigmask, SIGCLD);
498*11115SNobutomo.Nakano@Sun.COM 	(void) sigaddset(&sigmask, SIGTHAW);
499*11115SNobutomo.Nakano@Sun.COM 	(void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
5000Sstevel@tonic-gate 
5017879SSerge.Dussud@Sun.COM 	t_old = init_time;
5020Sstevel@tonic-gate 	last_time = t_old;
5030Sstevel@tonic-gate 	for (;;) {		/* MAIN LOOP */
5040Sstevel@tonic-gate 		t = time(NULL);
5057879SSerge.Dussud@Sun.COM 		if ((t_old > t) || (t-last_time > CUSHION) || reset_needed) {
5067879SSerge.Dussud@Sun.COM 			reset_needed = 0;
507*11115SNobutomo.Nakano@Sun.COM 			/*
508*11115SNobutomo.Nakano@Sun.COM 			 * the time was set backwards or forward or
509*11115SNobutomo.Nakano@Sun.COM 			 * refresh is requested.
510*11115SNobutomo.Nakano@Sun.COM 			 */
511*11115SNobutomo.Nakano@Sun.COM 			if (refresh)
512*11115SNobutomo.Nakano@Sun.COM 				msg("re-scheduling jobs");
513*11115SNobutomo.Nakano@Sun.COM 			else
514*11115SNobutomo.Nakano@Sun.COM 				msg("time was reset, re-initializing");
5150Sstevel@tonic-gate 			el_delete();
5160Sstevel@tonic-gate 			u = uhead;
5170Sstevel@tonic-gate 			while (u != NULL) {
5180Sstevel@tonic-gate 				rm_ctevents(u);
5190Sstevel@tonic-gate 				e = u->atevents;
5200Sstevel@tonic-gate 				while (e != NULL) {
5210Sstevel@tonic-gate 					free(e->cmd);
5220Sstevel@tonic-gate 					e2 = e->link;
5230Sstevel@tonic-gate 					free(e);
5240Sstevel@tonic-gate 					e = e2;
5250Sstevel@tonic-gate 				}
5260Sstevel@tonic-gate 				u->atevents = NULL;
5270Sstevel@tonic-gate 				u = u->nextusr;
5280Sstevel@tonic-gate 			}
5290Sstevel@tonic-gate 			(void) close(msgfd);
5300Sstevel@tonic-gate 			initialize(0);
5310Sstevel@tonic-gate 			t = time(NULL);
5320Sstevel@tonic-gate 			last_time = t;
5337879SSerge.Dussud@Sun.COM 			/*
5347879SSerge.Dussud@Sun.COM 			 * reset_needed might have been set in the functions
5357879SSerge.Dussud@Sun.COM 			 * call path from initialize()
5367879SSerge.Dussud@Sun.COM 			 */
5377879SSerge.Dussud@Sun.COM 			if (reset_needed) {
5387879SSerge.Dussud@Sun.COM 				continue;
5397879SSerge.Dussud@Sun.COM 			}
5400Sstevel@tonic-gate 		}
5410Sstevel@tonic-gate 		t_old = t;
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 		if (next_event == NULL && !el_empty()) {
5440Sstevel@tonic-gate 			next_event = (struct event *)el_first();
5450Sstevel@tonic-gate 		}
5460Sstevel@tonic-gate 		if (next_event == NULL) {
5470Sstevel@tonic-gate 			ne_time = INFINITY;
5480Sstevel@tonic-gate 		} else {
5490Sstevel@tonic-gate 			ne_time = next_event->time - t;
5500Sstevel@tonic-gate #ifdef DEBUG
5510Sstevel@tonic-gate 			cftime(timebuf, "%C", &next_event->time);
5520Sstevel@tonic-gate 			(void) fprintf(stderr, "next_time=%ld %s\n",
5535558Sbasabi 			    next_event->time, timebuf);
5540Sstevel@tonic-gate #endif
5550Sstevel@tonic-gate 		}
5560Sstevel@tonic-gate 		if (ne_time > 0) {
5577879SSerge.Dussud@Sun.COM 			/*
5587879SSerge.Dussud@Sun.COM 			 * reset_needed may be set in the functions call path
5597879SSerge.Dussud@Sun.COM 			 * from idle()
5607879SSerge.Dussud@Sun.COM 			 */
5617879SSerge.Dussud@Sun.COM 			if (idle(ne_time) || reset_needed) {
5627879SSerge.Dussud@Sun.COM 				reset_needed = 1;
5630Sstevel@tonic-gate 				continue;
5647879SSerge.Dussud@Sun.COM 			}
5650Sstevel@tonic-gate 		}
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 		if (stat(QUEDEFS, &buf)) {
5680Sstevel@tonic-gate 			msg("cannot stat QUEDEFS file");
5690Sstevel@tonic-gate 		} else if (lastmtime != buf.st_mtime) {
5700Sstevel@tonic-gate 			quedefs(LOAD);
5710Sstevel@tonic-gate 			lastmtime = buf.st_mtime;
5720Sstevel@tonic-gate 		}
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 		last_time = next_event->time; /* save execution time */
5750Sstevel@tonic-gate 
5767879SSerge.Dussud@Sun.COM 		/*
5777879SSerge.Dussud@Sun.COM 		 * reset_needed may be set in the functions call path
5787879SSerge.Dussud@Sun.COM 		 * from ex()
5797879SSerge.Dussud@Sun.COM 		 */
5807879SSerge.Dussud@Sun.COM 		if (ex(next_event) || reset_needed) {
5817879SSerge.Dussud@Sun.COM 			reset_needed = 1;
5820Sstevel@tonic-gate 			continue;
5837879SSerge.Dussud@Sun.COM 		}
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 		switch (next_event->etype) {
5860Sstevel@tonic-gate 		case CRONEVENT:
5870Sstevel@tonic-gate 			/* add cronevent back into the main event list */
5880Sstevel@tonic-gate 			if (delayed) {
5890Sstevel@tonic-gate 				delayed = 0;
5900Sstevel@tonic-gate 				break;
5910Sstevel@tonic-gate 			}
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 			/*
5940Sstevel@tonic-gate 			 * check if time(0)< last_time. if so, then the
5950Sstevel@tonic-gate 			 * system clock has gone backwards. to prevent this
5960Sstevel@tonic-gate 			 * job from being started twice, we reschedule this
5970Sstevel@tonic-gate 			 * job for the >>next time after last_time<<, and
5980Sstevel@tonic-gate 			 * then set next_event->time to this. note that
5990Sstevel@tonic-gate 			 * crontab's resolution is 1 minute.
6000Sstevel@tonic-gate 			 */
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 			if (last_time > time(NULL)) {
6030Sstevel@tonic-gate 				msg(CLOCK_DRIFT);
6040Sstevel@tonic-gate 				/*
6050Sstevel@tonic-gate 				 * bump up to next 30 second
6060Sstevel@tonic-gate 				 * increment
6070Sstevel@tonic-gate 				 * 1 <= newtime <= 30
6080Sstevel@tonic-gate 				 */
6090Sstevel@tonic-gate 				newtime = 30 - (last_time % 30);
6100Sstevel@tonic-gate 				newtime += last_time;
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 				/*
6130Sstevel@tonic-gate 				 * get the next scheduled event,
6140Sstevel@tonic-gate 				 * not the one that we just
6150Sstevel@tonic-gate 				 * kicked off!
6160Sstevel@tonic-gate 				 */
6170Sstevel@tonic-gate 				next_event->time =
6185558Sbasabi 				    next_time(next_event, newtime);
6190Sstevel@tonic-gate 				t_old = time(NULL);
6200Sstevel@tonic-gate 			} else {
6210Sstevel@tonic-gate 				next_event->time =
6225558Sbasabi 				    next_time(next_event, (time_t)0);
6230Sstevel@tonic-gate 			}
6240Sstevel@tonic-gate #ifdef DEBUG
6250Sstevel@tonic-gate 			cftime(timebuf, "%C", &next_event->time);
6260Sstevel@tonic-gate 			(void) fprintf(stderr,
6275558Sbasabi 			    "pushing back cron event %s at %ld (%s)\n",
6285558Sbasabi 			    next_event->cmd, next_event->time, timebuf);
6290Sstevel@tonic-gate #endif
6300Sstevel@tonic-gate 
6317879SSerge.Dussud@Sun.COM 			switch (el_add(next_event, next_event->time,
6327879SSerge.Dussud@Sun.COM 			    (next_event->u)->ctid)) {
6337879SSerge.Dussud@Sun.COM 			case -1:
6347879SSerge.Dussud@Sun.COM 				ignore_msg("main", "cron", next_event);
6357879SSerge.Dussud@Sun.COM 				break;
6367879SSerge.Dussud@Sun.COM 			case -2: /* event time lower than init time */
6377879SSerge.Dussud@Sun.COM 				reset_needed = 1;
6387879SSerge.Dussud@Sun.COM 				break;
6397879SSerge.Dussud@Sun.COM 			}
6400Sstevel@tonic-gate 			break;
6410Sstevel@tonic-gate 		default:
6420Sstevel@tonic-gate 			/* remove at or batch job from system */
6430Sstevel@tonic-gate 			if (delayed) {
6440Sstevel@tonic-gate 				delayed = 0;
6450Sstevel@tonic-gate 				break;
6460Sstevel@tonic-gate 			}
6470Sstevel@tonic-gate 			eprev = NULL;
6480Sstevel@tonic-gate 			e = (next_event->u)->atevents;
6490Sstevel@tonic-gate 			while (e != NULL) {
6500Sstevel@tonic-gate 				if (e == next_event) {
6510Sstevel@tonic-gate 					if (eprev == NULL)
6520Sstevel@tonic-gate 						(e->u)->atevents = e->link;
6530Sstevel@tonic-gate 					else
6540Sstevel@tonic-gate 						eprev->link = e->link;
6550Sstevel@tonic-gate 					free(e->cmd);
6560Sstevel@tonic-gate 					free(e);
6570Sstevel@tonic-gate 					break;
6580Sstevel@tonic-gate 				} else {
6590Sstevel@tonic-gate 					eprev = e;
6600Sstevel@tonic-gate 					e = e->link;
6610Sstevel@tonic-gate 				}
6620Sstevel@tonic-gate 			}
6630Sstevel@tonic-gate 			break;
6640Sstevel@tonic-gate 		}
6650Sstevel@tonic-gate 		next_event = NULL;
6660Sstevel@tonic-gate 	}
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	/*NOTREACHED*/
6690Sstevel@tonic-gate }
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate static void
initialize(int firstpass)6720Sstevel@tonic-gate initialize(int firstpass)
6730Sstevel@tonic-gate {
6740Sstevel@tonic-gate #ifdef DEBUG
6750Sstevel@tonic-gate 	(void) fprintf(stderr, "in initialize\n");
6760Sstevel@tonic-gate #endif
6770Sstevel@tonic-gate 	if (firstpass) {
6780Sstevel@tonic-gate 		/* for mail(1), make sure messages come from root */
6790Sstevel@tonic-gate 		if (putenv("LOGNAME=root") != 0) {
6800Sstevel@tonic-gate 			crabort("cannot expand env variable",
6815558Sbasabi 			    REMOVE_FIFO|CONSOLE_MSG);
6820Sstevel@tonic-gate 		}
6830Sstevel@tonic-gate 		if (access(FIFO, R_OK) == -1) {
6840Sstevel@tonic-gate 			if (errno == ENOENT) {
6850Sstevel@tonic-gate 				if (mknod(FIFO, S_IFIFO|0600, 0) != 0)
6860Sstevel@tonic-gate 					crabort("cannot create fifo queue",
6875558Sbasabi 					    REMOVE_FIFO|CONSOLE_MSG);
6880Sstevel@tonic-gate 			} else {
6890Sstevel@tonic-gate 				if (NOFORK) {
6900Sstevel@tonic-gate 					/* didn't fork... init(1M) is waiting */
6910Sstevel@tonic-gate 					(void) sleep(60);
6920Sstevel@tonic-gate 				}
6930Sstevel@tonic-gate 				perror("FIFO");
6940Sstevel@tonic-gate 				crabort("cannot access fifo queue",
6955558Sbasabi 				    REMOVE_FIFO|CONSOLE_MSG);
6960Sstevel@tonic-gate 			}
6970Sstevel@tonic-gate 		} else {
6980Sstevel@tonic-gate 			if (NOFORK) {
6990Sstevel@tonic-gate 				/* didn't fork... init(1M) is waiting */
7000Sstevel@tonic-gate 				(void) sleep(60);
7010Sstevel@tonic-gate 				/*
7020Sstevel@tonic-gate 				 * the wait is painful, but we don't want
7030Sstevel@tonic-gate 				 * init respawning this quickly
7040Sstevel@tonic-gate 				 */
7050Sstevel@tonic-gate 			}
7060Sstevel@tonic-gate 			crabort("cannot start cron; FIFO exists", CONSOLE_MSG);
7070Sstevel@tonic-gate 		}
7080Sstevel@tonic-gate 	}
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	if ((msgfd = open(FIFO, O_RDWR)) < 0) {
7110Sstevel@tonic-gate 		perror("! open");
7120Sstevel@tonic-gate 		crabort("cannot open fifo queue", REMOVE_FIFO|CONSOLE_MSG);
7130Sstevel@tonic-gate 	}
7140Sstevel@tonic-gate 
7157879SSerge.Dussud@Sun.COM 	init_time = time(NULL);
7167879SSerge.Dussud@Sun.COM 	el_init(8, init_time, (time_t)(60*60*24), 10);
7177879SSerge.Dussud@Sun.COM 
7188439SChris.Gerhard@sun.com 	init_time = time(NULL);
7198439SChris.Gerhard@sun.com 	el_init(8, init_time, (time_t)(60*60*24), 10);
7208439SChris.Gerhard@sun.com 
7210Sstevel@tonic-gate 	/*
7220Sstevel@tonic-gate 	 * read directories, create users list, and add events to the
7230Sstevel@tonic-gate 	 * main event list. Only zero user list on firstpass.
7240Sstevel@tonic-gate 	 */
7250Sstevel@tonic-gate 	if (firstpass)
7260Sstevel@tonic-gate 		uhead = NULL;
7275558Sbasabi 	read_dirs(firstpass);
7280Sstevel@tonic-gate 	next_event = NULL;
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 	if (!firstpass)
7310Sstevel@tonic-gate 		return;
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate 	/* stdout is log file */
7340Sstevel@tonic-gate 	if (freopen(ACCTFILE, "a", stdout) == NULL)
7350Sstevel@tonic-gate 		(void) fprintf(stderr, "cannot open %s\n", ACCTFILE);
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 	/* log should be root-only */
7380Sstevel@tonic-gate 	(void) fchmod(1, S_IRUSR|S_IWUSR);
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	/* stderr also goes to ACCTFILE */
7410Sstevel@tonic-gate 	(void) close(fileno(stderr));
7420Sstevel@tonic-gate 	(void) dup(1);
7430Sstevel@tonic-gate 	/* null for stdin */
7440Sstevel@tonic-gate 	(void) freopen("/dev/null", "r", stdin);
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	contract_set_template();
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate static void
read_dirs(int first)7505558Sbasabi read_dirs(int first)
7510Sstevel@tonic-gate {
7525558Sbasabi 	DIR		*dir;
7535558Sbasabi 	struct dirent	*dp;
7545558Sbasabi 	char		*ptr;
7555558Sbasabi 	int		jobtype;
7565558Sbasabi 	time_t		tim;
7575558Sbasabi 
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate 	if (chdir(CRONDIR) == -1)
7600Sstevel@tonic-gate 		crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG);
7610Sstevel@tonic-gate 	cwd = CRON;
7620Sstevel@tonic-gate 	if ((dir = opendir(".")) == NULL)
7630Sstevel@tonic-gate 		crabort(NOREADDIR, REMOVE_FIFO|CONSOLE_MSG);
7645558Sbasabi 	while ((dp = readdir(dir)) != NULL) {
7655558Sbasabi 		if (!valid_entry(dp->d_name, CRONEVENT))
7665558Sbasabi 			continue;
7675558Sbasabi 		init_cronevent(dp->d_name, first);
7685558Sbasabi 	}
7690Sstevel@tonic-gate 	(void) closedir(dir);
7705558Sbasabi 
7710Sstevel@tonic-gate 	if (chdir(ATDIR) == -1) {
7720Sstevel@tonic-gate 		msg("cannot chdir to at directory");
7730Sstevel@tonic-gate 		return;
7740Sstevel@tonic-gate 	}
7750Sstevel@tonic-gate 	if ((dir = opendir(".")) == NULL) {
7760Sstevel@tonic-gate 		msg("cannot read at at directory");
7770Sstevel@tonic-gate 		return;
7780Sstevel@tonic-gate 	}
7795558Sbasabi 	cwd = AT;
7805558Sbasabi 	while ((dp = readdir(dir)) != NULL) {
7815558Sbasabi 		if (!valid_entry(dp->d_name, ATEVENT))
7825558Sbasabi 			continue;
7835558Sbasabi 		ptr = dp->d_name;
7845558Sbasabi 		if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
7855558Sbasabi 			continue;
7865558Sbasabi 		ptr++;
7875558Sbasabi 		if (!isalpha(*ptr))
7885558Sbasabi 			continue;
7895558Sbasabi 		jobtype = *ptr - 'a';
7905558Sbasabi 		if (jobtype >= NQUEUE) {
7915558Sbasabi 			cron_unlink(dp->d_name);
7925558Sbasabi 			continue;
7935558Sbasabi 		}
7945558Sbasabi 		init_atevent(dp->d_name, tim, jobtype, first);
7955558Sbasabi 	}
7960Sstevel@tonic-gate 	(void) closedir(dir);
7970Sstevel@tonic-gate }
7980Sstevel@tonic-gate 
7995558Sbasabi static int
valid_entry(char * name,int type)8005558Sbasabi valid_entry(char *name, int type)
8015558Sbasabi {
8025558Sbasabi 	struct stat	buf;
8035558Sbasabi 
8045558Sbasabi 	if (strcmp(name, ".") == 0 ||
8055558Sbasabi 	    strcmp(name, "..") == 0)
8065558Sbasabi 		return (0);
8075558Sbasabi 
8085558Sbasabi 	/* skip over ancillary file names */
8095558Sbasabi 	if (audit_cron_is_anc_name(name))
8105558Sbasabi 		return (0);
8115558Sbasabi 
8125558Sbasabi 	if (stat(name, &buf)) {
8135558Sbasabi 		mail(name, BADSTAT, ERR_UNIXERR);
8145558Sbasabi 		cron_unlink(name);
8155558Sbasabi 		return (0);
8165558Sbasabi 	}
8175558Sbasabi 	if (!S_ISREG(buf.st_mode)) {
8185558Sbasabi 		mail(name, BADTYPE, ERR_NOTREG);
8195558Sbasabi 		cron_unlink(name);
8205558Sbasabi 		return (0);
8215558Sbasabi 	}
8225558Sbasabi 	if (type == ATEVENT) {
8235558Sbasabi 		if (!(buf.st_mode & ISUID)) {
8245558Sbasabi 			cron_unlink(name);
8255558Sbasabi 			return (0);
8265558Sbasabi 		}
8275558Sbasabi 	}
8285558Sbasabi 	return (1);
8295558Sbasabi }
8305558Sbasabi 
8315558Sbasabi struct usr *
create_ulist(char * name,int type)8325558Sbasabi create_ulist(char *name, int type)
8330Sstevel@tonic-gate {
8345558Sbasabi 	struct usr	*u;
8355558Sbasabi 
836*11115SNobutomo.Nakano@Sun.COM 	u = xcalloc(1, sizeof (struct usr));
837*11115SNobutomo.Nakano@Sun.COM 	u->name = xstrdup(name);
8385558Sbasabi 	if (type == CRONEVENT) {
8395558Sbasabi 		u->ctexists = TRUE;
8405558Sbasabi 		u->ctid = ecid++;
8415558Sbasabi 	} else {
8425558Sbasabi 		u->ctexists = FALSE;
8435558Sbasabi 		u->ctid = 0;
8445558Sbasabi 	}
845*11115SNobutomo.Nakano@Sun.COM 	u->uid = (uid_t)-1;
846*11115SNobutomo.Nakano@Sun.COM 	u->gid = (uid_t)-1;
8475558Sbasabi 	u->nextusr = uhead;
8485558Sbasabi 	uhead = u;
8495558Sbasabi 	return (u);
8505558Sbasabi }
8515558Sbasabi 
8525558Sbasabi void
init_cronevent(char * name,int first)8535558Sbasabi init_cronevent(char *name, int first)
8545558Sbasabi {
8555558Sbasabi 	struct usr	*u;
8565558Sbasabi 
8575558Sbasabi 	if (first) {
8585558Sbasabi 		u = create_ulist(name, CRONEVENT);
8595558Sbasabi 		readcron(u, 0);
8605558Sbasabi 	} else {
8615558Sbasabi 		if ((u = find_usr(name)) == NULL) {
8625558Sbasabi 			u = create_ulist(name, CRONEVENT);
8635558Sbasabi 			readcron(u, 0);
8645558Sbasabi 		} else {
8655558Sbasabi 			u->ctexists = TRUE;
8665558Sbasabi 			rm_ctevents(u);
8675558Sbasabi 			el_remove(u->ctid, 0);
8685558Sbasabi 			readcron(u, 0);
8690Sstevel@tonic-gate 		}
8705558Sbasabi 	}
8715558Sbasabi }
8725558Sbasabi 
8735558Sbasabi void
init_atevent(char * name,time_t tim,int jobtype,int first)8745558Sbasabi init_atevent(char *name, time_t tim, int jobtype, int first)
8755558Sbasabi {
8765558Sbasabi 	struct usr	*u;
8775558Sbasabi 
8785558Sbasabi 	if (first) {
8795558Sbasabi 		u = create_ulist(name, ATEVENT);
8805558Sbasabi 		add_atevent(u, name, tim, jobtype);
8815558Sbasabi 	} else {
8825558Sbasabi 		if ((u = find_usr(name)) == NULL) {
8835558Sbasabi 			u = create_ulist(name, ATEVENT);
8845558Sbasabi 			add_atevent(u, name, tim, jobtype);
8855558Sbasabi 		} else {
8865558Sbasabi 			update_atevent(u, name, tim, jobtype);
8875558Sbasabi 		}
8880Sstevel@tonic-gate 	}
8890Sstevel@tonic-gate }
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate static void
mod_ctab(char * name,time_t reftime)8920Sstevel@tonic-gate mod_ctab(char *name, time_t reftime)
8930Sstevel@tonic-gate {
8940Sstevel@tonic-gate 	struct	passwd	*pw;
8950Sstevel@tonic-gate 	struct	stat	buf;
8960Sstevel@tonic-gate 	struct	usr	*u;
8978439SChris.Gerhard@sun.com 	char	namebuf[LINE_MAX];
8980Sstevel@tonic-gate 	char	*pname;
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 	/* skip over ancillary file names */
9010Sstevel@tonic-gate 	if (audit_cron_is_anc_name(name))
9020Sstevel@tonic-gate 		return;
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate 	if ((pw = getpwnam(name)) == NULL) {
9050Sstevel@tonic-gate 		msg("No such user as %s - cron entries not created", name);
9060Sstevel@tonic-gate 		return;
9070Sstevel@tonic-gate 	}
9080Sstevel@tonic-gate 	if (cwd != CRON) {
9090Sstevel@tonic-gate 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
9105558Sbasabi 		    CRONDIR, name) >= sizeof (namebuf)) {
9110Sstevel@tonic-gate 			msg("Too long path name %s - cron entries not created",
9125558Sbasabi 			    namebuf);
9130Sstevel@tonic-gate 			return;
9140Sstevel@tonic-gate 		}
9150Sstevel@tonic-gate 		pname = namebuf;
9160Sstevel@tonic-gate 	} else {
9170Sstevel@tonic-gate 		pname = name;
9180Sstevel@tonic-gate 	}
9190Sstevel@tonic-gate 	/*
9200Sstevel@tonic-gate 	 * a warning message is given by the crontab command so there is
9210Sstevel@tonic-gate 	 * no need to give one here......  use this code if you only want
9220Sstevel@tonic-gate 	 * users with a login shell of /usr/bin/sh to use cron
9230Sstevel@tonic-gate 	 */
9240Sstevel@tonic-gate #ifdef BOURNESHELLONLY
9250Sstevel@tonic-gate 	if ((strcmp(pw->pw_shell, "") != 0) &&
9260Sstevel@tonic-gate 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
9270Sstevel@tonic-gate 		mail(name, BADSHELL, ERR_CANTEXECCRON);
9280Sstevel@tonic-gate 		cron_unlink(pname);
9290Sstevel@tonic-gate 		return;
9300Sstevel@tonic-gate 	}
9310Sstevel@tonic-gate #endif
9320Sstevel@tonic-gate 	if (stat(pname, &buf)) {
9330Sstevel@tonic-gate 		mail(name, BADSTAT, ERR_UNIXERR);
9340Sstevel@tonic-gate 		cron_unlink(pname);
9350Sstevel@tonic-gate 		return;
9360Sstevel@tonic-gate 	}
9370Sstevel@tonic-gate 	if (!S_ISREG(buf.st_mode)) {
9380Sstevel@tonic-gate 		mail(name, BADTYPE, ERR_CRONTABENT);
9390Sstevel@tonic-gate 		return;
9400Sstevel@tonic-gate 	}
9410Sstevel@tonic-gate 	if ((u = find_usr(name)) == NULL) {
9420Sstevel@tonic-gate #ifdef DEBUG
9430Sstevel@tonic-gate 		(void) fprintf(stderr, "new user (%s) with a crontab\n", name);
9440Sstevel@tonic-gate #endif
9455558Sbasabi 		u = create_ulist(name, CRONEVENT);
9465558Sbasabi 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
9470Sstevel@tonic-gate 		(void) strcpy(u->home, pw->pw_dir);
9480Sstevel@tonic-gate 		u->uid = pw->pw_uid;
9490Sstevel@tonic-gate 		u->gid = pw->pw_gid;
9500Sstevel@tonic-gate 		readcron(u, reftime);
9510Sstevel@tonic-gate 	} else {
9520Sstevel@tonic-gate 		u->uid = pw->pw_uid;
9530Sstevel@tonic-gate 		u->gid = pw->pw_gid;
9545581Sbasabi 		if (u->home != NULL) {
9555581Sbasabi 			if (strcmp(u->home, pw->pw_dir) != 0) {
9565581Sbasabi 				free(u->home);
9575581Sbasabi 				u->home = xmalloc(strlen(pw->pw_dir) + 1);
9585581Sbasabi 				(void) strcpy(u->home, pw->pw_dir);
9595581Sbasabi 			}
9605581Sbasabi 		} else {
9615558Sbasabi 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
9620Sstevel@tonic-gate 			(void) strcpy(u->home, pw->pw_dir);
9630Sstevel@tonic-gate 		}
9640Sstevel@tonic-gate 		u->ctexists = TRUE;
9650Sstevel@tonic-gate 		if (u->ctid == 0) {
9660Sstevel@tonic-gate #ifdef DEBUG
9670Sstevel@tonic-gate 			(void) fprintf(stderr, "%s now has a crontab\n",
9680Sstevel@tonic-gate 			    u->name);
9690Sstevel@tonic-gate #endif
9700Sstevel@tonic-gate 			/* user didnt have a crontab last time */
9710Sstevel@tonic-gate 			u->ctid = ecid++;
9720Sstevel@tonic-gate 			u->ctevents = NULL;
9730Sstevel@tonic-gate 			readcron(u, reftime);
9740Sstevel@tonic-gate 			return;
9750Sstevel@tonic-gate 		}
9760Sstevel@tonic-gate #ifdef DEBUG
9770Sstevel@tonic-gate 		(void) fprintf(stderr, "%s has revised his crontab\n", u->name);
9780Sstevel@tonic-gate #endif
9790Sstevel@tonic-gate 		rm_ctevents(u);
9800Sstevel@tonic-gate 		el_remove(u->ctid, 0);
9810Sstevel@tonic-gate 		readcron(u, reftime);
9820Sstevel@tonic-gate 	}
9830Sstevel@tonic-gate }
9840Sstevel@tonic-gate 
9850Sstevel@tonic-gate /* ARGSUSED */
9860Sstevel@tonic-gate static void
mod_atjob(char * name,time_t reftime)9870Sstevel@tonic-gate mod_atjob(char *name, time_t reftime)
9880Sstevel@tonic-gate {
9890Sstevel@tonic-gate 	char	*ptr;
9900Sstevel@tonic-gate 	time_t	tim;
9910Sstevel@tonic-gate 	struct	passwd	*pw;
9920Sstevel@tonic-gate 	struct	stat	buf;
9930Sstevel@tonic-gate 	struct	usr	*u;
9940Sstevel@tonic-gate 	char	namebuf[PATH_MAX];
9950Sstevel@tonic-gate 	char	*pname;
9960Sstevel@tonic-gate 	int	jobtype;
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 	ptr = name;
9990Sstevel@tonic-gate 	if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
10000Sstevel@tonic-gate 		return;
10010Sstevel@tonic-gate 	ptr++;
10020Sstevel@tonic-gate 	if (!isalpha(*ptr))
10030Sstevel@tonic-gate 		return;
10040Sstevel@tonic-gate 	jobtype = *ptr - 'a';
10050Sstevel@tonic-gate 
10060Sstevel@tonic-gate 	/* check for audit ancillary file */
10070Sstevel@tonic-gate 	if (audit_cron_is_anc_name(name))
10080Sstevel@tonic-gate 		return;
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate 	if (cwd != AT) {
10110Sstevel@tonic-gate 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name)
10120Sstevel@tonic-gate 		    >= sizeof (namebuf)) {
10130Sstevel@tonic-gate 			return;
10140Sstevel@tonic-gate 		}
10150Sstevel@tonic-gate 		pname = namebuf;
10160Sstevel@tonic-gate 	} else {
10170Sstevel@tonic-gate 		pname = name;
10180Sstevel@tonic-gate 	}
10190Sstevel@tonic-gate 	if (stat(pname, &buf) || jobtype >= NQUEUE) {
10200Sstevel@tonic-gate 		cron_unlink(pname);
10210Sstevel@tonic-gate 		return;
10220Sstevel@tonic-gate 	}
10230Sstevel@tonic-gate 	if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) {
10240Sstevel@tonic-gate 		cron_unlink(pname);
10250Sstevel@tonic-gate 		return;
10260Sstevel@tonic-gate 	}
10270Sstevel@tonic-gate 	if ((pw = getpwuid(buf.st_uid)) == NULL) {
10280Sstevel@tonic-gate 		cron_unlink(pname);
10290Sstevel@tonic-gate 		return;
10300Sstevel@tonic-gate 	}
10310Sstevel@tonic-gate 	/*
10320Sstevel@tonic-gate 	 * a warning message is given by the at command so there is no
10330Sstevel@tonic-gate 	 * need to give one here......use this code if you only want
10340Sstevel@tonic-gate 	 * users with a login shell of /usr/bin/sh to use cron
10350Sstevel@tonic-gate 	 */
10360Sstevel@tonic-gate #ifdef BOURNESHELLONLY
10370Sstevel@tonic-gate 	if ((strcmp(pw->pw_shell, "") != 0) &&
10380Sstevel@tonic-gate 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
10390Sstevel@tonic-gate 		mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT);
10400Sstevel@tonic-gate 		cron_unlink(pname);
10410Sstevel@tonic-gate 		return;
10420Sstevel@tonic-gate 	}
10430Sstevel@tonic-gate #endif
10440Sstevel@tonic-gate 	if ((u = find_usr(pw->pw_name)) == NULL) {
10450Sstevel@tonic-gate #ifdef DEBUG
10460Sstevel@tonic-gate 		(void) fprintf(stderr, "new user (%s) with an at job = %s\n",
10475558Sbasabi 		    pw->pw_name, name);
10480Sstevel@tonic-gate #endif
10495558Sbasabi 		u = create_ulist(pw->pw_name, ATEVENT);
1050*11115SNobutomo.Nakano@Sun.COM 		u->home = xstrdup(pw->pw_dir);
10510Sstevel@tonic-gate 		u->uid = pw->pw_uid;
10520Sstevel@tonic-gate 		u->gid = pw->pw_gid;
10530Sstevel@tonic-gate 		add_atevent(u, name, tim, jobtype);
10540Sstevel@tonic-gate 	} else {
10550Sstevel@tonic-gate 		u->uid = pw->pw_uid;
10560Sstevel@tonic-gate 		u->gid = pw->pw_gid;
10575558Sbasabi 		free(u->home);
1058*11115SNobutomo.Nakano@Sun.COM 		u->home = xstrdup(pw->pw_dir);
10595558Sbasabi 		update_atevent(u, name, tim, jobtype);
10600Sstevel@tonic-gate 	}
10610Sstevel@tonic-gate }
10620Sstevel@tonic-gate 
10630Sstevel@tonic-gate static void
add_atevent(struct usr * u,char * job,time_t tim,int jobtype)10640Sstevel@tonic-gate add_atevent(struct usr *u, char *job, time_t tim, int jobtype)
10650Sstevel@tonic-gate {
10660Sstevel@tonic-gate 	struct event *e;
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate 	e = xmalloc(sizeof (struct event));
10690Sstevel@tonic-gate 	e->etype = jobtype;
10705558Sbasabi 	e->cmd = xmalloc(strlen(job) + 1);
10710Sstevel@tonic-gate 	(void) strcpy(e->cmd, job);
10720Sstevel@tonic-gate 	e->u = u;
10730Sstevel@tonic-gate 	e->link = u->atevents;
10740Sstevel@tonic-gate 	u->atevents = e;
10750Sstevel@tonic-gate 	e->of.at.exists = TRUE;
10760Sstevel@tonic-gate 	e->of.at.eventid = ecid++;
10770Sstevel@tonic-gate 	if (tim < init_time)	/* old job */
10780Sstevel@tonic-gate 		e->time = init_time;
10790Sstevel@tonic-gate 	else
10800Sstevel@tonic-gate 		e->time = tim;
10810Sstevel@tonic-gate #ifdef DEBUG
10820Sstevel@tonic-gate 	(void) fprintf(stderr, "add_atevent: user=%s, job=%s, time=%ld\n",
10835558Sbasabi 	    u->name, e->cmd, e->time);
10840Sstevel@tonic-gate #endif
10857879SSerge.Dussud@Sun.COM 	if (el_add(e, e->time, e->of.at.eventid) < 0) {
10867879SSerge.Dussud@Sun.COM 		ignore_msg("add_atevent", "at", e);
10877879SSerge.Dussud@Sun.COM 	}
10880Sstevel@tonic-gate }
10890Sstevel@tonic-gate 
10905558Sbasabi void
update_atevent(struct usr * u,char * name,time_t tim,int jobtype)10915558Sbasabi update_atevent(struct usr *u, char *name, time_t tim, int jobtype)
10925558Sbasabi {
10935558Sbasabi 	struct event *e;
10945558Sbasabi 
10955558Sbasabi 	e = u->atevents;
10965558Sbasabi 	while (e != NULL) {
10975558Sbasabi 		if (strcmp(e->cmd, name) == 0) {
10985558Sbasabi 			e->of.at.exists = TRUE;
10995558Sbasabi 			break;
11005558Sbasabi 		} else {
11015558Sbasabi 			e = e->link;
11025558Sbasabi 		}
11035558Sbasabi 	}
11045558Sbasabi 	if (e == NULL) {
11055558Sbasabi #ifdef DEBUG
11065558Sbasabi 		(void) fprintf(stderr, "%s has a new at job = %s\n",
11075558Sbasabi 		    u->name, name);
11085558Sbasabi #endif
11095558Sbasabi 			add_atevent(u, name, tim, jobtype);
11105558Sbasabi 	}
11115558Sbasabi }
11120Sstevel@tonic-gate 
11130Sstevel@tonic-gate static char line[CTLINESIZE];	/* holds a line from a crontab file */
11140Sstevel@tonic-gate static int cursor;		/* cursor for the above line */
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate static void
readcron(struct usr * u,time_t reftime)11170Sstevel@tonic-gate readcron(struct usr *u, time_t reftime)
11180Sstevel@tonic-gate {
11190Sstevel@tonic-gate 	/*
11200Sstevel@tonic-gate 	 * readcron reads in a crontab file for a user (u). The list of
11210Sstevel@tonic-gate 	 * events for user u is built, and u->events is made to point to
11220Sstevel@tonic-gate 	 * this list. Each event is also entered into the main event
11230Sstevel@tonic-gate 	 * list.
11240Sstevel@tonic-gate 	 */
11250Sstevel@tonic-gate 	FILE *cf;	/* cf will be a user's crontab file */
11260Sstevel@tonic-gate 	struct event *e;
11270Sstevel@tonic-gate 	int start;
11280Sstevel@tonic-gate 	unsigned int i;
11290Sstevel@tonic-gate 	char namebuf[PATH_MAX];
11300Sstevel@tonic-gate 	char *pname;
11318439SChris.Gerhard@sun.com 	struct shared *tz = NULL;
11328439SChris.Gerhard@sun.com 	struct shared *home = NULL;
11338439SChris.Gerhard@sun.com 	struct shared *shell = NULL;
11340Sstevel@tonic-gate 	int lineno = 0;
11350Sstevel@tonic-gate 
11360Sstevel@tonic-gate 	/* read the crontab file */
11370Sstevel@tonic-gate 	cte_init();		/* Init error handling */
11380Sstevel@tonic-gate 	if (cwd != CRON) {
11390Sstevel@tonic-gate 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
11400Sstevel@tonic-gate 		    CRONDIR, u->name) >= sizeof (namebuf)) {
11410Sstevel@tonic-gate 			return;
11420Sstevel@tonic-gate 		}
11430Sstevel@tonic-gate 		pname = namebuf;
11440Sstevel@tonic-gate 	} else {
11450Sstevel@tonic-gate 		pname = u->name;
11460Sstevel@tonic-gate 	}
11470Sstevel@tonic-gate 	if ((cf = fopen(pname, "r")) == NULL) {
11480Sstevel@tonic-gate 		mail(u->name, NOREAD, ERR_UNIXERR);
11490Sstevel@tonic-gate 		return;
11500Sstevel@tonic-gate 	}
11510Sstevel@tonic-gate 	while (fgets(line, CTLINESIZE, cf) != NULL) {
11528439SChris.Gerhard@sun.com 		char *tmp;
11530Sstevel@tonic-gate 		/* process a line of a crontab file */
11540Sstevel@tonic-gate 		lineno++;
11550Sstevel@tonic-gate 		if (cte_istoomany())
11560Sstevel@tonic-gate 			break;
11570Sstevel@tonic-gate 		cursor = 0;
11580Sstevel@tonic-gate 		while (line[cursor] == ' ' || line[cursor] == '\t')
11590Sstevel@tonic-gate 			cursor++;
11600Sstevel@tonic-gate 		if (line[cursor] == '#' || line[cursor] == '\n')
11610Sstevel@tonic-gate 			continue;
11628439SChris.Gerhard@sun.com 
11638439SChris.Gerhard@sun.com 		if (strncmp(&line[cursor], ENV_TZ,
11648439SChris.Gerhard@sun.com 		    strlen(ENV_TZ)) == 0) {
11658439SChris.Gerhard@sun.com 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
11668439SChris.Gerhard@sun.com 				*tmp = NULL;
11678439SChris.Gerhard@sun.com 			}
11688439SChris.Gerhard@sun.com 
11698439SChris.Gerhard@sun.com 			if (!isvalid_tz(&line[cursor + strlen(ENV_TZ)], NULL,
11708439SChris.Gerhard@sun.com 			    _VTZ_ALL)) {
11718439SChris.Gerhard@sun.com 				cte_add(lineno, line);
11728439SChris.Gerhard@sun.com 				break;
11738439SChris.Gerhard@sun.com 			}
11748439SChris.Gerhard@sun.com 			if (tz == NULL || strcmp(&line[cursor], get_obj(tz))) {
11758439SChris.Gerhard@sun.com 				rel_shared(tz);
11768439SChris.Gerhard@sun.com 				tz = create_shared_str(&line[cursor]);
11778439SChris.Gerhard@sun.com 			}
11788439SChris.Gerhard@sun.com 			continue;
11798439SChris.Gerhard@sun.com 		}
11808439SChris.Gerhard@sun.com 
11818439SChris.Gerhard@sun.com 		if (strncmp(&line[cursor], ENV_HOME,
11828439SChris.Gerhard@sun.com 		    strlen(ENV_HOME)) == 0) {
11838439SChris.Gerhard@sun.com 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
11848439SChris.Gerhard@sun.com 				*tmp = NULL;
11858439SChris.Gerhard@sun.com 			}
11868439SChris.Gerhard@sun.com 			if (home == NULL ||
11878439SChris.Gerhard@sun.com 			    strcmp(&line[cursor], get_obj(home))) {
11888439SChris.Gerhard@sun.com 				rel_shared(home);
11898439SChris.Gerhard@sun.com 				home = create_shared_str(
11908439SChris.Gerhard@sun.com 				    &line[cursor + strlen(ENV_HOME)]);
11918439SChris.Gerhard@sun.com 			}
11928439SChris.Gerhard@sun.com 			continue;
11938439SChris.Gerhard@sun.com 		}
11948439SChris.Gerhard@sun.com 
11958439SChris.Gerhard@sun.com 		if (strncmp(&line[cursor], ENV_SHELL,
11968439SChris.Gerhard@sun.com 		    strlen(ENV_SHELL)) == 0) {
11978439SChris.Gerhard@sun.com 			if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
11988439SChris.Gerhard@sun.com 				*tmp = NULL;
11998439SChris.Gerhard@sun.com 			}
12008439SChris.Gerhard@sun.com 			if (shell == NULL ||
12018439SChris.Gerhard@sun.com 			    strcmp(&line[cursor], get_obj(shell))) {
12028439SChris.Gerhard@sun.com 				rel_shared(shell);
12038439SChris.Gerhard@sun.com 				shell = create_shared_str(&line[cursor]);
12048439SChris.Gerhard@sun.com 			}
12058439SChris.Gerhard@sun.com 			continue;
12068439SChris.Gerhard@sun.com 		}
12078439SChris.Gerhard@sun.com 
12080Sstevel@tonic-gate 		e = xmalloc(sizeof (struct event));
12090Sstevel@tonic-gate 		e->etype = CRONEVENT;
12100Sstevel@tonic-gate 		if (!(((e->of.ct.minute = next_field(0, 59)) != NULL) &&
12110Sstevel@tonic-gate 		    ((e->of.ct.hour = next_field(0, 23)) != NULL) &&
12120Sstevel@tonic-gate 		    ((e->of.ct.daymon = next_field(1, 31)) != NULL) &&
12130Sstevel@tonic-gate 		    ((e->of.ct.month = next_field(1, 12)) != NULL) &&
12140Sstevel@tonic-gate 		    ((e->of.ct.dayweek = next_field(0, 6)) != NULL))) {
12150Sstevel@tonic-gate 			free(e);
12160Sstevel@tonic-gate 			cte_add(lineno, line);
12170Sstevel@tonic-gate 			continue;
12180Sstevel@tonic-gate 		}
12190Sstevel@tonic-gate 		while (line[cursor] == ' ' || line[cursor] == '\t')
12200Sstevel@tonic-gate 			cursor++;
12210Sstevel@tonic-gate 		if (line[cursor] == '\n' || line[cursor] == '\0')
12220Sstevel@tonic-gate 			continue;
12230Sstevel@tonic-gate 		/* get the command to execute	*/
12240Sstevel@tonic-gate 		start = cursor;
12250Sstevel@tonic-gate again:
12260Sstevel@tonic-gate 		while ((line[cursor] != '%') &&
12270Sstevel@tonic-gate 		    (line[cursor] != '\n') &&
12280Sstevel@tonic-gate 		    (line[cursor] != '\0') &&
12290Sstevel@tonic-gate 		    (line[cursor] != '\\'))
12300Sstevel@tonic-gate 			cursor++;
12310Sstevel@tonic-gate 		if (line[cursor] == '\\') {
12320Sstevel@tonic-gate 			cursor += 2;
12330Sstevel@tonic-gate 			goto again;
12340Sstevel@tonic-gate 		}
12355558Sbasabi 		e->cmd = xmalloc(cursor-start + 1);
12365558Sbasabi 		(void) strncpy(e->cmd, line + start, cursor-start);
12370Sstevel@tonic-gate 		e->cmd[cursor-start] = '\0';
12380Sstevel@tonic-gate 		/* see if there is any standard input	*/
12390Sstevel@tonic-gate 		if (line[cursor] == '%') {
12405558Sbasabi 			e->of.ct.input = xmalloc(strlen(line)-cursor + 1);
12415558Sbasabi 			(void) strcpy(e->of.ct.input, line + cursor + 1);
12420Sstevel@tonic-gate 			for (i = 0; i < strlen(e->of.ct.input); i++) {
12430Sstevel@tonic-gate 				if (e->of.ct.input[i] == '%')
12440Sstevel@tonic-gate 					e->of.ct.input[i] = '\n';
12450Sstevel@tonic-gate 			}
12460Sstevel@tonic-gate 		} else {
12470Sstevel@tonic-gate 			e->of.ct.input = NULL;
12480Sstevel@tonic-gate 		}
12498439SChris.Gerhard@sun.com 		/* set the timezone of this entry */
12508439SChris.Gerhard@sun.com 		e->of.ct.tz = dup_shared(tz);
12518439SChris.Gerhard@sun.com 		/* set the shell of this entry */
12528439SChris.Gerhard@sun.com 		e->of.ct.shell = dup_shared(shell);
12538439SChris.Gerhard@sun.com 		/* set the home of this entry */
12548439SChris.Gerhard@sun.com 		e->of.ct.home = dup_shared(home);
12550Sstevel@tonic-gate 		/* have the event point to it's owner	*/
12560Sstevel@tonic-gate 		e->u = u;
12570Sstevel@tonic-gate 		/* insert this event at the front of this user's event list */
12580Sstevel@tonic-gate 		e->link = u->ctevents;
12590Sstevel@tonic-gate 		u->ctevents = e;
12600Sstevel@tonic-gate 		/* set the time for the first occurance of this event	*/
12610Sstevel@tonic-gate 		e->time = next_time(e, reftime);
12620Sstevel@tonic-gate 		/* finally, add this event to the main event list	*/
12637879SSerge.Dussud@Sun.COM 		switch (el_add(e, e->time, u->ctid)) {
12647879SSerge.Dussud@Sun.COM 		case -1:
12657879SSerge.Dussud@Sun.COM 			ignore_msg("readcron", "cron", e);
12667879SSerge.Dussud@Sun.COM 			break;
12677879SSerge.Dussud@Sun.COM 		case -2: /* event time lower than init time */
12687879SSerge.Dussud@Sun.COM 			reset_needed = 1;
12697879SSerge.Dussud@Sun.COM 			break;
12707879SSerge.Dussud@Sun.COM 		}
12710Sstevel@tonic-gate 		cte_valid();
12720Sstevel@tonic-gate #ifdef DEBUG
12730Sstevel@tonic-gate 		cftime(timebuf, "%C", &e->time);
12740Sstevel@tonic-gate 		(void) fprintf(stderr, "inserting cron event %s at %ld (%s)\n",
12755558Sbasabi 		    e->cmd, e->time, timebuf);
12760Sstevel@tonic-gate #endif
12770Sstevel@tonic-gate 	}
12780Sstevel@tonic-gate 	cte_sendmail(u->name);	/* mail errors if any to user */
12790Sstevel@tonic-gate 	(void) fclose(cf);
12808439SChris.Gerhard@sun.com 	rel_shared(tz);
12818439SChris.Gerhard@sun.com 	rel_shared(shell);
12828439SChris.Gerhard@sun.com 	rel_shared(home);
12830Sstevel@tonic-gate }
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate /*
12860Sstevel@tonic-gate  * Below are the functions for handling of errors in crontabs. Concept is to
12870Sstevel@tonic-gate  * collect faulty lines and send one email at the end of the crontab
12880Sstevel@tonic-gate  * evaluation. If there are erroneous lines only ((cte_nvalid == 0), evaluation
12890Sstevel@tonic-gate  * of crontab is aborted. Otherwise reading of crontab is continued to the end
12900Sstevel@tonic-gate  * of the file but no further error logging appears.
12910Sstevel@tonic-gate  */
12920Sstevel@tonic-gate static void
cte_init()12930Sstevel@tonic-gate cte_init()
12940Sstevel@tonic-gate {
12950Sstevel@tonic-gate 	if (cte_text == NULL)
12960Sstevel@tonic-gate 		cte_text = xmalloc(MAILBUFLEN);
12970Sstevel@tonic-gate 	(void) strlcpy(cte_text, cte_intro, MAILBUFLEN);
12980Sstevel@tonic-gate 	cte_lp = cte_text + sizeof (cte_intro) - 1;
12990Sstevel@tonic-gate 	cte_free = MAILBINITFREE;
13000Sstevel@tonic-gate 	cte_nvalid = 0;
13010Sstevel@tonic-gate }
13020Sstevel@tonic-gate 
13030Sstevel@tonic-gate static void
cte_add(int lineno,char * ctline)13040Sstevel@tonic-gate cte_add(int lineno, char *ctline)
13050Sstevel@tonic-gate {
13060Sstevel@tonic-gate 	int len;
13070Sstevel@tonic-gate 	char *p;
13080Sstevel@tonic-gate 
13090Sstevel@tonic-gate 	if (cte_free >= LINELIMIT) {
13100Sstevel@tonic-gate 		(void) sprintf(cte_lp, "%4d: ", lineno);
13110Sstevel@tonic-gate 		(void) strlcat(cte_lp, ctline, LINELIMIT - 1);
13120Sstevel@tonic-gate 		len = strlen(cte_lp);
13130Sstevel@tonic-gate 		if (cte_lp[len - 1] != '\n') {
13140Sstevel@tonic-gate 			cte_lp[len++] = '\n';
13150Sstevel@tonic-gate 			cte_lp[len] = '\0';
13160Sstevel@tonic-gate 		}
13170Sstevel@tonic-gate 		for (p = cte_lp; *p; p++) {
13180Sstevel@tonic-gate 			if (isprint(*p) || *p == '\n' || *p == '\t')
13190Sstevel@tonic-gate 				continue;
13200Sstevel@tonic-gate 			*p = '.';
13210Sstevel@tonic-gate 		}
13220Sstevel@tonic-gate 		cte_lp += len;
13230Sstevel@tonic-gate 		cte_free -= len;
13240Sstevel@tonic-gate 		if (cte_free < LINELIMIT) {
13250Sstevel@tonic-gate 			size_t buflen = MAILBUFLEN - (cte_lp - cte_text);
13260Sstevel@tonic-gate 			(void) strlcpy(cte_lp, cte_trail1, buflen);
13270Sstevel@tonic-gate 			if (cte_nvalid == 0)
13280Sstevel@tonic-gate 				(void) strlcat(cte_lp, cte_trail2, buflen);
13290Sstevel@tonic-gate 		}
13300Sstevel@tonic-gate 	}
13310Sstevel@tonic-gate }
13320Sstevel@tonic-gate 
13330Sstevel@tonic-gate static void
cte_valid()13340Sstevel@tonic-gate cte_valid()
13350Sstevel@tonic-gate {
13360Sstevel@tonic-gate 	cte_nvalid++;
13370Sstevel@tonic-gate }
13380Sstevel@tonic-gate 
13390Sstevel@tonic-gate static int
cte_istoomany()13400Sstevel@tonic-gate cte_istoomany()
13410Sstevel@tonic-gate {
13420Sstevel@tonic-gate 	/*
13430Sstevel@tonic-gate 	 * Return TRUE only if all lines are faulty. So evaluation of
13440Sstevel@tonic-gate 	 * a crontab is not aborted if at least one valid line was found.
13450Sstevel@tonic-gate 	 */
13460Sstevel@tonic-gate 	return (cte_nvalid == 0 && cte_free < LINELIMIT);
13470Sstevel@tonic-gate }
13480Sstevel@tonic-gate 
13490Sstevel@tonic-gate static void
cte_sendmail(char * username)13500Sstevel@tonic-gate cte_sendmail(char *username)
13510Sstevel@tonic-gate {
13520Sstevel@tonic-gate 	if (cte_free < MAILBINITFREE)
13530Sstevel@tonic-gate 		mail(username, cte_text, ERR_CRONTABENT);
13540Sstevel@tonic-gate }
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate /*
13570Sstevel@tonic-gate  * Send mail with error message to a user
13580Sstevel@tonic-gate  */
13590Sstevel@tonic-gate static void
mail(char * usrname,char * mesg,int format)13600Sstevel@tonic-gate mail(char *usrname, char *mesg, int format)
13610Sstevel@tonic-gate {
13620Sstevel@tonic-gate 	/* mail mails a user a message.	*/
13630Sstevel@tonic-gate 	FILE *pipe;
13640Sstevel@tonic-gate 	char *temp;
13650Sstevel@tonic-gate 	struct passwd	*ruser_ids;
13660Sstevel@tonic-gate 	pid_t fork_val;
13670Sstevel@tonic-gate 	int saveerrno = errno;
13680Sstevel@tonic-gate 	struct utsname	name;
13690Sstevel@tonic-gate 
13700Sstevel@tonic-gate #ifdef TESTING
13710Sstevel@tonic-gate 	return;
13720Sstevel@tonic-gate #endif
13730Sstevel@tonic-gate 	(void) uname(&name);
13740Sstevel@tonic-gate 	if ((fork_val = fork()) == (pid_t)-1) {
13750Sstevel@tonic-gate 		msg("cron cannot fork\n");
13760Sstevel@tonic-gate 		return;
13770Sstevel@tonic-gate 	}
13780Sstevel@tonic-gate 	if (fork_val == 0) {
13790Sstevel@tonic-gate 		child_sigreset();
13800Sstevel@tonic-gate 		contract_clear_template();
13810Sstevel@tonic-gate 		if ((ruser_ids = getpwnam(usrname)) == NULL)
13820Sstevel@tonic-gate 			exit(0);
13830Sstevel@tonic-gate 		(void) setuid(ruser_ids->pw_uid);
13845558Sbasabi 		temp = xmalloc(strlen(MAIL) + strlen(usrname) + 2);
13850Sstevel@tonic-gate 		(void) sprintf(temp, "%s %s", MAIL, usrname);
13860Sstevel@tonic-gate 		pipe = popen(temp, "w");
13870Sstevel@tonic-gate 		if (pipe != NULL) {
13880Sstevel@tonic-gate 			(void) fprintf(pipe, "To: %s\n", usrname);
13890Sstevel@tonic-gate 			switch (format) {
13900Sstevel@tonic-gate 			case ERR_CRONTABENT:
13910Sstevel@tonic-gate 				(void) fprintf(pipe, CRONTABERR);
13920Sstevel@tonic-gate 				(void) fprintf(pipe, "Your \"crontab\" on %s\n",
13930Sstevel@tonic-gate 				    name.nodename);
13940Sstevel@tonic-gate 				(void) fprintf(pipe, mesg);
13950Sstevel@tonic-gate 				(void) fprintf(pipe,
13960Sstevel@tonic-gate 				    "\nEntries or crontab have been ignored\n");
13970Sstevel@tonic-gate 				break;
13980Sstevel@tonic-gate 			case ERR_UNIXERR:
13990Sstevel@tonic-gate 				(void) fprintf(pipe, "Subject: %s\n\n", mesg);
14000Sstevel@tonic-gate 				(void) fprintf(pipe,
14010Sstevel@tonic-gate 				    "The error on %s was \"%s\"\n",
14020Sstevel@tonic-gate 				    name.nodename, errmsg(saveerrno));
14030Sstevel@tonic-gate 				break;
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate 			case ERR_CANTEXECCRON:
14060Sstevel@tonic-gate 				(void) fprintf(pipe,
14070Sstevel@tonic-gate 				"Subject: Couldn't run your \"cron\" job\n\n");
14080Sstevel@tonic-gate 				(void) fprintf(pipe,
14090Sstevel@tonic-gate 				    "Your \"cron\" job on %s ", name.nodename);
14100Sstevel@tonic-gate 				(void) fprintf(pipe, "couldn't be run\n");
14110Sstevel@tonic-gate 				(void) fprintf(pipe, "%s\n", mesg);
14120Sstevel@tonic-gate 				(void) fprintf(pipe,
14130Sstevel@tonic-gate 				"The error was \"%s\"\n", errmsg(saveerrno));
14140Sstevel@tonic-gate 				break;
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 			case ERR_CANTEXECAT:
14170Sstevel@tonic-gate 				(void) fprintf(pipe,
14180Sstevel@tonic-gate 				"Subject: Couldn't run your \"at\" job\n\n");
14190Sstevel@tonic-gate 				(void) fprintf(pipe, "Your \"at\" job on %s ",
14200Sstevel@tonic-gate 				    name.nodename);
14210Sstevel@tonic-gate 				(void) fprintf(pipe, "couldn't be run\n");
14220Sstevel@tonic-gate 				(void) fprintf(pipe, "%s\n", mesg);
14230Sstevel@tonic-gate 				(void) fprintf(pipe,
14240Sstevel@tonic-gate 				"The error was \"%s\"\n", errmsg(saveerrno));
14250Sstevel@tonic-gate 				break;
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 			default:
14280Sstevel@tonic-gate 				break;
14290Sstevel@tonic-gate 			}
14300Sstevel@tonic-gate 			(void) pclose(pipe);
14310Sstevel@tonic-gate 		}
14320Sstevel@tonic-gate 		free(temp);
14330Sstevel@tonic-gate 		exit(0);
14340Sstevel@tonic-gate 	}
14350Sstevel@tonic-gate 
14360Sstevel@tonic-gate 	contract_abandon_latest(fork_val);
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate 	if (cron_pid == getpid()) {
14390Sstevel@tonic-gate 		miscpid_insert(fork_val);
14400Sstevel@tonic-gate 	}
14410Sstevel@tonic-gate }
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate static char *
next_field(int lower,int upper)14440Sstevel@tonic-gate next_field(int lower, int upper)
14450Sstevel@tonic-gate {
14460Sstevel@tonic-gate 	/*
14470Sstevel@tonic-gate 	 * next_field returns a pointer to a string which holds the next
14480Sstevel@tonic-gate 	 * field of a line of a crontab file.
14490Sstevel@tonic-gate 	 *   if (numbers in this field are out of range (lower..upper),
14500Sstevel@tonic-gate 	 *	or there is a syntax error) then
14510Sstevel@tonic-gate 	 *	NULL is returned, and a mail message is sent to the
14520Sstevel@tonic-gate 	 *	user telling him which line the error was in.
14530Sstevel@tonic-gate 	 */
14540Sstevel@tonic-gate 
14550Sstevel@tonic-gate 	char *s;
14560Sstevel@tonic-gate 	int num, num2, start;
14570Sstevel@tonic-gate 
14580Sstevel@tonic-gate 	while ((line[cursor] == ' ') || (line[cursor] == '\t'))
14590Sstevel@tonic-gate 		cursor++;
14600Sstevel@tonic-gate 	start = cursor;
14610Sstevel@tonic-gate 	if (line[cursor] == '\0') {
14620Sstevel@tonic-gate 		return (NULL);
14630Sstevel@tonic-gate 	}
14640Sstevel@tonic-gate 	if (line[cursor] == '*') {
14650Sstevel@tonic-gate 		cursor++;
14660Sstevel@tonic-gate 		if ((line[cursor] != ' ') && (line[cursor] != '\t'))
14670Sstevel@tonic-gate 			return (NULL);
14680Sstevel@tonic-gate 		s = xmalloc(2);
14690Sstevel@tonic-gate 		(void) strcpy(s, "*");
14700Sstevel@tonic-gate 		return (s);
14710Sstevel@tonic-gate 	}
14720Sstevel@tonic-gate 	for (;;) {
14730Sstevel@tonic-gate 		if (!isdigit(line[cursor]))
14740Sstevel@tonic-gate 			return (NULL);
14750Sstevel@tonic-gate 		num = 0;
14760Sstevel@tonic-gate 		do {
14770Sstevel@tonic-gate 			num = num*10 + (line[cursor]-'0');
14780Sstevel@tonic-gate 		} while (isdigit(line[++cursor]));
14790Sstevel@tonic-gate 		if ((num < lower) || (num > upper))
14800Sstevel@tonic-gate 			return (NULL);
14810Sstevel@tonic-gate 		if (line[cursor] == '-') {
14820Sstevel@tonic-gate 			if (!isdigit(line[++cursor]))
14830Sstevel@tonic-gate 				return (NULL);
14840Sstevel@tonic-gate 			num2 = 0;
14850Sstevel@tonic-gate 			do {
14860Sstevel@tonic-gate 				num2 = num2*10 + (line[cursor]-'0');
14870Sstevel@tonic-gate 			} while (isdigit(line[++cursor]));
14880Sstevel@tonic-gate 			if ((num2 < lower) || (num2 > upper))
14890Sstevel@tonic-gate 				return (NULL);
14900Sstevel@tonic-gate 		}
14910Sstevel@tonic-gate 		if ((line[cursor] == ' ') || (line[cursor] == '\t'))
14920Sstevel@tonic-gate 			break;
14930Sstevel@tonic-gate 		if (line[cursor] == '\0')
14940Sstevel@tonic-gate 			return (NULL);
14950Sstevel@tonic-gate 		if (line[cursor++] != ',')
14960Sstevel@tonic-gate 			return (NULL);
14970Sstevel@tonic-gate 	}
14985558Sbasabi 	s = xmalloc(cursor-start + 1);
14995558Sbasabi 	(void) strncpy(s, line + start, cursor-start);
15000Sstevel@tonic-gate 	s[cursor-start] = '\0';
15010Sstevel@tonic-gate 	return (s);
15020Sstevel@tonic-gate }
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate #define	tm_cmp(t1, t2) (\
15050Sstevel@tonic-gate 	(t1)->tm_year == (t2)->tm_year && \
15060Sstevel@tonic-gate 	(t1)->tm_mon == (t2)->tm_mon && \
15070Sstevel@tonic-gate 	(t1)->tm_mday == (t2)->tm_mday && \
15080Sstevel@tonic-gate 	(t1)->tm_hour == (t2)->tm_hour && \
15090Sstevel@tonic-gate 	(t1)->tm_min == (t2)->tm_min)
15100Sstevel@tonic-gate 
15110Sstevel@tonic-gate #define	tm_setup(tp, yr, mon, dy, hr, min, dst) \
15120Sstevel@tonic-gate 	(tp)->tm_year = yr; \
15130Sstevel@tonic-gate 	(tp)->tm_mon = mon; \
15140Sstevel@tonic-gate 	(tp)->tm_mday = dy; \
15150Sstevel@tonic-gate 	(tp)->tm_hour = hr; \
15160Sstevel@tonic-gate 	(tp)->tm_min = min; \
15170Sstevel@tonic-gate 	(tp)->tm_isdst = dst; \
15180Sstevel@tonic-gate 	(tp)->tm_sec = 0; \
15190Sstevel@tonic-gate 	(tp)->tm_wday = 0; \
15200Sstevel@tonic-gate 	(tp)->tm_yday = 0;
15210Sstevel@tonic-gate 
15220Sstevel@tonic-gate /*
15230Sstevel@tonic-gate  * modification for bugid 1104537. the second argument to next_time is
15240Sstevel@tonic-gate  * now the value of time(2) to be used. if this is 0, then use the
15250Sstevel@tonic-gate  * current time. otherwise, the second argument is the time from which to
15260Sstevel@tonic-gate  * calculate things. this is useful to correct situations where you've
15270Sstevel@tonic-gate  * gone backwards in time (I.e. the system's internal clock is correcting
15280Sstevel@tonic-gate  * itself backwards).
15290Sstevel@tonic-gate  */
15300Sstevel@tonic-gate 
15318439SChris.Gerhard@sun.com 
15328439SChris.Gerhard@sun.com 
15330Sstevel@tonic-gate static time_t
tz_next_time(struct event * e,time_t tflag)15348439SChris.Gerhard@sun.com tz_next_time(struct event *e, time_t tflag)
15350Sstevel@tonic-gate {
15360Sstevel@tonic-gate 	/*
15370Sstevel@tonic-gate 	 * returns the integer time for the next occurance of event e.
15380Sstevel@tonic-gate 	 * the following fields have ranges as indicated:
15390Sstevel@tonic-gate 	 * PRGM  | min	hour	day of month	mon	day of week
15400Sstevel@tonic-gate 	 * ------|-------------------------------------------------------
15410Sstevel@tonic-gate 	 * cron  | 0-59	0-23	    1-31	1-12	0-6 (0=sunday)
15420Sstevel@tonic-gate 	 * time  | 0-59	0-23	    1-31	0-11	0-6 (0=sunday)
15430Sstevel@tonic-gate 	 * NOTE: this routine is hard to understand.
15440Sstevel@tonic-gate 	 */
15450Sstevel@tonic-gate 
15460Sstevel@tonic-gate 	struct tm *tm, ref_tm, tmp, tmp1, tmp2;
15478439SChris.Gerhard@sun.com 	int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days;
15488439SChris.Gerhard@sun.com 	int d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd;
15498439SChris.Gerhard@sun.com 	int today;
15500Sstevel@tonic-gate 	time_t t, ref_t, t1, t2, zone_start;
15510Sstevel@tonic-gate 	int fallback;
15520Sstevel@tonic-gate 	extern int days_btwn(int, int, int, int, int, int);
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate 	if (tflag == 0) {
15550Sstevel@tonic-gate 		t = time(NULL);	/* original way of doing things	*/
15560Sstevel@tonic-gate 	} else {
15570Sstevel@tonic-gate 		t =  tflag;
15580Sstevel@tonic-gate 	}
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate 	tm = &ref_tm;	/* use a local variable and call localtime_r() */
15610Sstevel@tonic-gate 	ref_t = t;	/* keep a copy of the reference time */
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate recalc:
15640Sstevel@tonic-gate 	fallback = 0;
15650Sstevel@tonic-gate 
15660Sstevel@tonic-gate 	(void) localtime_r(&t, tm);
15670Sstevel@tonic-gate 
15680Sstevel@tonic-gate 	if (daylight) {
15690Sstevel@tonic-gate 		tmp = *tm;
15700Sstevel@tonic-gate 		tmp.tm_isdst = (tm->tm_isdst > 0 ? 0 : 1);
15710Sstevel@tonic-gate 		t1 = xmktime(&tmp);
15720Sstevel@tonic-gate 		/*
15730Sstevel@tonic-gate 		 * see if we will have timezone switch over, and clock will
15740Sstevel@tonic-gate 		 * fall back. zone_start will hold the time when it happens
15750Sstevel@tonic-gate 		 * (ie time of PST -> PDT switch over).
15760Sstevel@tonic-gate 		 */
15770Sstevel@tonic-gate 		if (tm->tm_isdst != tmp.tm_isdst &&
15780Sstevel@tonic-gate 		    (t1 - t) == (timezone - altzone) &&
15790Sstevel@tonic-gate 		    tm_cmp(tm, &tmp)) {
15800Sstevel@tonic-gate 			zone_start = get_switching_time(tmp.tm_isdst, t);
15810Sstevel@tonic-gate 			fallback = 1;
15820Sstevel@tonic-gate 		}
15830Sstevel@tonic-gate 	}
15840Sstevel@tonic-gate 
15855558Sbasabi 	tm_mon = next_ge(tm->tm_mon + 1, e->of.ct.month) - 1;	/* 0-11 */
15860Sstevel@tonic-gate 	tm_mday = next_ge(tm->tm_mday, e->of.ct.daymon);	/* 1-31 */
15870Sstevel@tonic-gate 	tm_wday = next_ge(tm->tm_wday, e->of.ct.dayweek);	/* 0-6	*/
15880Sstevel@tonic-gate 	today = TRUE;
15890Sstevel@tonic-gate 	if ((strcmp(e->of.ct.daymon, "*") == 0 && tm->tm_wday != tm_wday) ||
15900Sstevel@tonic-gate 	    (strcmp(e->of.ct.dayweek, "*") == 0 && tm->tm_mday != tm_mday) ||
15910Sstevel@tonic-gate 	    (tm->tm_mday != tm_mday && tm->tm_wday != tm_wday) ||
15920Sstevel@tonic-gate 	    (tm->tm_mon != tm_mon)) {
15930Sstevel@tonic-gate 		today = FALSE;
15940Sstevel@tonic-gate 	}
15950Sstevel@tonic-gate 	m = tm->tm_min + (t == ref_t ? 1 : 0);
15960Sstevel@tonic-gate 	if ((tm->tm_hour + 1) <= next_ge(tm->tm_hour, e->of.ct.hour)) {
15970Sstevel@tonic-gate 		m = 0;
15980Sstevel@tonic-gate 	}
15990Sstevel@tonic-gate 	min = next_ge(m%60, e->of.ct.minute);
16000Sstevel@tonic-gate 	carry = (min < m) ? 1 : 0;
16010Sstevel@tonic-gate 	h = tm->tm_hour + carry;
16020Sstevel@tonic-gate 	hr = next_ge(h%24, e->of.ct.hour);
16030Sstevel@tonic-gate 	carry = (hr < h) ? 1 : 0;
16040Sstevel@tonic-gate 
16050Sstevel@tonic-gate 	if (carry == 0 && today) {
16060Sstevel@tonic-gate 		/* this event must occur today */
16070Sstevel@tonic-gate 		tm_setup(&tmp, tm->tm_year, tm->tm_mon, tm->tm_mday,
16085558Sbasabi 		    hr, min, tm->tm_isdst);
16090Sstevel@tonic-gate 		tmp1 = tmp;
16100Sstevel@tonic-gate 		if ((t1 = xmktime(&tmp1)) == (time_t)-1) {
16110Sstevel@tonic-gate 			return (0);
16120Sstevel@tonic-gate 		}
16130Sstevel@tonic-gate 		if (daylight && tmp.tm_isdst != tmp1.tm_isdst) {
16140Sstevel@tonic-gate 			/* In case we are falling back */
16150Sstevel@tonic-gate 			if (fallback) {
16160Sstevel@tonic-gate 				/* we may need to run the job once more. */
16170Sstevel@tonic-gate 				t = zone_start;
16180Sstevel@tonic-gate 				goto recalc;
16190Sstevel@tonic-gate 			}
16200Sstevel@tonic-gate 
16210Sstevel@tonic-gate 			/*
16220Sstevel@tonic-gate 			 * In case we are not in falling back period,
16230Sstevel@tonic-gate 			 * calculate the time assuming the DST. If the
16240Sstevel@tonic-gate 			 * date/time is not altered by mktime, it is the
16250Sstevel@tonic-gate 			 * time to execute the job.
16260Sstevel@tonic-gate 			 */
16270Sstevel@tonic-gate 			tmp2 = tmp;
16280Sstevel@tonic-gate 			tmp2.tm_isdst = tmp1.tm_isdst;
16290Sstevel@tonic-gate 			if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
16300Sstevel@tonic-gate 				return (0);
16310Sstevel@tonic-gate 			}
16320Sstevel@tonic-gate 			if (tmp1.tm_isdst == tmp2.tm_isdst &&
16330Sstevel@tonic-gate 			    tm_cmp(&tmp, &tmp2)) {
16340Sstevel@tonic-gate 				/*
16350Sstevel@tonic-gate 				 * We got a valid time.
16360Sstevel@tonic-gate 				 */
16370Sstevel@tonic-gate 				return (t1);
16380Sstevel@tonic-gate 			} else {
16390Sstevel@tonic-gate 				/*
16400Sstevel@tonic-gate 				 * If the date does not match even if
16410Sstevel@tonic-gate 				 * we assume the alternate timezone, then
16420Sstevel@tonic-gate 				 * it must be the invalid time. eg
16430Sstevel@tonic-gate 				 * 2am while switching 1:59am to 3am.
16440Sstevel@tonic-gate 				 * t1 should point the time before the
16450Sstevel@tonic-gate 				 * switching over as we've calculate the
16460Sstevel@tonic-gate 				 * time with assuming alternate zone.
16470Sstevel@tonic-gate 				 */
16480Sstevel@tonic-gate 				if (tmp1.tm_isdst != tmp2.tm_isdst) {
16490Sstevel@tonic-gate 					t = get_switching_time(tmp1.tm_isdst,
16505558Sbasabi 					    t1);
16510Sstevel@tonic-gate 				} else {
16520Sstevel@tonic-gate 					/* does this really happen? */
16530Sstevel@tonic-gate 					t = get_switching_time(tmp1.tm_isdst,
16545558Sbasabi 					    t1 - abs(timezone - altzone));
16550Sstevel@tonic-gate 				}
16568439SChris.Gerhard@sun.com 				if (t == (time_t)-1) {
16570Sstevel@tonic-gate 					return (0);
16588439SChris.Gerhard@sun.com 				}
16590Sstevel@tonic-gate 			}
16600Sstevel@tonic-gate 			goto recalc;
16610Sstevel@tonic-gate 		}
16620Sstevel@tonic-gate 		if (tm_cmp(&tmp, &tmp1)) {
16630Sstevel@tonic-gate 			/* got valid time */
16640Sstevel@tonic-gate 			return (t1);
16650Sstevel@tonic-gate 		} else {
16660Sstevel@tonic-gate 			/*
16670Sstevel@tonic-gate 			 * This should never happen, but just in
16680Sstevel@tonic-gate 			 * case, we fall back to the old code.
16690Sstevel@tonic-gate 			 */
16700Sstevel@tonic-gate 			if (tm->tm_min > min) {
16710Sstevel@tonic-gate 				t += (time_t)(hr-tm->tm_hour-1) * HOUR +
16725558Sbasabi 				    (time_t)(60-tm->tm_min + min) * MINUTE;
16730Sstevel@tonic-gate 			} else {
16740Sstevel@tonic-gate 				t += (time_t)(hr-tm->tm_hour) * HOUR +
16755558Sbasabi 				    (time_t)(min-tm->tm_min) * MINUTE;
16760Sstevel@tonic-gate 			}
16770Sstevel@tonic-gate 			t1 = t;
16780Sstevel@tonic-gate 			t -= (time_t)tm->tm_sec;
16790Sstevel@tonic-gate 			(void) localtime_r(&t, &tmp);
16800Sstevel@tonic-gate 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
16810Sstevel@tonic-gate 				t -= (timezone - altzone);
16820Sstevel@tonic-gate 			return ((t <= ref_t) ? t1 : t);
16830Sstevel@tonic-gate 		}
16840Sstevel@tonic-gate 	}
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 	/*
16870Sstevel@tonic-gate 	 * Job won't run today, however if we have a switch over within
16880Sstevel@tonic-gate 	 * one hour and we will have one hour time drifting back in this
16890Sstevel@tonic-gate 	 * period, we may need to run the job one more time if the job was
16900Sstevel@tonic-gate 	 * set to run on this hour of clock.
16910Sstevel@tonic-gate 	 */
16920Sstevel@tonic-gate 	if (fallback) {
16930Sstevel@tonic-gate 		t = zone_start;
16940Sstevel@tonic-gate 		goto recalc;
16950Sstevel@tonic-gate 	}
16960Sstevel@tonic-gate 
16970Sstevel@tonic-gate 	min = next_ge(0, e->of.ct.minute);
16980Sstevel@tonic-gate 	hr = next_ge(0, e->of.ct.hour);
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate 	/*
17010Sstevel@tonic-gate 	 * calculate the date of the next occurance of this event, which
17020Sstevel@tonic-gate 	 * will be on a different day than the current
17030Sstevel@tonic-gate 	 */
17040Sstevel@tonic-gate 
17050Sstevel@tonic-gate 	/* check monthly day specification	*/
17065558Sbasabi 	d1 = tm->tm_mday + 1;
17075558Sbasabi 	day1 = next_ge((d1-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1,
17085558Sbasabi 	    e->of.ct.daymon);
17090Sstevel@tonic-gate 	carry1 = (day1 < d1) ? 1 : 0;
17100Sstevel@tonic-gate 
17110Sstevel@tonic-gate 	/* check weekly day specification	*/
17125558Sbasabi 	d2 = tm->tm_wday + 1;
17130Sstevel@tonic-gate 	wday = next_ge(d2%7, e->of.ct.dayweek);
17140Sstevel@tonic-gate 	if (wday < d2)
17150Sstevel@tonic-gate 		daysahead = 7 - d2 + wday;
17160Sstevel@tonic-gate 	else
17170Sstevel@tonic-gate 		daysahead = wday - d2;
17185558Sbasabi 	day2 = (d1 + daysahead-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1;
17190Sstevel@tonic-gate 	carry2 = (day2 < d1) ? 1 : 0;
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate 	/*
17220Sstevel@tonic-gate 	 *	based on their respective specifications, day1, and day2 give
17230Sstevel@tonic-gate 	 *	the day of the month for the next occurance of this event.
17240Sstevel@tonic-gate 	 */
17250Sstevel@tonic-gate 	if ((strcmp(e->of.ct.daymon, "*") == 0) &&
17260Sstevel@tonic-gate 	    (strcmp(e->of.ct.dayweek, "*") != 0)) {
17270Sstevel@tonic-gate 		day1 = day2;
17280Sstevel@tonic-gate 		carry1 = carry2;
17290Sstevel@tonic-gate 	}
17300Sstevel@tonic-gate 	if ((strcmp(e->of.ct.daymon, "*") != 0) &&
17310Sstevel@tonic-gate 	    (strcmp(e->of.ct.dayweek, "*") == 0)) {
17320Sstevel@tonic-gate 		day2 = day1;
17330Sstevel@tonic-gate 		carry2 = carry1;
17340Sstevel@tonic-gate 	}
17350Sstevel@tonic-gate 
17360Sstevel@tonic-gate 	yr = tm->tm_year;
17370Sstevel@tonic-gate 	if ((carry1 && carry2) || (tm->tm_mon != tm_mon)) {
17380Sstevel@tonic-gate 		/* event does not occur in this month	*/
17395558Sbasabi 		m = tm->tm_mon + 1;
17405558Sbasabi 		mon = next_ge(m%12 + 1, e->of.ct.month) - 1;	/* 0..11 */
17410Sstevel@tonic-gate 		carry = (mon < m) ? 1 : 0;
17420Sstevel@tonic-gate 		yr += carry;
17430Sstevel@tonic-gate 		/* recompute day1 and day2	*/
17440Sstevel@tonic-gate 		day1 = next_ge(1, e->of.ct.daymon);
17450Sstevel@tonic-gate 		db = days_btwn(tm->tm_mon, tm->tm_mday, tm->tm_year, mon,
17465558Sbasabi 		    1, yr) + 1;
17475558Sbasabi 		wd = (tm->tm_wday + db)%7;
17480Sstevel@tonic-gate 		/* wd is the day of the week of the first of month mon	*/
17490Sstevel@tonic-gate 		wday = next_ge(wd, e->of.ct.dayweek);
17500Sstevel@tonic-gate 		if (wday < wd)
17510Sstevel@tonic-gate 			day2 = 1 + 7 - wd + wday;
17520Sstevel@tonic-gate 		else
17530Sstevel@tonic-gate 			day2 = 1 + wday - wd;
17540Sstevel@tonic-gate 		if ((strcmp(e->of.ct.daymon, "*") != 0) &&
17550Sstevel@tonic-gate 		    (strcmp(e->of.ct.dayweek, "*") == 0))
17560Sstevel@tonic-gate 			day2 = day1;
17570Sstevel@tonic-gate 		if ((strcmp(e->of.ct.daymon, "*") == 0) &&
17580Sstevel@tonic-gate 		    (strcmp(e->of.ct.dayweek, "*") != 0))
17590Sstevel@tonic-gate 			day1 = day2;
17600Sstevel@tonic-gate 		day = (day1 < day2) ? day1 : day2;
17610Sstevel@tonic-gate 	} else {			/* event occurs in this month	*/
17620Sstevel@tonic-gate 		mon = tm->tm_mon;
17630Sstevel@tonic-gate 		if (!carry1 && !carry2)
17640Sstevel@tonic-gate 			day = (day1 < day2) ? day1 : day2;
17650Sstevel@tonic-gate 		else if (!carry1)
17660Sstevel@tonic-gate 			day = day1;
17670Sstevel@tonic-gate 		else
17680Sstevel@tonic-gate 			day = day2;
17690Sstevel@tonic-gate 	}
17700Sstevel@tonic-gate 
17710Sstevel@tonic-gate 	/*
17720Sstevel@tonic-gate 	 * now that we have the min, hr, day, mon, yr of the next event,
17730Sstevel@tonic-gate 	 * figure out what time that turns out to be.
17740Sstevel@tonic-gate 	 */
17750Sstevel@tonic-gate 	tm_setup(&tmp, yr, mon, day, hr, min, -1);
17760Sstevel@tonic-gate 	tmp2 = tmp;
17770Sstevel@tonic-gate 	if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
17780Sstevel@tonic-gate 		return (0);
17790Sstevel@tonic-gate 	}
17800Sstevel@tonic-gate 	if (tm_cmp(&tmp, &tmp2)) {
17810Sstevel@tonic-gate 		/*
17820Sstevel@tonic-gate 		 * mktime returns clock for the current time zone. If the
17830Sstevel@tonic-gate 		 * target date was in fallback period, it needs to be adjusted
17840Sstevel@tonic-gate 		 * to the time comes first.
17850Sstevel@tonic-gate 		 * Suppose, we are at Jan and scheduling job at 1:30am10/26/03.
17860Sstevel@tonic-gate 		 * mktime returns the time in PST, but 1:30am in PDT comes
17870Sstevel@tonic-gate 		 * first. So reverse the tm_isdst, and see if we have such
17880Sstevel@tonic-gate 		 * time/date.
17890Sstevel@tonic-gate 		 */
17900Sstevel@tonic-gate 		if (daylight) {
17910Sstevel@tonic-gate 			int dst = tmp2.tm_isdst;
17920Sstevel@tonic-gate 
17930Sstevel@tonic-gate 			tmp2 = tmp;
17940Sstevel@tonic-gate 			tmp2.tm_isdst = (dst > 0 ? 0 : 1);
17950Sstevel@tonic-gate 			if ((t2 = xmktime(&tmp2)) == (time_t)-1) {
17960Sstevel@tonic-gate 				return (0);
17970Sstevel@tonic-gate 			}
17980Sstevel@tonic-gate 			if (tm_cmp(&tmp, &tmp2)) {
17990Sstevel@tonic-gate 				/*
18000Sstevel@tonic-gate 				 * same time/date found in the opposite zone.
18010Sstevel@tonic-gate 				 * check the clock to see which comes early.
18020Sstevel@tonic-gate 				 */
18030Sstevel@tonic-gate 				if (t2 > ref_t && t2 < t1) {
18040Sstevel@tonic-gate 					t1 = t2;
18050Sstevel@tonic-gate 				}
18060Sstevel@tonic-gate 			}
18070Sstevel@tonic-gate 		}
18080Sstevel@tonic-gate 		return (t1);
18090Sstevel@tonic-gate 	} else {
18100Sstevel@tonic-gate 		/*
18110Sstevel@tonic-gate 		 * mktime has set different time/date for the given date.
18120Sstevel@tonic-gate 		 * This means that the next job is scheduled to be run on the
18130Sstevel@tonic-gate 		 * invalid time. There are three possible invalid date/time.
18140Sstevel@tonic-gate 		 * 1. Non existing day of the month. such as April 31th.
18150Sstevel@tonic-gate 		 * 2. Feb 29th in the non-leap year.
18160Sstevel@tonic-gate 		 * 3. Time gap during the DST switch over.
18170Sstevel@tonic-gate 		 */
18180Sstevel@tonic-gate 		d1 = days_in_mon(mon, yr);
18190Sstevel@tonic-gate 		if ((mon != 1 && day > d1) || (mon == 1 && day > 29)) {
18200Sstevel@tonic-gate 			/*
18210Sstevel@tonic-gate 			 * see if we have got a specific date which
18220Sstevel@tonic-gate 			 * is invalid.
18230Sstevel@tonic-gate 			 */
18240Sstevel@tonic-gate 			if (strcmp(e->of.ct.dayweek, "*") == 0 &&
18255558Sbasabi 			    mon == (next_ge((mon + 1)%12 + 1,
18265558Sbasabi 			    e->of.ct.month) - 1) &&
18270Sstevel@tonic-gate 			    day <= next_ge(1, e->of.ct.daymon)) {
18280Sstevel@tonic-gate 				/* job never run */
18290Sstevel@tonic-gate 				return (0);
18300Sstevel@tonic-gate 			}
18310Sstevel@tonic-gate 			/*
18320Sstevel@tonic-gate 			 * Since the day has gone invalid, we need to go to
18330Sstevel@tonic-gate 			 * next month, and recalcuate the first occurrence.
18340Sstevel@tonic-gate 			 * eg the cron tab such as:
18350Sstevel@tonic-gate 			 * 0 0 1,15,31 1,2,3,4,5 * /usr/bin....
18360Sstevel@tonic-gate 			 * 2/31 is invalid, so the next job is 3/1.
18370Sstevel@tonic-gate 			 */
18380Sstevel@tonic-gate 			tmp2 = tmp;
18390Sstevel@tonic-gate 			tmp2.tm_min = 0;
18400Sstevel@tonic-gate 			tmp2.tm_hour = 0;
18410Sstevel@tonic-gate 			tmp2.tm_mday = 1; /* 1st day of the month */
18420Sstevel@tonic-gate 			if (mon == 11) {
18430Sstevel@tonic-gate 				tmp2.tm_mon = 0;
18440Sstevel@tonic-gate 				tmp2.tm_year = yr + 1;
18450Sstevel@tonic-gate 			} else {
18460Sstevel@tonic-gate 				tmp2.tm_mon = mon + 1;
18470Sstevel@tonic-gate 			}
18480Sstevel@tonic-gate 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
18490Sstevel@tonic-gate 				return (0);
18500Sstevel@tonic-gate 			}
18510Sstevel@tonic-gate 		} else if (mon == 1 && day > d1) {
18520Sstevel@tonic-gate 			/*
18530Sstevel@tonic-gate 			 * ie 29th in the non-leap year. Forwarding the
18540Sstevel@tonic-gate 			 * clock to Feb 29th 00:00 (March 1st), and recalculate
18550Sstevel@tonic-gate 			 * the next time.
18560Sstevel@tonic-gate 			 */
18570Sstevel@tonic-gate 			tmp2 = tmp;
18580Sstevel@tonic-gate 			tmp2.tm_min = 0;
18590Sstevel@tonic-gate 			tmp2.tm_hour = 0;
18600Sstevel@tonic-gate 			if ((t = xmktime(&tmp2)) == (time_t)-1) {
18610Sstevel@tonic-gate 				return (0);
18620Sstevel@tonic-gate 			}
18630Sstevel@tonic-gate 		} else if (daylight) {
18640Sstevel@tonic-gate 			/*
18650Sstevel@tonic-gate 			 * Non existing time, eg 2am PST during summer time
18660Sstevel@tonic-gate 			 * switch.
18670Sstevel@tonic-gate 			 * We need to get the correct isdst which we are
18680Sstevel@tonic-gate 			 * swithing to, by adding time difference to make sure
18690Sstevel@tonic-gate 			 * that t2 is in the zone being switched.
18700Sstevel@tonic-gate 			 */
18710Sstevel@tonic-gate 			t2 = t1;
18720Sstevel@tonic-gate 			t2 += abs(timezone - altzone);
18730Sstevel@tonic-gate 			(void) localtime_r(&t2, &tmp2);
18740Sstevel@tonic-gate 			zone_start = get_switching_time(tmp2.tm_isdst,
18755558Sbasabi 			    t1 - abs(timezone - altzone));
18760Sstevel@tonic-gate 			if (zone_start == (time_t)-1) {
18770Sstevel@tonic-gate 				return (0);
18780Sstevel@tonic-gate 			}
18790Sstevel@tonic-gate 			t = zone_start;
18800Sstevel@tonic-gate 		} else {
18810Sstevel@tonic-gate 			/*
18820Sstevel@tonic-gate 			 * This should never happen, but fall back to the
18830Sstevel@tonic-gate 			 * old code.
18840Sstevel@tonic-gate 			 */
18850Sstevel@tonic-gate 			days = days_btwn(tm->tm_mon,
18865558Sbasabi 			    tm->tm_mday, tm->tm_year, mon, day, yr);
18870Sstevel@tonic-gate 			t += (time_t)(23-tm->tm_hour)*HOUR
18885558Sbasabi 			    + (time_t)(60-tm->tm_min)*MINUTE
18895558Sbasabi 			    + (time_t)hr*HOUR + (time_t)min*MINUTE
18905558Sbasabi 			    + (time_t)days*DAY;
18910Sstevel@tonic-gate 			t1 = t;
18920Sstevel@tonic-gate 			t -= (time_t)tm->tm_sec;
18930Sstevel@tonic-gate 			(void) localtime_r(&t, &tmp);
18940Sstevel@tonic-gate 			if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
18950Sstevel@tonic-gate 				t -= (timezone - altzone);
18960Sstevel@tonic-gate 			return (t <= ref_t ? t1 : t);
18970Sstevel@tonic-gate 		}
18980Sstevel@tonic-gate 		goto recalc;
18990Sstevel@tonic-gate 	}
19000Sstevel@tonic-gate 	/*NOTREACHED*/
19010Sstevel@tonic-gate }
19020Sstevel@tonic-gate 
19038439SChris.Gerhard@sun.com static time_t
next_time(struct event * e,time_t tflag)19048439SChris.Gerhard@sun.com next_time(struct event *e, time_t tflag)
19058439SChris.Gerhard@sun.com {
19068439SChris.Gerhard@sun.com 	if (e->of.ct.tz != NULL) {
19078439SChris.Gerhard@sun.com 		time_t ret;
19088439SChris.Gerhard@sun.com 
19098439SChris.Gerhard@sun.com 		(void) putenv((char *)get_obj(e->of.ct.tz));
19108439SChris.Gerhard@sun.com 		tzset();
19118439SChris.Gerhard@sun.com 		ret = tz_next_time(e, tflag);
19128439SChris.Gerhard@sun.com 		(void) putenv(tzone);
19138439SChris.Gerhard@sun.com 		tzset();
19148439SChris.Gerhard@sun.com 		return (ret);
19158439SChris.Gerhard@sun.com 	} else {
19168439SChris.Gerhard@sun.com 		return (tz_next_time(e, tflag));
19178439SChris.Gerhard@sun.com 	}
19188439SChris.Gerhard@sun.com }
19198439SChris.Gerhard@sun.com 
19200Sstevel@tonic-gate /*
19210Sstevel@tonic-gate  * This returns TOD in time_t that zone switch will happen, and this
19220Sstevel@tonic-gate  * will be called when clock fallback is about to happen.
19230Sstevel@tonic-gate  * (ie 30minutes before the time of PST -> PDT switch. 2:00 AM PST
19240Sstevel@tonic-gate  * will fall back to 1:00 PDT. So this function will be called only
19250Sstevel@tonic-gate  * for the time between 1:00 AM PST and 2:00 PST(1:00 PST)).
19260Sstevel@tonic-gate  * First goes through the common time differences to see if zone
19270Sstevel@tonic-gate  * switch happens at those minutes later. If not, check every minutes
19280Sstevel@tonic-gate  * until 6 hours ahead see if it happens(We might have 45minutes
19290Sstevel@tonic-gate  * fallback).
19300Sstevel@tonic-gate  */
19310Sstevel@tonic-gate static time_t
get_switching_time(int to_dst,time_t t_ref)19320Sstevel@tonic-gate get_switching_time(int to_dst, time_t t_ref)
19330Sstevel@tonic-gate {
19340Sstevel@tonic-gate 	time_t t, t1;
19350Sstevel@tonic-gate 	struct tm tmp, tmp1;
19360Sstevel@tonic-gate 	int hints[] = { 60, 120, 30, 90, 0}; /* minutes */
19370Sstevel@tonic-gate 	int i;
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate 	(void) localtime_r(&t_ref, &tmp);
19400Sstevel@tonic-gate 	tmp1 = tmp;
19410Sstevel@tonic-gate 	tmp1.tm_sec = 0;
19420Sstevel@tonic-gate 	tmp1.tm_min = 0;
19430Sstevel@tonic-gate 	if ((t = xmktime(&tmp1)) == (time_t)-1)
19440Sstevel@tonic-gate 		return ((time_t)-1);
19450Sstevel@tonic-gate 
19460Sstevel@tonic-gate 	/* fast path */
19470Sstevel@tonic-gate 	for (i = 0; hints[i] != 0; i++) {
19480Sstevel@tonic-gate 		t1 = t + hints[i] * 60;
19490Sstevel@tonic-gate 		(void) localtime_r(&t1, &tmp1);
19500Sstevel@tonic-gate 		if (tmp1.tm_isdst == to_dst) {
19510Sstevel@tonic-gate 			t1--;
19520Sstevel@tonic-gate 			(void) localtime_r(&t1, &tmp1);
19530Sstevel@tonic-gate 			if (tmp1.tm_isdst != to_dst) {
19540Sstevel@tonic-gate 				return (t1 + 1);
19550Sstevel@tonic-gate 			}
19560Sstevel@tonic-gate 		}
19570Sstevel@tonic-gate 	}
19580Sstevel@tonic-gate 
19590Sstevel@tonic-gate 	/* ugly, but don't know other than this. */
19600Sstevel@tonic-gate 	tmp1 = tmp;
19610Sstevel@tonic-gate 	tmp1.tm_sec = 0;
19620Sstevel@tonic-gate 	if ((t = xmktime(&tmp1)) == (time_t)-1)
19630Sstevel@tonic-gate 		return ((time_t)-1);
19640Sstevel@tonic-gate 	while (t < (t_ref + 6*60*60)) { /* 6 hours should be enough */
19650Sstevel@tonic-gate 		t += 60; /* at least one minute, I assume */
19660Sstevel@tonic-gate 		(void) localtime_r(&t, &tmp);
19670Sstevel@tonic-gate 		if (tmp.tm_isdst == to_dst)
19680Sstevel@tonic-gate 			return (t);
19690Sstevel@tonic-gate 	}
19700Sstevel@tonic-gate 	return ((time_t)-1);
19710Sstevel@tonic-gate }
19720Sstevel@tonic-gate 
19730Sstevel@tonic-gate static time_t
xmktime(struct tm * tmp)19740Sstevel@tonic-gate xmktime(struct tm *tmp)
19750Sstevel@tonic-gate {
19760Sstevel@tonic-gate 	time_t ret;
19770Sstevel@tonic-gate 
19780Sstevel@tonic-gate 	if ((ret = mktime(tmp)) == (time_t)-1) {
19790Sstevel@tonic-gate 		if (errno == EOVERFLOW) {
19800Sstevel@tonic-gate 			return ((time_t)-1);
19810Sstevel@tonic-gate 		}
19820Sstevel@tonic-gate 		crabort("internal error: mktime failed",
19835558Sbasabi 		    REMOVE_FIFO|CONSOLE_MSG);
19840Sstevel@tonic-gate 	}
19850Sstevel@tonic-gate 	return (ret);
19860Sstevel@tonic-gate }
19870Sstevel@tonic-gate 
19880Sstevel@tonic-gate #define	DUMMY	100
19890Sstevel@tonic-gate 
19900Sstevel@tonic-gate static int
next_ge(int current,char * list)19910Sstevel@tonic-gate next_ge(int current, char *list)
19920Sstevel@tonic-gate {
19930Sstevel@tonic-gate 	/*
19940Sstevel@tonic-gate 	 * list is a character field as in a crontab file;
19950Sstevel@tonic-gate 	 * for example: "40, 20, 50-10"
19960Sstevel@tonic-gate 	 * next_ge returns the next number in the list that is
19970Sstevel@tonic-gate 	 * greater than  or equal to current. if no numbers of list
19980Sstevel@tonic-gate 	 * are >= current, the smallest element of list is returned.
19990Sstevel@tonic-gate 	 * NOTE: current must be in the appropriate range.
20000Sstevel@tonic-gate 	 */
20010Sstevel@tonic-gate 
20020Sstevel@tonic-gate 	char *ptr;
20030Sstevel@tonic-gate 	int n, n2, min, min_gt;
20040Sstevel@tonic-gate 
20050Sstevel@tonic-gate 	if (strcmp(list, "*") == 0)
20060Sstevel@tonic-gate 		return (current);
20070Sstevel@tonic-gate 	ptr = list;
20080Sstevel@tonic-gate 	min = DUMMY;
20090Sstevel@tonic-gate 	min_gt = DUMMY;
20100Sstevel@tonic-gate 	for (;;) {
20110Sstevel@tonic-gate 		if ((n = (int)num(&ptr)) == current)
20120Sstevel@tonic-gate 			return (current);
20130Sstevel@tonic-gate 		if (n < min)
20140Sstevel@tonic-gate 			min = n;
20150Sstevel@tonic-gate 		if ((n > current) && (n < min_gt))
20160Sstevel@tonic-gate 			min_gt = n;
20170Sstevel@tonic-gate 		if (*ptr == '-') {
20180Sstevel@tonic-gate 			ptr++;
20190Sstevel@tonic-gate 			if ((n2 = (int)num(&ptr)) > n) {
20200Sstevel@tonic-gate 				if ((current > n) && (current <= n2))
20210Sstevel@tonic-gate 					return (current);
20220Sstevel@tonic-gate 			} else {	/* range that wraps around */
20230Sstevel@tonic-gate 				if (current > n)
20240Sstevel@tonic-gate 					return (current);
20250Sstevel@tonic-gate 				if (current <= n2)
20260Sstevel@tonic-gate 					return (current);
20270Sstevel@tonic-gate 			}
20280Sstevel@tonic-gate 		}
20290Sstevel@tonic-gate 		if (*ptr == '\0')
20300Sstevel@tonic-gate 			break;
20310Sstevel@tonic-gate 		ptr += 1;
20320Sstevel@tonic-gate 	}
20330Sstevel@tonic-gate 	if (min_gt != DUMMY)
20340Sstevel@tonic-gate 		return (min_gt);
20350Sstevel@tonic-gate 	else
20360Sstevel@tonic-gate 		return (min);
20370Sstevel@tonic-gate }
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate static void
free_if_unused(struct usr * u)20400Sstevel@tonic-gate free_if_unused(struct usr *u)
20410Sstevel@tonic-gate {
20420Sstevel@tonic-gate 	struct usr *cur, *prev;
20430Sstevel@tonic-gate 	/*
20440Sstevel@tonic-gate 	 *	To make sure a usr structure is idle we must check that
20450Sstevel@tonic-gate 	 *	there are no at jobs queued for the user; the user does
20460Sstevel@tonic-gate 	 *	not have a crontab, and also that there are no running at
20470Sstevel@tonic-gate 	 *	or cron jobs (since the runinfo structure also has a
20480Sstevel@tonic-gate 	 *	pointer to the usr structure).
20490Sstevel@tonic-gate 	 */
20500Sstevel@tonic-gate 	if (!u->ctexists && u->atevents == NULL &&
20510Sstevel@tonic-gate 	    u->cruncnt == 0 && u->aruncnt == 0) {
20520Sstevel@tonic-gate #ifdef DEBUG
20530Sstevel@tonic-gate 		(void) fprintf(stderr, "%s removed from usr list\n", u->name);
20540Sstevel@tonic-gate #endif
20550Sstevel@tonic-gate 		for (cur = uhead, prev = NULL;
20565558Sbasabi 		    cur != u;
20575558Sbasabi 		    prev = cur, cur = cur->nextusr) {
20580Sstevel@tonic-gate 			if (cur == NULL) {
20590Sstevel@tonic-gate 				return;
20600Sstevel@tonic-gate 			}
20610Sstevel@tonic-gate 		}
20620Sstevel@tonic-gate 
20630Sstevel@tonic-gate 		if (prev == NULL)
20640Sstevel@tonic-gate 			uhead = u->nextusr;
20650Sstevel@tonic-gate 		else
20660Sstevel@tonic-gate 			prev->nextusr = u->nextusr;
20670Sstevel@tonic-gate 		free(u->name);
20680Sstevel@tonic-gate 		free(u->home);
20690Sstevel@tonic-gate 		free(u);
20700Sstevel@tonic-gate 	}
20710Sstevel@tonic-gate }
20720Sstevel@tonic-gate 
20730Sstevel@tonic-gate static void
del_atjob(char * name,char * usrname)20740Sstevel@tonic-gate del_atjob(char *name, char *usrname)
20750Sstevel@tonic-gate {
20760Sstevel@tonic-gate 
20770Sstevel@tonic-gate 	struct	event	*e, *eprev;
20780Sstevel@tonic-gate 	struct	usr	*u;
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 	if ((u = find_usr(usrname)) == NULL)
20810Sstevel@tonic-gate 		return;
20820Sstevel@tonic-gate 	e = u->atevents;
20830Sstevel@tonic-gate 	eprev = NULL;
20840Sstevel@tonic-gate 	while (e != NULL) {
20850Sstevel@tonic-gate 		if (strcmp(name, e->cmd) == 0) {
20860Sstevel@tonic-gate 			if (next_event == e)
20870Sstevel@tonic-gate 				next_event = NULL;
20880Sstevel@tonic-gate 			if (eprev == NULL)
20890Sstevel@tonic-gate 				u->atevents = e->link;
20900Sstevel@tonic-gate 			else
20910Sstevel@tonic-gate 				eprev->link = e->link;
20920Sstevel@tonic-gate 			el_remove(e->of.at.eventid, 1);
20930Sstevel@tonic-gate 			free(e->cmd);
20940Sstevel@tonic-gate 			free(e);
20950Sstevel@tonic-gate 			break;
20960Sstevel@tonic-gate 		} else {
20970Sstevel@tonic-gate 			eprev = e;
20980Sstevel@tonic-gate 			e = e->link;
20990Sstevel@tonic-gate 		}
21000Sstevel@tonic-gate 	}
21010Sstevel@tonic-gate 
21020Sstevel@tonic-gate 	free_if_unused(u);
21030Sstevel@tonic-gate }
21040Sstevel@tonic-gate 
21050Sstevel@tonic-gate static void
del_ctab(char * name)21060Sstevel@tonic-gate del_ctab(char *name)
21070Sstevel@tonic-gate {
21080Sstevel@tonic-gate 
21090Sstevel@tonic-gate 	struct	usr *u;
21100Sstevel@tonic-gate 
21110Sstevel@tonic-gate 	if ((u = find_usr(name)) == NULL)
21120Sstevel@tonic-gate 		return;
21130Sstevel@tonic-gate 	rm_ctevents(u);
21140Sstevel@tonic-gate 	el_remove(u->ctid, 0);
21150Sstevel@tonic-gate 	u->ctid = 0;
21160Sstevel@tonic-gate 	u->ctexists = 0;
21170Sstevel@tonic-gate 
21180Sstevel@tonic-gate 	free_if_unused(u);
21190Sstevel@tonic-gate }
21200Sstevel@tonic-gate 
21210Sstevel@tonic-gate static void
rm_ctevents(struct usr * u)21220Sstevel@tonic-gate rm_ctevents(struct usr *u)
21230Sstevel@tonic-gate {
21240Sstevel@tonic-gate 	struct event *e2, *e3;
21250Sstevel@tonic-gate 
21260Sstevel@tonic-gate 	/*
21270Sstevel@tonic-gate 	 * see if the next event (to be run by cron) is a cronevent
21280Sstevel@tonic-gate 	 * owned by this user.
21290Sstevel@tonic-gate 	 */
21300Sstevel@tonic-gate 
21310Sstevel@tonic-gate 	if ((next_event != NULL) &&
21320Sstevel@tonic-gate 	    (next_event->etype == CRONEVENT) &&
21330Sstevel@tonic-gate 	    (next_event->u == u)) {
21340Sstevel@tonic-gate 		next_event = NULL;
21350Sstevel@tonic-gate 	}
21360Sstevel@tonic-gate 	e2 = u->ctevents;
21370Sstevel@tonic-gate 	while (e2 != NULL) {
21380Sstevel@tonic-gate 		free(e2->cmd);
21398439SChris.Gerhard@sun.com 		rel_shared(e2->of.ct.tz);
21408439SChris.Gerhard@sun.com 		rel_shared(e2->of.ct.shell);
21418439SChris.Gerhard@sun.com 		rel_shared(e2->of.ct.home);
21420Sstevel@tonic-gate 		free(e2->of.ct.minute);
21430Sstevel@tonic-gate 		free(e2->of.ct.hour);
21440Sstevel@tonic-gate 		free(e2->of.ct.daymon);
21450Sstevel@tonic-gate 		free(e2->of.ct.month);
21460Sstevel@tonic-gate 		free(e2->of.ct.dayweek);
21470Sstevel@tonic-gate 		if (e2->of.ct.input != NULL)
21480Sstevel@tonic-gate 			free(e2->of.ct.input);
21490Sstevel@tonic-gate 		e3 = e2->link;
21500Sstevel@tonic-gate 		free(e2);
21510Sstevel@tonic-gate 		e2 = e3;
21520Sstevel@tonic-gate 	}
21530Sstevel@tonic-gate 	u->ctevents = NULL;
21540Sstevel@tonic-gate }
21550Sstevel@tonic-gate 
21560Sstevel@tonic-gate 
21570Sstevel@tonic-gate static struct usr *
find_usr(char * uname)21580Sstevel@tonic-gate find_usr(char *uname)
21590Sstevel@tonic-gate {
21600Sstevel@tonic-gate 	struct usr *u;
21610Sstevel@tonic-gate 
21620Sstevel@tonic-gate 	u = uhead;
21630Sstevel@tonic-gate 	while (u != NULL) {
21640Sstevel@tonic-gate 		if (strcmp(u->name, uname) == 0)
21650Sstevel@tonic-gate 			return (u);
21660Sstevel@tonic-gate 		u = u->nextusr;
21670Sstevel@tonic-gate 	}
21680Sstevel@tonic-gate 	return (NULL);
21690Sstevel@tonic-gate }
21700Sstevel@tonic-gate 
21710Sstevel@tonic-gate /*
21720Sstevel@tonic-gate  * Execute cron command or at/batch job.
21730Sstevel@tonic-gate  * If ever a premature return is added to this function pay attention to
21740Sstevel@tonic-gate  * free at_cmdfile and outfile plus jobname buffers of the runinfo structure.
21750Sstevel@tonic-gate  */
21760Sstevel@tonic-gate static int
ex(struct event * e)21770Sstevel@tonic-gate ex(struct event *e)
21780Sstevel@tonic-gate {
21790Sstevel@tonic-gate 	int r;
21800Sstevel@tonic-gate 	int fd;
21810Sstevel@tonic-gate 	pid_t rfork;
21820Sstevel@tonic-gate 	FILE *atcmdfp;
21830Sstevel@tonic-gate 	char mailvar[4];
21840Sstevel@tonic-gate 	char *at_cmdfile = NULL;
21850Sstevel@tonic-gate 	struct stat buf;
21860Sstevel@tonic-gate 	struct queue *qp;
21870Sstevel@tonic-gate 	struct runinfo *rp;
21880Sstevel@tonic-gate 	struct project proj, *pproj = NULL;
21898439SChris.Gerhard@sun.com 	union {
21908439SChris.Gerhard@sun.com 		struct {
21918439SChris.Gerhard@sun.com 			char buf[PROJECT_BUFSZ];
21928439SChris.Gerhard@sun.com 			char buf2[PROJECT_BUFSZ];
21938439SChris.Gerhard@sun.com 		} p;
21948439SChris.Gerhard@sun.com 		char error[CANT_STR_LEN + PATH_MAX];
21958439SChris.Gerhard@sun.com 	} bufs;
21960Sstevel@tonic-gate 	char *tmpfile;
21970Sstevel@tonic-gate 	FILE *fptr;
21980Sstevel@tonic-gate 	time_t dhltime;
21990Sstevel@tonic-gate 	projid_t projid;
22000Sstevel@tonic-gate 	int projflag = 0;
22018439SChris.Gerhard@sun.com 	char *home;
22028439SChris.Gerhard@sun.com 	char *sh;
22030Sstevel@tonic-gate 
22040Sstevel@tonic-gate 	qp = &qt[e->etype];	/* set pointer to queue defs */
22050Sstevel@tonic-gate 	if (qp->nrun >= qp->njob) {
22065558Sbasabi 		msg("%c queue max run limit reached", e->etype + 'a');
22070Sstevel@tonic-gate 		resched(qp->nwait);
22080Sstevel@tonic-gate 		return (0);
22090Sstevel@tonic-gate 	}
2210*11115SNobutomo.Nakano@Sun.COM 
2211801Sbasabi 	rp = rinfo_get(0); /* allocating a new runinfo struct */
22120Sstevel@tonic-gate 
22130Sstevel@tonic-gate 	/*
22140Sstevel@tonic-gate 	 * the tempnam() function uses malloc(3C) to allocate space for the
22150Sstevel@tonic-gate 	 * constructed file name, and returns a pointer to this area, which
22160Sstevel@tonic-gate 	 * is assigned to rp->outfile. Here rp->outfile is not overwritten.
22170Sstevel@tonic-gate 	 */
22180Sstevel@tonic-gate 
22190Sstevel@tonic-gate 	rp->outfile = tempnam(TMPDIR, PFX);
22200Sstevel@tonic-gate 	rp->jobtype = e->etype;
22210Sstevel@tonic-gate 	if (e->etype == CRONEVENT) {
22225558Sbasabi 		rp->jobname = xmalloc(strlen(e->cmd) + 1);
22230Sstevel@tonic-gate 		(void) strcpy(rp->jobname, e->cmd);
22240Sstevel@tonic-gate 		/* "cron" jobs only produce mail if there's output */
22250Sstevel@tonic-gate 		rp->mailwhendone = 0;
22260Sstevel@tonic-gate 	} else {
22275558Sbasabi 		at_cmdfile = xmalloc(strlen(ATDIR) + strlen(e->cmd) + 2);
22280Sstevel@tonic-gate 		(void) sprintf(at_cmdfile, "%s/%s", ATDIR, e->cmd);
22290Sstevel@tonic-gate 		if ((atcmdfp = fopen(at_cmdfile, "r")) == NULL) {
22300Sstevel@tonic-gate 			if (errno == ENAMETOOLONG) {
22310Sstevel@tonic-gate 				if (chdir(ATDIR) == 0)
22320Sstevel@tonic-gate 					cron_unlink(e->cmd);
22330Sstevel@tonic-gate 			} else {
22340Sstevel@tonic-gate 				cron_unlink(at_cmdfile);
22350Sstevel@tonic-gate 			}
22360Sstevel@tonic-gate 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECAT);
22370Sstevel@tonic-gate 			free(at_cmdfile);
22380Sstevel@tonic-gate 			rinfo_free(rp);
22390Sstevel@tonic-gate 			return (0);
22400Sstevel@tonic-gate 		}
22415558Sbasabi 		rp->jobname = xmalloc(strlen(at_cmdfile) + 1);
22420Sstevel@tonic-gate 		(void) strcpy(rp->jobname, at_cmdfile);
22430Sstevel@tonic-gate 
22440Sstevel@tonic-gate 		/*
22450Sstevel@tonic-gate 		 * Skip over the first two lines.
22460Sstevel@tonic-gate 		 */
22470Sstevel@tonic-gate 		(void) fscanf(atcmdfp, "%*[^\n]\n");
22480Sstevel@tonic-gate 		(void) fscanf(atcmdfp, "%*[^\n]\n");
22490Sstevel@tonic-gate 		if (fscanf(atcmdfp, ": notify by mail: %3s%*[^\n]\n",
22505558Sbasabi 		    mailvar) == 1) {
22510Sstevel@tonic-gate 			/*
22520Sstevel@tonic-gate 			 * Check to see if we should always send mail
22530Sstevel@tonic-gate 			 * to the owner.
22540Sstevel@tonic-gate 			 */
22550Sstevel@tonic-gate 			rp->mailwhendone = (strcmp(mailvar, "yes") == 0);
22560Sstevel@tonic-gate 		} else {
22570Sstevel@tonic-gate 			rp->mailwhendone = 0;
22580Sstevel@tonic-gate 		}
22590Sstevel@tonic-gate 
22600Sstevel@tonic-gate 		if (fscanf(atcmdfp, "\n: project: %d\n", &projid) == 1) {
22610Sstevel@tonic-gate 			projflag = 1;
22620Sstevel@tonic-gate 		}
22630Sstevel@tonic-gate 		(void) fclose(atcmdfp);
22640Sstevel@tonic-gate 	}
22650Sstevel@tonic-gate 
22660Sstevel@tonic-gate 	/*
22670Sstevel@tonic-gate 	 * we make sure that the system time
22680Sstevel@tonic-gate 	 * hasn't drifted backwards. if it has, el_add() is now
22690Sstevel@tonic-gate 	 * called, to make sure that the event queue is back in order,
22700Sstevel@tonic-gate 	 * and we set the delayed flag. cron will pick up the request
22710Sstevel@tonic-gate 	 * later on at the proper time.
22720Sstevel@tonic-gate 	 */
22730Sstevel@tonic-gate 	dhltime = time(NULL);
22740Sstevel@tonic-gate 	if ((dhltime - e->time) < 0) {
22750Sstevel@tonic-gate 		msg("clock time drifted backwards!\n");
22760Sstevel@tonic-gate 		if (next_event->etype == CRONEVENT) {
22770Sstevel@tonic-gate 			msg("correcting cron event\n");
22780Sstevel@tonic-gate 			next_event->time = next_time(next_event, dhltime);
22797879SSerge.Dussud@Sun.COM 			switch (el_add(next_event, next_event->time,
22807879SSerge.Dussud@Sun.COM 			    (next_event->u)->ctid)) {
22817879SSerge.Dussud@Sun.COM 			case -1:
22827879SSerge.Dussud@Sun.COM 				ignore_msg("ex", "cron", next_event);
22837879SSerge.Dussud@Sun.COM 				break;
22847879SSerge.Dussud@Sun.COM 			case -2: /* event time lower than init time */
22857879SSerge.Dussud@Sun.COM 				reset_needed = 1;
22867879SSerge.Dussud@Sun.COM 				break;
22877879SSerge.Dussud@Sun.COM 			}
22880Sstevel@tonic-gate 		} else { /* etype == ATEVENT */
22890Sstevel@tonic-gate 			msg("correcting batch event\n");
22907879SSerge.Dussud@Sun.COM 			if (el_add(next_event, next_event->time,
22917879SSerge.Dussud@Sun.COM 			    next_event->of.at.eventid) < 0) {
22927879SSerge.Dussud@Sun.COM 				ignore_msg("ex", "at", next_event);
22937879SSerge.Dussud@Sun.COM 			}
22940Sstevel@tonic-gate 		}
22950Sstevel@tonic-gate 		delayed++;
22960Sstevel@tonic-gate 		t_old = time(NULL);
22970Sstevel@tonic-gate 		free(at_cmdfile);
22980Sstevel@tonic-gate 		rinfo_free(rp);
22990Sstevel@tonic-gate 		return (0);
23000Sstevel@tonic-gate 	}
23010Sstevel@tonic-gate 
23020Sstevel@tonic-gate 	if ((rfork = fork()) == (pid_t)-1) {
23030Sstevel@tonic-gate 		reap_child();
23040Sstevel@tonic-gate 		if ((rfork = fork()) == (pid_t)-1) {
23050Sstevel@tonic-gate 			msg("cannot fork");
23060Sstevel@tonic-gate 			free(at_cmdfile);
23070Sstevel@tonic-gate 			rinfo_free(rp);
23080Sstevel@tonic-gate 			resched(60);
23090Sstevel@tonic-gate 			(void) sleep(30);
23100Sstevel@tonic-gate 			return (0);
23110Sstevel@tonic-gate 		}
23120Sstevel@tonic-gate 	}
23130Sstevel@tonic-gate 	if (rfork) {		/* parent process */
23140Sstevel@tonic-gate 		contract_abandon_latest(rfork);
23150Sstevel@tonic-gate 
23160Sstevel@tonic-gate 		++qp->nrun;
23170Sstevel@tonic-gate 		rp->pid = rfork;
23180Sstevel@tonic-gate 		rp->que = e->etype;
23190Sstevel@tonic-gate 		if (e->etype != CRONEVENT)
23200Sstevel@tonic-gate 			(e->u)->aruncnt++;
23210Sstevel@tonic-gate 		else
23220Sstevel@tonic-gate 			(e->u)->cruncnt++;
23230Sstevel@tonic-gate 		rp->rusr = (e->u);
23240Sstevel@tonic-gate 		logit(BCHAR, rp, 0);
23250Sstevel@tonic-gate 		free(at_cmdfile);
23260Sstevel@tonic-gate 
23270Sstevel@tonic-gate 		return (0);
23280Sstevel@tonic-gate 	}
23290Sstevel@tonic-gate 
23300Sstevel@tonic-gate 	child_sigreset();
23310Sstevel@tonic-gate 	contract_clear_template();
23320Sstevel@tonic-gate 
23330Sstevel@tonic-gate 	if (e->etype != CRONEVENT) {
23340Sstevel@tonic-gate 		/* open jobfile as stdin to shell */
23350Sstevel@tonic-gate 		if (stat(at_cmdfile, &buf)) {
23360Sstevel@tonic-gate 			if (errno == ENAMETOOLONG) {
23370Sstevel@tonic-gate 				if (chdir(ATDIR) == 0)
23380Sstevel@tonic-gate 					cron_unlink(e->cmd);
23390Sstevel@tonic-gate 			} else
23400Sstevel@tonic-gate 				cron_unlink(at_cmdfile);
23410Sstevel@tonic-gate 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
23420Sstevel@tonic-gate 			exit(1);
23430Sstevel@tonic-gate 		}
23440Sstevel@tonic-gate 		if (!(buf.st_mode&ISUID)) {
23450Sstevel@tonic-gate 			/*
23460Sstevel@tonic-gate 			 * if setuid bit off, original owner has
23470Sstevel@tonic-gate 			 * given this file to someone else
23480Sstevel@tonic-gate 			 */
23490Sstevel@tonic-gate 			cron_unlink(at_cmdfile);
23500Sstevel@tonic-gate 			exit(1);
23510Sstevel@tonic-gate 		}
23520Sstevel@tonic-gate 		if ((fd = open(at_cmdfile, O_RDONLY)) == -1) {
23530Sstevel@tonic-gate 			mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
23540Sstevel@tonic-gate 			cron_unlink(at_cmdfile);
23550Sstevel@tonic-gate 			exit(1);
23560Sstevel@tonic-gate 		}
23570Sstevel@tonic-gate 		if (fd != 0) {
23580Sstevel@tonic-gate 			(void) dup2(fd, 0);
23590Sstevel@tonic-gate 			(void) close(fd);
23600Sstevel@tonic-gate 		}
23610Sstevel@tonic-gate 		/*
23620Sstevel@tonic-gate 		 * retrieve the project id of the at job and convert it
23630Sstevel@tonic-gate 		 * to a project name.  fail if it's not a valid project
23640Sstevel@tonic-gate 		 * or if the user isn't a member of the project.
23650Sstevel@tonic-gate 		 */
23660Sstevel@tonic-gate 		if (projflag == 1) {
23670Sstevel@tonic-gate 			if ((pproj = getprojbyid(projid, &proj,
23688439SChris.Gerhard@sun.com 			    (void *)&bufs.p.buf,
23698439SChris.Gerhard@sun.com 			    sizeof (bufs.p.buf))) == NULL ||
23700Sstevel@tonic-gate 			    !inproj(e->u->name, pproj->pj_name,
23718439SChris.Gerhard@sun.com 			    bufs.p.buf2, sizeof (bufs.p.buf2))) {
23720Sstevel@tonic-gate 				cron_unlink(at_cmdfile);
23730Sstevel@tonic-gate 				mail((e->u)->name, BADPROJID, ERR_CANTEXECAT);
23740Sstevel@tonic-gate 				exit(1);
23750Sstevel@tonic-gate 			}
23760Sstevel@tonic-gate 		}
23770Sstevel@tonic-gate 	}
23780Sstevel@tonic-gate 
23790Sstevel@tonic-gate 	/*
23800Sstevel@tonic-gate 	 * Put process in a new session, and create a new task.
23810Sstevel@tonic-gate 	 */
23820Sstevel@tonic-gate 	if (setsid() < 0) {
23830Sstevel@tonic-gate 		msg("setsid failed with errno = %d. job failed (%s)"
23840Sstevel@tonic-gate 		    " for user %s", errno, e->cmd, e->u->name);
23850Sstevel@tonic-gate 		if (e->etype != CRONEVENT)
23860Sstevel@tonic-gate 			cron_unlink(at_cmdfile);
23870Sstevel@tonic-gate 		exit(1);
23880Sstevel@tonic-gate 	}
23890Sstevel@tonic-gate 
23900Sstevel@tonic-gate 	/*
23910Sstevel@tonic-gate 	 * set correct user identification and check his account
23920Sstevel@tonic-gate 	 */
23930Sstevel@tonic-gate 	r = set_user_cred(e->u, pproj);
23940Sstevel@tonic-gate 	if (r == VUC_EXPIRED) {
23950Sstevel@tonic-gate 		msg("user (%s) account is expired", e->u->name);
23960Sstevel@tonic-gate 		audit_cron_user_acct_expired(e->u->name);
23970Sstevel@tonic-gate 		clean_out_user(e->u);
23980Sstevel@tonic-gate 		exit(1);
23990Sstevel@tonic-gate 	}
24000Sstevel@tonic-gate 	if (r == VUC_NEW_AUTH) {
24010Sstevel@tonic-gate 		msg("user (%s) password has expired", e->u->name);
24020Sstevel@tonic-gate 		audit_cron_user_acct_expired(e->u->name);
24030Sstevel@tonic-gate 		clean_out_user(e->u);
24040Sstevel@tonic-gate 		exit(1);
24050Sstevel@tonic-gate 	}
24060Sstevel@tonic-gate 	if (r != VUC_OK) {
24070Sstevel@tonic-gate 		msg("bad user (%s)", e->u->name);
24080Sstevel@tonic-gate 		audit_cron_bad_user(e->u->name);
24090Sstevel@tonic-gate 		clean_out_user(e->u);
24100Sstevel@tonic-gate 		exit(1);
24110Sstevel@tonic-gate 	}
24120Sstevel@tonic-gate 	/*
24130Sstevel@tonic-gate 	 * check user and initialize the supplementary group access list.
24140Sstevel@tonic-gate 	 * bugid 1230784: deleted from parent to avoid cron hang. Now
24150Sstevel@tonic-gate 	 * only child handles the call.
24160Sstevel@tonic-gate 	 */
24170Sstevel@tonic-gate 
24180Sstevel@tonic-gate 	if (verify_user_cred(e->u) != VUC_OK ||
24190Sstevel@tonic-gate 	    setgid(e->u->gid) == -1 ||
24200Sstevel@tonic-gate 	    initgroups(e->u->name, e->u->gid) == -1) {
24210Sstevel@tonic-gate 		msg("bad user (%s) or setgid failed (%s)",
24225558Sbasabi 		    e->u->name, e->u->name);
24230Sstevel@tonic-gate 		audit_cron_bad_user(e->u->name);
24240Sstevel@tonic-gate 		clean_out_user(e->u);
24250Sstevel@tonic-gate 		exit(1);
24260Sstevel@tonic-gate 	}
24270Sstevel@tonic-gate 
24286858Sjc144527 	if ((e->u)->uid == 0) { /* set default path */
24296858Sjc144527 		/* path settable in defaults file */
24306858Sjc144527 		envinit[2] = supath;
24316858Sjc144527 	} else {
24326858Sjc144527 		envinit[2] = path;
24336858Sjc144527 	}
24346858Sjc144527 
24350Sstevel@tonic-gate 	if (e->etype != CRONEVENT) {
24360Sstevel@tonic-gate 		r = audit_cron_session(e->u->name, NULL,
24375558Sbasabi 		    e->u->uid, e->u->gid, at_cmdfile);
24380Sstevel@tonic-gate 		cron_unlink(at_cmdfile);
24390Sstevel@tonic-gate 	} else {
24400Sstevel@tonic-gate 		r = audit_cron_session(e->u->name, CRONDIR,
24415558Sbasabi 		    e->u->uid, e->u->gid, NULL);
24420Sstevel@tonic-gate 	}
24430Sstevel@tonic-gate 	if (r != 0) {
24440Sstevel@tonic-gate 		msg("cron audit problem. job failed (%s) for user %s",
24455558Sbasabi 		    e->cmd, e->u->name);
24460Sstevel@tonic-gate 		exit(1);
24470Sstevel@tonic-gate 	}
24480Sstevel@tonic-gate 
24490Sstevel@tonic-gate 	audit_cron_new_job(e->cmd, e->etype, (void *)e);
24500Sstevel@tonic-gate 
24510Sstevel@tonic-gate 	if (setuid(e->u->uid) == -1)  {
24520Sstevel@tonic-gate 		msg("setuid failed (%s)", e->u->name);
24530Sstevel@tonic-gate 		clean_out_user(e->u);
24540Sstevel@tonic-gate 		exit(1);
24550Sstevel@tonic-gate 	}
24560Sstevel@tonic-gate 
24570Sstevel@tonic-gate 	if (e->etype == CRONEVENT) {
24580Sstevel@tonic-gate 		/* check for standard input to command	*/
24590Sstevel@tonic-gate 		if (e->of.ct.input != NULL) {
24600Sstevel@tonic-gate 			if ((tmpfile = strdup(TMPINFILE)) == NULL) {
24610Sstevel@tonic-gate 				mail((e->u)->name, MALLOCERR,
24625558Sbasabi 				    ERR_CANTEXECCRON);
24630Sstevel@tonic-gate 				exit(1);
24640Sstevel@tonic-gate 			}
24650Sstevel@tonic-gate 			if ((fd = mkstemp(tmpfile)) == -1 ||
24665558Sbasabi 			    (fptr = fdopen(fd, "w")) == NULL) {
24670Sstevel@tonic-gate 				mail((e->u)->name, NOSTDIN,
24685558Sbasabi 				    ERR_CANTEXECCRON);
24690Sstevel@tonic-gate 				cron_unlink(tmpfile);
24700Sstevel@tonic-gate 				free(tmpfile);
24710Sstevel@tonic-gate 				exit(1);
24720Sstevel@tonic-gate 			}
24730Sstevel@tonic-gate 			if ((fwrite(e->of.ct.input, sizeof (char),
24745558Sbasabi 			    strlen(e->of.ct.input), fptr)) !=
24755558Sbasabi 			    strlen(e->of.ct.input)) {
24760Sstevel@tonic-gate 				mail((e->u)->name, NOSTDIN, ERR_CANTEXECCRON);
24770Sstevel@tonic-gate 				cron_unlink(tmpfile);
24780Sstevel@tonic-gate 				free(tmpfile);
24790Sstevel@tonic-gate 				(void) close(fd);
24800Sstevel@tonic-gate 				(void) fclose(fptr);
24810Sstevel@tonic-gate 				exit(1);
24820Sstevel@tonic-gate 			}
24830Sstevel@tonic-gate 			if (fseek(fptr, (off_t)0, SEEK_SET) != -1) {
24840Sstevel@tonic-gate 				if (fd != 0) {
24850Sstevel@tonic-gate 					(void) dup2(fd, 0);
24860Sstevel@tonic-gate 					(void) close(fd);
24870Sstevel@tonic-gate 				}
24880Sstevel@tonic-gate 			}
24890Sstevel@tonic-gate 			cron_unlink(tmpfile);
24900Sstevel@tonic-gate 			free(tmpfile);
24910Sstevel@tonic-gate 			(void) fclose(fptr);
24920Sstevel@tonic-gate 		} else if ((fd = open("/dev/null", O_RDONLY)) > 0) {
24930Sstevel@tonic-gate 			(void) dup2(fd, 0);
24940Sstevel@tonic-gate 			(void) close(fd);
24950Sstevel@tonic-gate 		}
24960Sstevel@tonic-gate 	}
24970Sstevel@tonic-gate 
24980Sstevel@tonic-gate 	/* redirect stdout and stderr for the shell	*/
24990Sstevel@tonic-gate 	if ((fd = open(rp->outfile, O_WRONLY|O_CREAT|O_EXCL, OUTMODE)) == 1)
25000Sstevel@tonic-gate 		fd = open("/dev/null", O_WRONLY);
25010Sstevel@tonic-gate 
25020Sstevel@tonic-gate 	if (fd >= 0 && fd != 1)
25030Sstevel@tonic-gate 		(void) dup2(fd, 1);
25040Sstevel@tonic-gate 
25050Sstevel@tonic-gate 	if (fd >= 0 && fd != 2) {
25060Sstevel@tonic-gate 		(void) dup2(fd, 2);
25070Sstevel@tonic-gate 		if (fd != 1)
25080Sstevel@tonic-gate 			(void) close(fd);
25090Sstevel@tonic-gate 	}
25100Sstevel@tonic-gate 
25118439SChris.Gerhard@sun.com 	if (e->etype == CRONEVENT && e->of.ct.home != NULL) {
25128439SChris.Gerhard@sun.com 		home = (char *)get_obj(e->of.ct.home);
25138439SChris.Gerhard@sun.com 	} else {
25148439SChris.Gerhard@sun.com 		home = (e->u)->home;
25158439SChris.Gerhard@sun.com 	}
25168439SChris.Gerhard@sun.com 	(void) strlcat(homedir, home, sizeof (homedir));
25170Sstevel@tonic-gate 	(void) strlcat(logname, (e->u)->name, sizeof (logname));
25180Sstevel@tonic-gate 	environ = envinit;
25198439SChris.Gerhard@sun.com 	if (chdir(home) == -1) {
25208439SChris.Gerhard@sun.com 		snprintf(bufs.error, sizeof (bufs.error), CANTCDHOME, home);
25218439SChris.Gerhard@sun.com 		mail((e->u)->name, bufs.error,
25225558Sbasabi 		    e->etype == CRONEVENT ? ERR_CANTEXECCRON :
25235558Sbasabi 		    ERR_CANTEXECAT);
25240Sstevel@tonic-gate 		exit(1);
25250Sstevel@tonic-gate 	}
25260Sstevel@tonic-gate #ifdef TESTING
25270Sstevel@tonic-gate 	exit(1);
25280Sstevel@tonic-gate #endif
25290Sstevel@tonic-gate 	/*
25300Sstevel@tonic-gate 	 * make sure that all file descriptors EXCEPT 0, 1 and 2
25310Sstevel@tonic-gate 	 * will be closed.
25320Sstevel@tonic-gate 	 */
25330Sstevel@tonic-gate 	closefrom(3);
25340Sstevel@tonic-gate 
25350Sstevel@tonic-gate 	if ((e->u)->uid != 0)
25360Sstevel@tonic-gate 		(void) nice(qp->nice);
25378439SChris.Gerhard@sun.com 	if (e->etype == CRONEVENT) {
25388439SChris.Gerhard@sun.com 		if (e->of.ct.tz) {
25398439SChris.Gerhard@sun.com 			(void) putenv((char *)get_obj(e->of.ct.tz));
25408439SChris.Gerhard@sun.com 		}
25418439SChris.Gerhard@sun.com 		if (e->of.ct.shell) {
25428439SChris.Gerhard@sun.com 			char *name;
25438439SChris.Gerhard@sun.com 
25448439SChris.Gerhard@sun.com 			sh = (char *)get_obj(e->of.ct.shell);
25458439SChris.Gerhard@sun.com 			name = strrchr(sh, '/');
25468439SChris.Gerhard@sun.com 			if (name == NULL)
25478439SChris.Gerhard@sun.com 				name = sh;
25488439SChris.Gerhard@sun.com 			else
25498439SChris.Gerhard@sun.com 				name++;
25508439SChris.Gerhard@sun.com 
25518439SChris.Gerhard@sun.com 			(void) putenv(sh);
25528439SChris.Gerhard@sun.com 			sh += strlen(ENV_SHELL);
25538439SChris.Gerhard@sun.com 			(void) execl(sh, name, "-c", e->cmd, 0);
25548439SChris.Gerhard@sun.com 		} else {
25558439SChris.Gerhard@sun.com 			(void) execl(SHELL, "sh", "-c", e->cmd, 0);
25568439SChris.Gerhard@sun.com 			sh = SHELL;
25578439SChris.Gerhard@sun.com 		}
25588439SChris.Gerhard@sun.com 	} else {		/* type == ATEVENT */
25590Sstevel@tonic-gate 		(void) execl(SHELL, "sh", 0);
25608439SChris.Gerhard@sun.com 		sh = SHELL;
25618439SChris.Gerhard@sun.com 	}
25628439SChris.Gerhard@sun.com 	snprintf(bufs.error, sizeof (bufs.error), CANTEXECSH, sh);
25638439SChris.Gerhard@sun.com 	mail((e->u)->name, bufs.error,
25645558Sbasabi 	    e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT);
25650Sstevel@tonic-gate 	exit(1);
25660Sstevel@tonic-gate 	/*NOTREACHED*/
25670Sstevel@tonic-gate }
25680Sstevel@tonic-gate 
2569*11115SNobutomo.Nakano@Sun.COM /*
2570*11115SNobutomo.Nakano@Sun.COM  * Main idle loop.
2571*11115SNobutomo.Nakano@Sun.COM  * When timed out to run the job, return 0.
2572*11115SNobutomo.Nakano@Sun.COM  * If for some reasons we need to reschedule jobs, return 1.
2573*11115SNobutomo.Nakano@Sun.COM  */
25740Sstevel@tonic-gate static int
idle(long t)25750Sstevel@tonic-gate idle(long t)
25760Sstevel@tonic-gate {
25770Sstevel@tonic-gate 	time_t	now;
25780Sstevel@tonic-gate 
2579*11115SNobutomo.Nakano@Sun.COM 	refresh = 0;
2580*11115SNobutomo.Nakano@Sun.COM 
25810Sstevel@tonic-gate 	while (t > 0L) {
25820Sstevel@tonic-gate 		if (msg_wait(t) != 0) {
25830Sstevel@tonic-gate 			/* we need to run next job immediately */
25840Sstevel@tonic-gate 			return (0);
25850Sstevel@tonic-gate 		}
25860Sstevel@tonic-gate 
25870Sstevel@tonic-gate 		reap_child();
25880Sstevel@tonic-gate 
2589*11115SNobutomo.Nakano@Sun.COM 		if (refresh) {
2590*11115SNobutomo.Nakano@Sun.COM 			/* We got THAW or REFRESH message  */
2591*11115SNobutomo.Nakano@Sun.COM 			return (1);
2592*11115SNobutomo.Nakano@Sun.COM 		}
2593*11115SNobutomo.Nakano@Sun.COM 
25940Sstevel@tonic-gate 		now = time(NULL);
25950Sstevel@tonic-gate 		if (last_time > now) {
2596*11115SNobutomo.Nakano@Sun.COM 			/* clock has been reset to backward */
25970Sstevel@tonic-gate 			return (1);
25980Sstevel@tonic-gate 		}
25990Sstevel@tonic-gate 
26000Sstevel@tonic-gate 		if (next_event == NULL && !el_empty()) {
26010Sstevel@tonic-gate 			next_event = (struct event *)el_first();
26020Sstevel@tonic-gate 		}
2603*11115SNobutomo.Nakano@Sun.COM 
26040Sstevel@tonic-gate 		if (next_event == NULL)
26050Sstevel@tonic-gate 			t = INFINITY;
26060Sstevel@tonic-gate 		else
26070Sstevel@tonic-gate 			t = (long)next_event->time - now;
26080Sstevel@tonic-gate 	}
26090Sstevel@tonic-gate 	return (0);
26100Sstevel@tonic-gate }
26110Sstevel@tonic-gate 
26120Sstevel@tonic-gate /*
26130Sstevel@tonic-gate  * This used to be in the idle(), but moved to the separate function.
26140Sstevel@tonic-gate  * This called from various place when cron needs to reap the
26150Sstevel@tonic-gate  * child. It includes the situation that cron hit maxrun, and needs
26160Sstevel@tonic-gate  * to reschedule the job.
26170Sstevel@tonic-gate  */
26180Sstevel@tonic-gate static void
reap_child()26190Sstevel@tonic-gate reap_child()
26200Sstevel@tonic-gate {
26210Sstevel@tonic-gate 	pid_t	pid;
26220Sstevel@tonic-gate 	int	prc;
26230Sstevel@tonic-gate 	struct	runinfo	*rp;
26240Sstevel@tonic-gate 
26250Sstevel@tonic-gate 	for (;;) {
26260Sstevel@tonic-gate 		pid = waitpid((pid_t)-1, &prc, WNOHANG);
26270Sstevel@tonic-gate 		if (pid <= 0)
26280Sstevel@tonic-gate 			break;
26290Sstevel@tonic-gate #ifdef DEBUG
26300Sstevel@tonic-gate 		fprintf(stderr,
26315558Sbasabi 		    "wait returned %x for process %d\n", prc, pid);
26320Sstevel@tonic-gate #endif
26330Sstevel@tonic-gate 		if ((rp = rinfo_get(pid)) == NULL) {
26340Sstevel@tonic-gate 			if (miscpid_delete(pid) == 0) {
26350Sstevel@tonic-gate 				/* not found in anywhere */
26360Sstevel@tonic-gate 				msg(PIDERR, pid);
26370Sstevel@tonic-gate 			}
26380Sstevel@tonic-gate 		} else if (rp->que == ZOMB) {
26390Sstevel@tonic-gate 			(void) unlink(rp->outfile);
26400Sstevel@tonic-gate 			rinfo_free(rp);
26410Sstevel@tonic-gate 		} else {
26420Sstevel@tonic-gate 			cleanup(rp, prc);
26430Sstevel@tonic-gate 		}
26440Sstevel@tonic-gate 	}
26450Sstevel@tonic-gate }
26460Sstevel@tonic-gate 
26470Sstevel@tonic-gate static void
cleanup(struct runinfo * pr,int rc)26480Sstevel@tonic-gate cleanup(struct runinfo *pr, int rc)
26490Sstevel@tonic-gate {
26500Sstevel@tonic-gate 	int	nextfork = 1;
26510Sstevel@tonic-gate 	struct	usr	*p;
26520Sstevel@tonic-gate 	struct	stat	buf;
26530Sstevel@tonic-gate 
26540Sstevel@tonic-gate 	logit(ECHAR, pr, rc);
26550Sstevel@tonic-gate 	--qt[pr->que].nrun;
26560Sstevel@tonic-gate 	p = pr->rusr;
26570Sstevel@tonic-gate 	if (pr->que != CRONEVENT)
26580Sstevel@tonic-gate 		--p->aruncnt;
26590Sstevel@tonic-gate 	else
26600Sstevel@tonic-gate 		--p->cruncnt;
26610Sstevel@tonic-gate 
2662871Scasper 	if (lstat(pr->outfile, &buf) == 0) {
2663871Scasper 		if (!S_ISLNK(buf.st_mode) &&
26640Sstevel@tonic-gate 		    (buf.st_size > 0 || pr->mailwhendone)) {
26650Sstevel@tonic-gate 			/* mail user stdout and stderr */
26660Sstevel@tonic-gate 			for (;;) {
26670Sstevel@tonic-gate 				if ((pr->pid = fork()) < 0) {
26680Sstevel@tonic-gate 					/*
26690Sstevel@tonic-gate 					 * if fork fails try forever in doubling
26700Sstevel@tonic-gate 					 * retry times, up to 16 seconds
26710Sstevel@tonic-gate 					 */
26720Sstevel@tonic-gate 					(void) sleep(nextfork);
26730Sstevel@tonic-gate 					if (nextfork < 16)
26740Sstevel@tonic-gate 						nextfork += nextfork;
26750Sstevel@tonic-gate 					continue;
26760Sstevel@tonic-gate 				} else if (pr->pid == 0) {
26770Sstevel@tonic-gate 					child_sigreset();
26780Sstevel@tonic-gate 					contract_clear_template();
26790Sstevel@tonic-gate 
26800Sstevel@tonic-gate 					mail_result(p, pr, buf.st_size);
26810Sstevel@tonic-gate 					/* NOTREACHED */
26820Sstevel@tonic-gate 				} else {
26830Sstevel@tonic-gate 					contract_abandon_latest(pr->pid);
26840Sstevel@tonic-gate 					pr->que = ZOMB;
26850Sstevel@tonic-gate 					break;
26860Sstevel@tonic-gate 				}
26870Sstevel@tonic-gate 			}
26880Sstevel@tonic-gate 		} else {
26890Sstevel@tonic-gate 			(void) unlink(pr->outfile);
26900Sstevel@tonic-gate 			rinfo_free(pr);
26910Sstevel@tonic-gate 		}
26920Sstevel@tonic-gate 	} else {
26930Sstevel@tonic-gate 		rinfo_free(pr);
26940Sstevel@tonic-gate 	}
26950Sstevel@tonic-gate 
26960Sstevel@tonic-gate 	free_if_unused(p);
26970Sstevel@tonic-gate }
26980Sstevel@tonic-gate 
26990Sstevel@tonic-gate /*
27000Sstevel@tonic-gate  * Mail stdout and stderr of a job to user. Get uid for real user and become
27010Sstevel@tonic-gate  * that person. We do this so that mail won't come from root since this
27020Sstevel@tonic-gate  * could be a security hole. If failure, quit - don't send mail as root.
27030Sstevel@tonic-gate  */
27040Sstevel@tonic-gate static void
mail_result(struct usr * p,struct runinfo * pr,size_t filesize)27050Sstevel@tonic-gate mail_result(struct usr *p, struct runinfo *pr, size_t filesize)
27060Sstevel@tonic-gate {
27070Sstevel@tonic-gate 	struct	passwd	*ruser_ids;
27080Sstevel@tonic-gate 	FILE	*mailpipe;
27090Sstevel@tonic-gate 	FILE	*st;
27100Sstevel@tonic-gate 	struct utsname	name;
27110Sstevel@tonic-gate 	int	nbytes;
27120Sstevel@tonic-gate 	char	iobuf[BUFSIZ];
27130Sstevel@tonic-gate 	char	*cmd;
27140Sstevel@tonic-gate 
27150Sstevel@tonic-gate 	(void) uname(&name);
27160Sstevel@tonic-gate 	if ((ruser_ids = getpwnam(p->name)) == NULL)
27170Sstevel@tonic-gate 		exit(0);
27180Sstevel@tonic-gate 	(void) setuid(ruser_ids->pw_uid);
27190Sstevel@tonic-gate 
27205558Sbasabi 	cmd = xmalloc(strlen(MAIL) + strlen(p->name)+2);
27210Sstevel@tonic-gate 	(void) sprintf(cmd, "%s %s", MAIL, p->name);
27220Sstevel@tonic-gate 	mailpipe = popen(cmd, "w");
27230Sstevel@tonic-gate 	free(cmd);
27240Sstevel@tonic-gate 	if (mailpipe == NULL)
27250Sstevel@tonic-gate 		exit(127);
27260Sstevel@tonic-gate 	(void) fprintf(mailpipe, "To: %s\n", p->name);
27270Sstevel@tonic-gate 	if (pr->jobtype == CRONEVENT) {
27280Sstevel@tonic-gate 		(void) fprintf(mailpipe, CRONOUT);
27290Sstevel@tonic-gate 		(void) fprintf(mailpipe, "Your \"cron\" job on %s\n",
27305558Sbasabi 		    name.nodename);
27310Sstevel@tonic-gate 		if (pr->jobname != NULL) {
27320Sstevel@tonic-gate 			(void) fprintf(mailpipe, "%s\n\n", pr->jobname);
27330Sstevel@tonic-gate 		}
27340Sstevel@tonic-gate 	} else {
27350Sstevel@tonic-gate 		(void) fprintf(mailpipe, "Subject: Output from \"at\" job\n\n");
27360Sstevel@tonic-gate 		(void) fprintf(mailpipe, "Your \"at\" job on %s\n",
27375558Sbasabi 		    name.nodename);
27380Sstevel@tonic-gate 		if (pr->jobname != NULL) {
27390Sstevel@tonic-gate 			(void) fprintf(mailpipe, "\"%s\"\n\n", pr->jobname);
27400Sstevel@tonic-gate 		}
27410Sstevel@tonic-gate 	}
27420Sstevel@tonic-gate 	/* Tmp. file is fopen'ed w/ "r",  secure open */
27430Sstevel@tonic-gate 	if (filesize > 0 &&
27440Sstevel@tonic-gate 	    (st = fopen(pr->outfile, "r")) != NULL) {
27450Sstevel@tonic-gate 		(void) fprintf(mailpipe,
27465558Sbasabi 		    "produced the following output:\n\n");
27470Sstevel@tonic-gate 		while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0)
27480Sstevel@tonic-gate 			(void) fwrite(iobuf, sizeof (char), nbytes, mailpipe);
27490Sstevel@tonic-gate 		(void) fclose(st);
27500Sstevel@tonic-gate 	} else {
27510Sstevel@tonic-gate 		(void) fprintf(mailpipe, "completed.\n");
27520Sstevel@tonic-gate 	}
27530Sstevel@tonic-gate 	(void) pclose(mailpipe);
27540Sstevel@tonic-gate 	exit(0);
27550Sstevel@tonic-gate }
27560Sstevel@tonic-gate 
27570Sstevel@tonic-gate static int
msg_wait(long tim)27580Sstevel@tonic-gate msg_wait(long tim)
27590Sstevel@tonic-gate {
27600Sstevel@tonic-gate 	struct	message	msg;
27610Sstevel@tonic-gate 	int	cnt;
27620Sstevel@tonic-gate 	time_t	reftime;
2763*11115SNobutomo.Nakano@Sun.COM 	fd_set	fds;
2764*11115SNobutomo.Nakano@Sun.COM 	struct timespec tout, *toutp;
27650Sstevel@tonic-gate 	static int	pending_msg;
27660Sstevel@tonic-gate 	static time_t	pending_reftime;
27670Sstevel@tonic-gate 
27680Sstevel@tonic-gate 	if (pending_msg) {
27690Sstevel@tonic-gate 		process_msg(&msgbuf, pending_reftime);
27700Sstevel@tonic-gate 		pending_msg = 0;
27710Sstevel@tonic-gate 		return (0);
27720Sstevel@tonic-gate 	}
27730Sstevel@tonic-gate 
2774*11115SNobutomo.Nakano@Sun.COM 	FD_ZERO(&fds);
2775*11115SNobutomo.Nakano@Sun.COM 	FD_SET(msgfd, &fds);
2776*11115SNobutomo.Nakano@Sun.COM 
2777*11115SNobutomo.Nakano@Sun.COM 	toutp = NULL;
2778*11115SNobutomo.Nakano@Sun.COM 	if (tim != INFINITY) {
27790Sstevel@tonic-gate #ifdef CRON_MAXSLEEP
2780*11115SNobutomo.Nakano@Sun.COM 		/*
2781*11115SNobutomo.Nakano@Sun.COM 		 * CRON_MAXSLEEP can be defined to have cron periodically wake
2782*11115SNobutomo.Nakano@Sun.COM 		 * up, so that cron can detect a change of TOD and adjust the
2783*11115SNobutomo.Nakano@Sun.COM 		 * sleep time more frequently.
2784*11115SNobutomo.Nakano@Sun.COM 		 */
2785*11115SNobutomo.Nakano@Sun.COM 		tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim;
27860Sstevel@tonic-gate #endif
2787*11115SNobutomo.Nakano@Sun.COM 		tout.tv_nsec = 0;
2788*11115SNobutomo.Nakano@Sun.COM 		tout.tv_sec = tim;
2789*11115SNobutomo.Nakano@Sun.COM 		toutp = &tout;
2790*11115SNobutomo.Nakano@Sun.COM 	}
2791*11115SNobutomo.Nakano@Sun.COM 
2792*11115SNobutomo.Nakano@Sun.COM 	cnt = pselect(msgfd + 1, &fds, NULL, NULL, toutp, &defmask);
2793*11115SNobutomo.Nakano@Sun.COM 	if (cnt == -1 && errno != EINTR)
2794*11115SNobutomo.Nakano@Sun.COM 		perror("! pselect");
2795*11115SNobutomo.Nakano@Sun.COM 
2796*11115SNobutomo.Nakano@Sun.COM 	/* pselect timeout or interrupted */
27970Sstevel@tonic-gate 	if (cnt <= 0)
27980Sstevel@tonic-gate 		return (0);
27990Sstevel@tonic-gate 
28000Sstevel@tonic-gate 	errno = 0;
28010Sstevel@tonic-gate 	if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) {
28020Sstevel@tonic-gate 		if (cnt != -1 || errno != EAGAIN)
28030Sstevel@tonic-gate 			perror("! read");
28040Sstevel@tonic-gate 		return (0);
28050Sstevel@tonic-gate 	}
28060Sstevel@tonic-gate 	reftime = time(NULL);
28070Sstevel@tonic-gate 	if (next_event != NULL && reftime >= next_event->time) {
28080Sstevel@tonic-gate 		/*
28090Sstevel@tonic-gate 		 * we need to run the job before reloading crontab.
28100Sstevel@tonic-gate 		 */
28110Sstevel@tonic-gate 		(void) memcpy(&msgbuf, &msg, sizeof (msg));
28120Sstevel@tonic-gate 		pending_msg = 1;
28130Sstevel@tonic-gate 		pending_reftime = reftime;
28140Sstevel@tonic-gate 		return (1);
28150Sstevel@tonic-gate 	}
28160Sstevel@tonic-gate 	process_msg(&msg, reftime);
28170Sstevel@tonic-gate 	return (0);
28180Sstevel@tonic-gate }
28190Sstevel@tonic-gate 
28200Sstevel@tonic-gate /*
28210Sstevel@tonic-gate  * process the message supplied via pipe. This will be called either
28220Sstevel@tonic-gate  * immediately after cron read the message from pipe, or idle time
28230Sstevel@tonic-gate  * if the message was pending due to the job execution.
28240Sstevel@tonic-gate  */
28250Sstevel@tonic-gate static void
process_msg(struct message * pmsg,time_t reftime)28260Sstevel@tonic-gate process_msg(struct message *pmsg, time_t reftime)
28270Sstevel@tonic-gate {
28280Sstevel@tonic-gate 	if (pmsg->etype == NULL)
28290Sstevel@tonic-gate 		return;
28300Sstevel@tonic-gate 
28310Sstevel@tonic-gate 	switch (pmsg->etype) {
28320Sstevel@tonic-gate 	case AT:
28330Sstevel@tonic-gate 		if (pmsg->action == DELETE)
28340Sstevel@tonic-gate 			del_atjob(pmsg->fname, pmsg->logname);
28350Sstevel@tonic-gate 		else
28360Sstevel@tonic-gate 			mod_atjob(pmsg->fname, (time_t)0);
28370Sstevel@tonic-gate 		break;
28380Sstevel@tonic-gate 	case CRON:
28390Sstevel@tonic-gate 		if (pmsg->action == DELETE)
28400Sstevel@tonic-gate 			del_ctab(pmsg->fname);
28410Sstevel@tonic-gate 		else
28420Sstevel@tonic-gate 			mod_ctab(pmsg->fname, reftime);
28430Sstevel@tonic-gate 		break;
2844*11115SNobutomo.Nakano@Sun.COM 	case REFRESH:
2845*11115SNobutomo.Nakano@Sun.COM 		refresh = 1;
2846*11115SNobutomo.Nakano@Sun.COM 		pmsg->etype = 0;
2847*11115SNobutomo.Nakano@Sun.COM 		return;
28480Sstevel@tonic-gate 	default:
28490Sstevel@tonic-gate 		msg("message received - bad format");
28500Sstevel@tonic-gate 		break;
28510Sstevel@tonic-gate 	}
28520Sstevel@tonic-gate 	if (next_event != NULL) {
28537879SSerge.Dussud@Sun.COM 		if (next_event->etype == CRONEVENT) {
28547879SSerge.Dussud@Sun.COM 			switch (el_add(next_event, next_event->time,
28557879SSerge.Dussud@Sun.COM 			    (next_event->u)->ctid)) {
28567879SSerge.Dussud@Sun.COM 			case -1:
28577879SSerge.Dussud@Sun.COM 				ignore_msg("process_msg", "cron", next_event);
28587879SSerge.Dussud@Sun.COM 				break;
28597879SSerge.Dussud@Sun.COM 			case -2: /* event time lower than init time */
28607879SSerge.Dussud@Sun.COM 				reset_needed = 1;
28617879SSerge.Dussud@Sun.COM 				break;
28627879SSerge.Dussud@Sun.COM 			}
28637879SSerge.Dussud@Sun.COM 		} else { /* etype == ATEVENT */
28647879SSerge.Dussud@Sun.COM 			if (el_add(next_event, next_event->time,
28657879SSerge.Dussud@Sun.COM 			    next_event->of.at.eventid) < 0) {
28667879SSerge.Dussud@Sun.COM 				ignore_msg("process_msg", "at", next_event);
28677879SSerge.Dussud@Sun.COM 			}
28687879SSerge.Dussud@Sun.COM 		}
28690Sstevel@tonic-gate 		next_event = NULL;
28700Sstevel@tonic-gate 	}
28710Sstevel@tonic-gate 	(void) fflush(stdout);
2872*11115SNobutomo.Nakano@Sun.COM 	pmsg->etype = 0;
28730Sstevel@tonic-gate }
28740Sstevel@tonic-gate 
2875801Sbasabi /*
2876801Sbasabi  * Allocate a new or find an existing runinfo structure
2877801Sbasabi  */
28780Sstevel@tonic-gate static struct runinfo *
rinfo_get(pid_t pid)28790Sstevel@tonic-gate rinfo_get(pid_t pid)
28800Sstevel@tonic-gate {
28810Sstevel@tonic-gate 	struct runinfo *rp;
28820Sstevel@tonic-gate 
2883801Sbasabi 	if (pid == 0) {		/* allocate a new entry */
2884801Sbasabi 		rp = xcalloc(1, sizeof (struct runinfo));
2885801Sbasabi 		rp->next = rthead;	/* link the entry into the list */
2886801Sbasabi 		rthead = rp;
2887801Sbasabi 		return (rp);
2888801Sbasabi 	}
2889801Sbasabi 	/* search the list for an existing entry */
2890801Sbasabi 	for (rp = rthead; rp != NULL; rp = rp->next) {
28910Sstevel@tonic-gate 		if (rp->pid == pid)
28920Sstevel@tonic-gate 			break;
28930Sstevel@tonic-gate 	}
2894801Sbasabi 	return (rp);
28950Sstevel@tonic-gate }
28960Sstevel@tonic-gate 
28970Sstevel@tonic-gate /*
2898801Sbasabi  * Free a runinfo structure and its associated memory
28990Sstevel@tonic-gate  */
29000Sstevel@tonic-gate static void
rinfo_free(struct runinfo * entry)2901801Sbasabi rinfo_free(struct runinfo *entry)
29020Sstevel@tonic-gate {
2903801Sbasabi 	struct runinfo **rpp;
2904801Sbasabi 	struct runinfo *rp;
2905801Sbasabi 
2906801Sbasabi #ifdef DEBUG
2907801Sbasabi 	(void) fprintf(stderr, "freeing job %s\n", entry->jobname);
2908801Sbasabi #endif
2909801Sbasabi 	for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) {
2910801Sbasabi 		if (rp == entry) {
2911801Sbasabi 			*rpp = rp->next;	/* unlink the entry */
2912801Sbasabi 			free(rp->outfile);
2913801Sbasabi 			free(rp->jobname);
2914801Sbasabi 			free(rp);
2915801Sbasabi 			break;
2916801Sbasabi 		}
2917801Sbasabi 	}
29180Sstevel@tonic-gate }
29190Sstevel@tonic-gate 
29200Sstevel@tonic-gate /* ARGSUSED */
29210Sstevel@tonic-gate static void
thaw_handler(int sig)29220Sstevel@tonic-gate thaw_handler(int sig)
29230Sstevel@tonic-gate {
2924*11115SNobutomo.Nakano@Sun.COM 	refresh = 1;
29250Sstevel@tonic-gate }
29260Sstevel@tonic-gate 
29270Sstevel@tonic-gate 
29280Sstevel@tonic-gate /* ARGSUSED */
29290Sstevel@tonic-gate static void
cronend(int sig)29300Sstevel@tonic-gate cronend(int sig)
29310Sstevel@tonic-gate {
29320Sstevel@tonic-gate 	crabort("SIGTERM", REMOVE_FIFO);
29330Sstevel@tonic-gate }
29340Sstevel@tonic-gate 
29350Sstevel@tonic-gate /*ARGSUSED*/
29360Sstevel@tonic-gate static void
child_handler(int sig)29370Sstevel@tonic-gate child_handler(int sig)
29380Sstevel@tonic-gate {
2939*11115SNobutomo.Nakano@Sun.COM 	;
29400Sstevel@tonic-gate }
29410Sstevel@tonic-gate 
29420Sstevel@tonic-gate static void
child_sigreset(void)29430Sstevel@tonic-gate child_sigreset(void)
29440Sstevel@tonic-gate {
29450Sstevel@tonic-gate 	(void) signal(SIGCLD, SIG_DFL);
29460Sstevel@tonic-gate 	(void) sigprocmask(SIG_SETMASK, &defmask, NULL);
29470Sstevel@tonic-gate }
29480Sstevel@tonic-gate 
29490Sstevel@tonic-gate /*
29500Sstevel@tonic-gate  * crabort() - handle exits out of cron
29510Sstevel@tonic-gate  */
29520Sstevel@tonic-gate static void
crabort(char * mssg,int action)29530Sstevel@tonic-gate crabort(char *mssg, int action)
29540Sstevel@tonic-gate {
29550Sstevel@tonic-gate 	int	c;
29560Sstevel@tonic-gate 
29570Sstevel@tonic-gate 	if (action & REMOVE_FIFO) {
29580Sstevel@tonic-gate 		/* FIFO vanishes when cron finishes */
29590Sstevel@tonic-gate 		if (unlink(FIFO) < 0)
29600Sstevel@tonic-gate 			perror("cron could not unlink FIFO");
29610Sstevel@tonic-gate 	}
29620Sstevel@tonic-gate 
29630Sstevel@tonic-gate 	if (action & CONSOLE_MSG) {
29640Sstevel@tonic-gate 		/* write error msg to console */
29650Sstevel@tonic-gate 		if ((c = open(CONSOLE, O_WRONLY)) >= 0) {
29660Sstevel@tonic-gate 			(void) write(c, "cron aborted: ", 14);
29670Sstevel@tonic-gate 			(void) write(c, mssg, strlen(mssg));
29680Sstevel@tonic-gate 			(void) write(c, "\n", 1);
29690Sstevel@tonic-gate 			(void) close(c);
29700Sstevel@tonic-gate 		}
29710Sstevel@tonic-gate 	}
29720Sstevel@tonic-gate 
29730Sstevel@tonic-gate 	/* always log the message */
29740Sstevel@tonic-gate 	msg(mssg);
29750Sstevel@tonic-gate 	msg("******* CRON ABORTED ********");
29760Sstevel@tonic-gate 	exit(1);
29770Sstevel@tonic-gate }
29780Sstevel@tonic-gate 
29790Sstevel@tonic-gate /*
29800Sstevel@tonic-gate  * msg() - time-stamped error reporting function
29810Sstevel@tonic-gate  */
29820Sstevel@tonic-gate /*PRINTFLIKE1*/
29830Sstevel@tonic-gate static void
msg(char * fmt,...)29840Sstevel@tonic-gate msg(char *fmt, ...)
29850Sstevel@tonic-gate {
29860Sstevel@tonic-gate 	va_list args;
29870Sstevel@tonic-gate 	time_t	t;
29880Sstevel@tonic-gate 
29890Sstevel@tonic-gate 	t = time(NULL);
29900Sstevel@tonic-gate 
29910Sstevel@tonic-gate 	(void) fflush(stdout);
29920Sstevel@tonic-gate 
29930Sstevel@tonic-gate 	(void) fprintf(stderr, "! ");
29940Sstevel@tonic-gate 
29950Sstevel@tonic-gate 	va_start(args, fmt);
29960Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, args);
29970Sstevel@tonic-gate 	va_end(args);
29980Sstevel@tonic-gate 
29990Sstevel@tonic-gate 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
30000Sstevel@tonic-gate 	(void) fprintf(stderr, " %s\n", timebuf);
30010Sstevel@tonic-gate 
30020Sstevel@tonic-gate 	(void) fflush(stderr);
30030Sstevel@tonic-gate }
30040Sstevel@tonic-gate 
30050Sstevel@tonic-gate static void
ignore_msg(char * func_name,char * job_type,struct event * event)30067879SSerge.Dussud@Sun.COM ignore_msg(char *func_name, char *job_type, struct event *event)
30077879SSerge.Dussud@Sun.COM {
30087879SSerge.Dussud@Sun.COM 	msg("%s: ignoring %s job (user: %s, cmd: %s, time: %ld)",
30097879SSerge.Dussud@Sun.COM 	    func_name, job_type,
30107879SSerge.Dussud@Sun.COM 	    event->u->name ? event->u->name : "unknown",
30117879SSerge.Dussud@Sun.COM 	    event->cmd ? event->cmd : "unknown",
30127879SSerge.Dussud@Sun.COM 	    event->time);
30137879SSerge.Dussud@Sun.COM }
30147879SSerge.Dussud@Sun.COM 
30157879SSerge.Dussud@Sun.COM static void
logit(int cc,struct runinfo * rp,int rc)30160Sstevel@tonic-gate logit(int cc, struct runinfo *rp, int rc)
30170Sstevel@tonic-gate {
30180Sstevel@tonic-gate 	time_t t;
30190Sstevel@tonic-gate 	int    ret;
30200Sstevel@tonic-gate 
30210Sstevel@tonic-gate 	if (!log)
30220Sstevel@tonic-gate 		return;
30230Sstevel@tonic-gate 
30240Sstevel@tonic-gate 	t = time(NULL);
30250Sstevel@tonic-gate 	if (cc == BCHAR)
30260Sstevel@tonic-gate 		(void) printf("%c  CMD: %s\n", cc, next_event->cmd);
30270Sstevel@tonic-gate 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
30280Sstevel@tonic-gate 	(void) printf("%c  %.8s %u %c %s",
30295558Sbasabi 	    cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf);
30300Sstevel@tonic-gate 	if ((ret = TSTAT(rc)) != 0)
30310Sstevel@tonic-gate 		(void) printf(" ts=%d", ret);
30320Sstevel@tonic-gate 	if ((ret = RCODE(rc)) != 0)
30330Sstevel@tonic-gate 		(void) printf(" rc=%d", ret);
30340Sstevel@tonic-gate 	(void) putchar('\n');
30350Sstevel@tonic-gate 	(void) fflush(stdout);
30360Sstevel@tonic-gate }
30370Sstevel@tonic-gate 
30380Sstevel@tonic-gate static void
resched(int delay)30390Sstevel@tonic-gate resched(int delay)
30400Sstevel@tonic-gate {
30410Sstevel@tonic-gate 	time_t	nt;
30420Sstevel@tonic-gate 
30430Sstevel@tonic-gate 	/* run job at a later time */
30440Sstevel@tonic-gate 	nt = next_event->time + delay;
30450Sstevel@tonic-gate 	if (next_event->etype == CRONEVENT) {
30460Sstevel@tonic-gate 		next_event->time = next_time(next_event, (time_t)0);
30470Sstevel@tonic-gate 		if (nt < next_event->time)
30480Sstevel@tonic-gate 			next_event->time = nt;
30497879SSerge.Dussud@Sun.COM 		switch (el_add(next_event, next_event->time,
30507879SSerge.Dussud@Sun.COM 		    (next_event->u)->ctid)) {
30517879SSerge.Dussud@Sun.COM 		case -1:
30527879SSerge.Dussud@Sun.COM 			ignore_msg("resched", "cron", next_event);
30537879SSerge.Dussud@Sun.COM 			break;
30547879SSerge.Dussud@Sun.COM 		case -2: /* event time lower than init time */
30557879SSerge.Dussud@Sun.COM 			reset_needed = 1;
30567879SSerge.Dussud@Sun.COM 			break;
30577879SSerge.Dussud@Sun.COM 		}
30580Sstevel@tonic-gate 		delayed = 1;
30590Sstevel@tonic-gate 		msg("rescheduling a cron job");
30600Sstevel@tonic-gate 		return;
30610Sstevel@tonic-gate 	}
30620Sstevel@tonic-gate 	add_atevent(next_event->u, next_event->cmd, nt, next_event->etype);
30630Sstevel@tonic-gate 	msg("rescheduling at job");
30640Sstevel@tonic-gate }
30650Sstevel@tonic-gate 
30660Sstevel@tonic-gate static void
quedefs(int action)30670Sstevel@tonic-gate quedefs(int action)
30680Sstevel@tonic-gate {
30690Sstevel@tonic-gate 	int	i;
30700Sstevel@tonic-gate 	int	j;
30710Sstevel@tonic-gate 	char	qbuf[QBUFSIZ];
30720Sstevel@tonic-gate 	FILE	*fd;
30730Sstevel@tonic-gate 
30740Sstevel@tonic-gate 	/* set up default queue definitions */
30750Sstevel@tonic-gate 	for (i = 0; i < NQUEUE; i++) {
30760Sstevel@tonic-gate 		qt[i].njob = qd.njob;
30770Sstevel@tonic-gate 		qt[i].nice = qd.nice;
30780Sstevel@tonic-gate 		qt[i].nwait = qd.nwait;
30790Sstevel@tonic-gate 	}
30800Sstevel@tonic-gate 	if (action == DEFAULT)
30810Sstevel@tonic-gate 		return;
30820Sstevel@tonic-gate 	if ((fd = fopen(QUEDEFS, "r")) == NULL) {
30830Sstevel@tonic-gate 		msg("cannot open quedefs file");
30840Sstevel@tonic-gate 		msg("using default queue definitions");
30850Sstevel@tonic-gate 		return;
30860Sstevel@tonic-gate 	}
30870Sstevel@tonic-gate 	while (fgets(qbuf, QBUFSIZ, fd) != NULL) {
30880Sstevel@tonic-gate 		if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.')
30890Sstevel@tonic-gate 			continue;
30900Sstevel@tonic-gate 		parsqdef(&qbuf[2]);
30910Sstevel@tonic-gate 		qt[j].njob = qq.njob;
30920Sstevel@tonic-gate 		qt[j].nice = qq.nice;
30930Sstevel@tonic-gate 		qt[j].nwait = qq.nwait;
30940Sstevel@tonic-gate 	}
30950Sstevel@tonic-gate 	(void) fclose(fd);
30960Sstevel@tonic-gate }
30970Sstevel@tonic-gate 
30980Sstevel@tonic-gate static void
parsqdef(char * name)30990Sstevel@tonic-gate parsqdef(char *name)
31000Sstevel@tonic-gate {
31010Sstevel@tonic-gate 	int i;
31020Sstevel@tonic-gate 
31030Sstevel@tonic-gate 	qq = qd;
31040Sstevel@tonic-gate 	while (*name) {
31050Sstevel@tonic-gate 		i = 0;
31060Sstevel@tonic-gate 		while (isdigit(*name)) {
31070Sstevel@tonic-gate 			i *= 10;
31080Sstevel@tonic-gate 			i += *name++ - '0';
31090Sstevel@tonic-gate 		}
31100Sstevel@tonic-gate 		switch (*name++) {
31110Sstevel@tonic-gate 		case JOBF:
31120Sstevel@tonic-gate 			qq.njob = i;
31130Sstevel@tonic-gate 			break;
31140Sstevel@tonic-gate 		case NICEF:
31150Sstevel@tonic-gate 			qq.nice = i;
31160Sstevel@tonic-gate 			break;
31170Sstevel@tonic-gate 		case WAITF:
31180Sstevel@tonic-gate 			qq.nwait = i;
31190Sstevel@tonic-gate 			break;
31200Sstevel@tonic-gate 		}
31210Sstevel@tonic-gate 	}
31220Sstevel@tonic-gate }
31230Sstevel@tonic-gate 
31240Sstevel@tonic-gate /*
31250Sstevel@tonic-gate  * defaults - read defaults from /etc/default/cron
31260Sstevel@tonic-gate  */
31270Sstevel@tonic-gate static void
defaults()31280Sstevel@tonic-gate defaults()
31290Sstevel@tonic-gate {
31300Sstevel@tonic-gate 	int  flags;
31310Sstevel@tonic-gate 	char *deflog;
31320Sstevel@tonic-gate 	char *hz, *tz;
31330Sstevel@tonic-gate 
31340Sstevel@tonic-gate 	/*
31350Sstevel@tonic-gate 	 * get HZ value for environment
31360Sstevel@tonic-gate 	 */
31370Sstevel@tonic-gate 	if ((hz = getenv("HZ")) == (char *)NULL)
31380Sstevel@tonic-gate 		(void) sprintf(hzname, "HZ=%d", HZ);
31390Sstevel@tonic-gate 	else
31400Sstevel@tonic-gate 		(void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz);
31410Sstevel@tonic-gate 	/*
31420Sstevel@tonic-gate 	 * get TZ value for environment
31430Sstevel@tonic-gate 	 */
31440Sstevel@tonic-gate 	(void) snprintf(tzone, sizeof (tzone), "TZ=%s",
31455558Sbasabi 	    ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ);
31460Sstevel@tonic-gate 
31470Sstevel@tonic-gate 	if (defopen(DEFFILE) == 0) {
31480Sstevel@tonic-gate 		/* ignore case */
31490Sstevel@tonic-gate 		flags = defcntl(DC_GETFLAGS, 0);
31500Sstevel@tonic-gate 		TURNOFF(flags, DC_CASE);
31510Sstevel@tonic-gate 		(void) defcntl(DC_SETFLAGS, flags);
31520Sstevel@tonic-gate 
31530Sstevel@tonic-gate 		if (((deflog = defread("CRONLOG=")) == NULL) ||
31540Sstevel@tonic-gate 		    (*deflog == 'N') || (*deflog == 'n'))
31550Sstevel@tonic-gate 			log = 0;
31560Sstevel@tonic-gate 		else
31570Sstevel@tonic-gate 			log = 1;
31580Sstevel@tonic-gate 		/* fix for 1087611 - allow paths to be set in defaults file */
31590Sstevel@tonic-gate 		if ((Def_path = defread("PATH=")) != NULL) {
31600Sstevel@tonic-gate 			(void) strlcat(path, Def_path, LINE_MAX);
31610Sstevel@tonic-gate 		} else {
31620Sstevel@tonic-gate 			(void) strlcpy(path, NONROOTPATH, LINE_MAX);
31630Sstevel@tonic-gate 		}
31640Sstevel@tonic-gate 		if ((Def_supath = defread("SUPATH=")) != NULL) {
31650Sstevel@tonic-gate 			(void) strlcat(supath, Def_supath, LINE_MAX);
31660Sstevel@tonic-gate 		} else {
31670Sstevel@tonic-gate 			(void) strlcpy(supath, ROOTPATH, LINE_MAX);
31680Sstevel@tonic-gate 		}
31690Sstevel@tonic-gate 		(void) defopen(NULL);
31700Sstevel@tonic-gate 	}
31710Sstevel@tonic-gate }
31720Sstevel@tonic-gate 
31730Sstevel@tonic-gate /*
31740Sstevel@tonic-gate  * Determine if a user entry for a job is still ok.  The method used here
31750Sstevel@tonic-gate  * is a lot (about 75x) faster than using setgrent() / getgrent()
31760Sstevel@tonic-gate  * endgrent().  It should be safe because we use the sysconf to determine
31770Sstevel@tonic-gate  * the max, and it tolerates the max being 0.
31780Sstevel@tonic-gate  */
31790Sstevel@tonic-gate 
31800Sstevel@tonic-gate static int
verify_user_cred(struct usr * u)31815558Sbasabi verify_user_cred(struct usr *u)
31820Sstevel@tonic-gate {
31830Sstevel@tonic-gate 	struct passwd *pw;
31840Sstevel@tonic-gate 	size_t numUsrGrps = 0;
31850Sstevel@tonic-gate 	size_t numOrigGrps = 0;
31860Sstevel@tonic-gate 	size_t i;
31870Sstevel@tonic-gate 	int retval;
31880Sstevel@tonic-gate 
31890Sstevel@tonic-gate 	/*
31900Sstevel@tonic-gate 	 * Maximum number of groups a user may be in concurrently.  This
31910Sstevel@tonic-gate 	 * is a value which we obtain at runtime through a sysconf()
31920Sstevel@tonic-gate 	 * call.
31930Sstevel@tonic-gate 	 */
31940Sstevel@tonic-gate 
31950Sstevel@tonic-gate 	static size_t nGroupsMax = (size_t)-1;
31960Sstevel@tonic-gate 
31970Sstevel@tonic-gate 	/*
31980Sstevel@tonic-gate 	 * Arrays for cron user's group list, constructed at startup to
31990Sstevel@tonic-gate 	 * be nGroupsMax elements long, used for verifying user
32000Sstevel@tonic-gate 	 * credentials prior to execution.
32010Sstevel@tonic-gate 	 */
32020Sstevel@tonic-gate 
32030Sstevel@tonic-gate 	static gid_t *UsrGrps;
32040Sstevel@tonic-gate 	static gid_t *OrigGrps;
32050Sstevel@tonic-gate 
32065558Sbasabi 	if ((pw = getpwnam(u->name)) == NULL)
32070Sstevel@tonic-gate 		return (VUC_BADUSER);
32085558Sbasabi 	if (u->home != NULL) {
32095558Sbasabi 		if (strcmp(u->home, pw->pw_dir) != 0) {
32105558Sbasabi 			free(u->home);
32115558Sbasabi 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
32125558Sbasabi 			(void) strcpy(u->home, pw->pw_dir);
32135558Sbasabi 		}
32145558Sbasabi 	} else {
32155558Sbasabi 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
32165558Sbasabi 		(void) strcpy(u->home, pw->pw_dir);
32170Sstevel@tonic-gate 	}
32185558Sbasabi 	if (u->uid != pw->pw_uid)
32195558Sbasabi 		u->uid = pw->pw_uid;
32205558Sbasabi 	if (u->gid != pw->pw_gid)
32215558Sbasabi 		u->gid  = pw->pw_gid;
32220Sstevel@tonic-gate 
32230Sstevel@tonic-gate 	/*
32240Sstevel@tonic-gate 	 * Create the group id lists needed for job credential
32250Sstevel@tonic-gate 	 * verification.
32260Sstevel@tonic-gate 	 */
32270Sstevel@tonic-gate 
32280Sstevel@tonic-gate 	if (nGroupsMax == (size_t)-1) {
32290Sstevel@tonic-gate 		if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) {
32300Sstevel@tonic-gate 			UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t));
32310Sstevel@tonic-gate 			OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t));
32320Sstevel@tonic-gate 		}
32330Sstevel@tonic-gate 
32340Sstevel@tonic-gate #ifdef DEBUG
32350Sstevel@tonic-gate 		(void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax);
32360Sstevel@tonic-gate #endif
32370Sstevel@tonic-gate 	}
32380Sstevel@tonic-gate 
32390Sstevel@tonic-gate #ifdef DEBUG
32400Sstevel@tonic-gate 	(void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name,
32410Sstevel@tonic-gate 	    pw->pw_uid);
32420Sstevel@tonic-gate 	(void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, "
32430Sstevel@tonic-gate 	    "u->gid = %d\n", pw->pw_gid, u->gid);
32440Sstevel@tonic-gate #endif
32450Sstevel@tonic-gate 
32460Sstevel@tonic-gate 	retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP;
32470Sstevel@tonic-gate 
32480Sstevel@tonic-gate 	if (nGroupsMax > 0) {
32490Sstevel@tonic-gate 		numOrigGrps = getgroups(nGroupsMax, OrigGrps);
32500Sstevel@tonic-gate 
32510Sstevel@tonic-gate 		(void) initgroups(pw->pw_name, pw->pw_gid);
32520Sstevel@tonic-gate 		numUsrGrps = getgroups(nGroupsMax, UsrGrps);
32530Sstevel@tonic-gate 
32540Sstevel@tonic-gate 		for (i = 0; i < numUsrGrps; i++) {
32550Sstevel@tonic-gate 			if (UsrGrps[i] == u->gid) {
32560Sstevel@tonic-gate 				retval = VUC_OK;
32570Sstevel@tonic-gate 				break;
32580Sstevel@tonic-gate 			}
32590Sstevel@tonic-gate 		}
32600Sstevel@tonic-gate 
32610Sstevel@tonic-gate 		if (OrigGrps) {
32620Sstevel@tonic-gate 			(void) setgroups(numOrigGrps, OrigGrps);
32630Sstevel@tonic-gate 		}
32640Sstevel@tonic-gate 	}
32650Sstevel@tonic-gate 
32660Sstevel@tonic-gate #ifdef DEBUG
32670Sstevel@tonic-gate 	(void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval);
32680Sstevel@tonic-gate #endif
32690Sstevel@tonic-gate 
32700Sstevel@tonic-gate 	return (retval);
32710Sstevel@tonic-gate }
32720Sstevel@tonic-gate 
32730Sstevel@tonic-gate static int
set_user_cred(const struct usr * u,struct project * pproj)32740Sstevel@tonic-gate set_user_cred(const struct usr *u, struct project *pproj)
32750Sstevel@tonic-gate {
32760Sstevel@tonic-gate 	static char *progname = "cron";
32770Sstevel@tonic-gate 	int r = 0, rval = 0;
32780Sstevel@tonic-gate 
32790Sstevel@tonic-gate 	if ((r = pam_start(progname, u->name, &pam_conv, &pamh))
32805558Sbasabi 	    != PAM_SUCCESS) {
32810Sstevel@tonic-gate #ifdef DEBUG
32820Sstevel@tonic-gate 		msg("pam_start returns %d\n", r);
32830Sstevel@tonic-gate #endif
32840Sstevel@tonic-gate 		rval = VUC_BADUSER;
32850Sstevel@tonic-gate 		goto set_eser_cred_exit;
32860Sstevel@tonic-gate 	}
32870Sstevel@tonic-gate 
32880Sstevel@tonic-gate 	r = pam_acct_mgmt(pamh, 0);
32890Sstevel@tonic-gate #ifdef DEBUG
32900Sstevel@tonic-gate 	msg("pam_acc_mgmt returns %d\n", r);
32910Sstevel@tonic-gate #endif
32920Sstevel@tonic-gate 	if (r == PAM_ACCT_EXPIRED) {
32930Sstevel@tonic-gate 		rval = VUC_EXPIRED;
32940Sstevel@tonic-gate 		goto set_eser_cred_exit;
32950Sstevel@tonic-gate 	}
32960Sstevel@tonic-gate 	if (r == PAM_NEW_AUTHTOK_REQD) {
32970Sstevel@tonic-gate 		rval = VUC_NEW_AUTH;
32980Sstevel@tonic-gate 		goto set_eser_cred_exit;
32990Sstevel@tonic-gate 	}
33000Sstevel@tonic-gate 	if (r != PAM_SUCCESS) {
33010Sstevel@tonic-gate 		rval = VUC_BADUSER;
33020Sstevel@tonic-gate 		goto set_eser_cred_exit;
33030Sstevel@tonic-gate 	}
33040Sstevel@tonic-gate 
33050Sstevel@tonic-gate 	if (pproj != NULL) {
33060Sstevel@tonic-gate 		size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name);
33070Sstevel@tonic-gate 		char *buf = alloca(sz);
33080Sstevel@tonic-gate 
33090Sstevel@tonic-gate 		(void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name);
33100Sstevel@tonic-gate 		(void) pam_set_item(pamh, PAM_RESOURCE, buf);
33110Sstevel@tonic-gate 	}
33120Sstevel@tonic-gate 
33130Sstevel@tonic-gate 	r = pam_setcred(pamh, PAM_ESTABLISH_CRED);
33140Sstevel@tonic-gate 	if (r != PAM_SUCCESS)
33150Sstevel@tonic-gate 		rval = VUC_BADUSER;
33160Sstevel@tonic-gate 
33170Sstevel@tonic-gate set_eser_cred_exit:
33180Sstevel@tonic-gate 	(void) pam_end(pamh, r);
33190Sstevel@tonic-gate 	return (rval);
33200Sstevel@tonic-gate }
33210Sstevel@tonic-gate 
33220Sstevel@tonic-gate static void
clean_out_user(struct usr * u)33230Sstevel@tonic-gate clean_out_user(struct usr *u)
33240Sstevel@tonic-gate {
33250Sstevel@tonic-gate 	if (next_event->u == u) {
33260Sstevel@tonic-gate 		next_event = NULL;
33270Sstevel@tonic-gate 	}
33280Sstevel@tonic-gate 
33290Sstevel@tonic-gate 	clean_out_ctab(u);
33300Sstevel@tonic-gate 	clean_out_atjobs(u);
33310Sstevel@tonic-gate 	free_if_unused(u);
33320Sstevel@tonic-gate }
33330Sstevel@tonic-gate 
33340Sstevel@tonic-gate static void
clean_out_atjobs(struct usr * u)33350Sstevel@tonic-gate clean_out_atjobs(struct usr *u)
33360Sstevel@tonic-gate {
33370Sstevel@tonic-gate 	struct event *ev, *pv;
33380Sstevel@tonic-gate 
33390Sstevel@tonic-gate 	for (pv = NULL, ev = u->atevents;
33405558Sbasabi 	    ev != NULL;
33415558Sbasabi 	    pv = ev, ev = ev->link, free(pv)) {
33420Sstevel@tonic-gate 		el_remove(ev->of.at.eventid, 1);
33430Sstevel@tonic-gate 		if (cwd == AT)
33440Sstevel@tonic-gate 			cron_unlink(ev->cmd);
33450Sstevel@tonic-gate 		else {
33460Sstevel@tonic-gate 			char buf[PATH_MAX];
33470Sstevel@tonic-gate 			if (strlen(ATDIR) + strlen(ev->cmd) + 2
33485558Sbasabi 			    < PATH_MAX) {
33490Sstevel@tonic-gate 				(void) sprintf(buf, "%s/%s", ATDIR, ev->cmd);
33500Sstevel@tonic-gate 				cron_unlink(buf);
33510Sstevel@tonic-gate 			}
33520Sstevel@tonic-gate 		}
33530Sstevel@tonic-gate 		free(ev->cmd);
33540Sstevel@tonic-gate 	}
33550Sstevel@tonic-gate 
33560Sstevel@tonic-gate 	u->atevents = NULL;
33570Sstevel@tonic-gate }
33580Sstevel@tonic-gate 
33590Sstevel@tonic-gate static void
clean_out_ctab(struct usr * u)33600Sstevel@tonic-gate clean_out_ctab(struct usr *u)
33610Sstevel@tonic-gate {
33620Sstevel@tonic-gate 	rm_ctevents(u);
33630Sstevel@tonic-gate 	el_remove(u->ctid, 0);
33640Sstevel@tonic-gate 	u->ctid = 0;
33650Sstevel@tonic-gate 	u->ctexists = 0;
33660Sstevel@tonic-gate }
33670Sstevel@tonic-gate 
33680Sstevel@tonic-gate static void
cron_unlink(char * name)33690Sstevel@tonic-gate cron_unlink(char *name)
33700Sstevel@tonic-gate {
33710Sstevel@tonic-gate 	int r;
33720Sstevel@tonic-gate 
33730Sstevel@tonic-gate 	r = unlink(name);
33740Sstevel@tonic-gate 	if (r == 0 || (r == -1 && errno == ENOENT)) {
33750Sstevel@tonic-gate 		(void) audit_cron_delete_anc_file(name, NULL);
33760Sstevel@tonic-gate 	}
33770Sstevel@tonic-gate }
33780Sstevel@tonic-gate 
33790Sstevel@tonic-gate static void
create_anc_ctab(struct event * e)33800Sstevel@tonic-gate create_anc_ctab(struct event *e)
33810Sstevel@tonic-gate {
33820Sstevel@tonic-gate 	if (audit_cron_create_anc_file(e->u->name,
33835558Sbasabi 	    (cwd == CRON) ? NULL:CRONDIR,
33845558Sbasabi 	    e->u->name, e->u->uid) == -1) {
33850Sstevel@tonic-gate 		process_anc_files(CRON_ANC_DELETE);
33860Sstevel@tonic-gate 		crabort("cannot create ancillary files for crontabs",
33875558Sbasabi 		    REMOVE_FIFO|CONSOLE_MSG);
33880Sstevel@tonic-gate 	}
33890Sstevel@tonic-gate }
33900Sstevel@tonic-gate 
33910Sstevel@tonic-gate static void
delete_anc_ctab(struct event * e)33920Sstevel@tonic-gate delete_anc_ctab(struct event *e)
33930Sstevel@tonic-gate {
33940Sstevel@tonic-gate 	(void) audit_cron_delete_anc_file(e->u->name,
33955558Sbasabi 	    (cwd == CRON) ? NULL:CRONDIR);
33960Sstevel@tonic-gate }
33970Sstevel@tonic-gate 
33980Sstevel@tonic-gate static void
create_anc_atjob(struct event * e)33990Sstevel@tonic-gate create_anc_atjob(struct event *e)
34000Sstevel@tonic-gate {
34010Sstevel@tonic-gate 	if (!e->of.at.exists)
34020Sstevel@tonic-gate 		return;
34030Sstevel@tonic-gate 
34040Sstevel@tonic-gate 	if (audit_cron_create_anc_file(e->cmd,
34055558Sbasabi 	    (cwd == AT) ? NULL:ATDIR,
34065558Sbasabi 	    e->u->name, e->u->uid) == -1) {
34070Sstevel@tonic-gate 		process_anc_files(CRON_ANC_DELETE);
34080Sstevel@tonic-gate 		crabort("cannot create ancillary files for atjobs",
34095558Sbasabi 		    REMOVE_FIFO|CONSOLE_MSG);
34100Sstevel@tonic-gate 	}
34110Sstevel@tonic-gate }
34120Sstevel@tonic-gate 
34130Sstevel@tonic-gate static void
delete_anc_atjob(struct event * e)34140Sstevel@tonic-gate delete_anc_atjob(struct event *e)
34150Sstevel@tonic-gate {
34160Sstevel@tonic-gate 	if (!e->of.at.exists)
34170Sstevel@tonic-gate 		return;
34180Sstevel@tonic-gate 
34190Sstevel@tonic-gate 	(void) audit_cron_delete_anc_file(e->cmd,
34205558Sbasabi 	    (cwd == AT) ? NULL:ATDIR);
34210Sstevel@tonic-gate }
34220Sstevel@tonic-gate 
34230Sstevel@tonic-gate 
34240Sstevel@tonic-gate static void
process_anc_files(int del)34250Sstevel@tonic-gate process_anc_files(int del)
34260Sstevel@tonic-gate {
34270Sstevel@tonic-gate 	struct usr	*u = uhead;
34280Sstevel@tonic-gate 	struct event	*e;
34290Sstevel@tonic-gate 
34300Sstevel@tonic-gate 	if (!audit_cron_mode())
34310Sstevel@tonic-gate 		return;
34320Sstevel@tonic-gate 
34330Sstevel@tonic-gate 	for (;;) {
34340Sstevel@tonic-gate 		if (u->ctexists && u->ctevents != NULL) {
34350Sstevel@tonic-gate 			e = u->ctevents;
34360Sstevel@tonic-gate 			for (;;) {
34370Sstevel@tonic-gate 				if (del)
34380Sstevel@tonic-gate 					delete_anc_ctab(e);
34390Sstevel@tonic-gate 				else
34400Sstevel@tonic-gate 					create_anc_ctab(e);
34410Sstevel@tonic-gate 				if ((e = e->link) == NULL)
34420Sstevel@tonic-gate 					break;
34430Sstevel@tonic-gate 			}
34440Sstevel@tonic-gate 		}
34450Sstevel@tonic-gate 
34460Sstevel@tonic-gate 		if (u->atevents != NULL) {
34470Sstevel@tonic-gate 			e = u->atevents;
34480Sstevel@tonic-gate 			for (;;) {
34490Sstevel@tonic-gate 				if (del)
34500Sstevel@tonic-gate 					delete_anc_atjob(e);
34510Sstevel@tonic-gate 				else
34520Sstevel@tonic-gate 					create_anc_atjob(e);
34530Sstevel@tonic-gate 				if ((e = e->link) == NULL)
34540Sstevel@tonic-gate 					break;
34550Sstevel@tonic-gate 			}
34560Sstevel@tonic-gate 		}
34570Sstevel@tonic-gate 
34580Sstevel@tonic-gate 		if ((u = u->nextusr)  == NULL)
34590Sstevel@tonic-gate 			break;
34600Sstevel@tonic-gate 	}
34610Sstevel@tonic-gate }
34620Sstevel@tonic-gate 
34630Sstevel@tonic-gate /*ARGSUSED*/
34640Sstevel@tonic-gate static int
cron_conv(int num_msg,struct pam_message ** msgs,struct pam_response ** response,void * appdata_ptr)34650Sstevel@tonic-gate cron_conv(int num_msg, struct pam_message **msgs,
34660Sstevel@tonic-gate     struct pam_response **response, void *appdata_ptr)
34670Sstevel@tonic-gate {
34680Sstevel@tonic-gate 	struct pam_message	**m = msgs;
34690Sstevel@tonic-gate 	int i;
34700Sstevel@tonic-gate 
34710Sstevel@tonic-gate 	for (i = 0; i < num_msg; i++) {
34720Sstevel@tonic-gate 		switch (m[i]->msg_style) {
34730Sstevel@tonic-gate 		case PAM_ERROR_MSG:
34740Sstevel@tonic-gate 		case PAM_TEXT_INFO:
34750Sstevel@tonic-gate 			if (m[i]->msg != NULL) {
34760Sstevel@tonic-gate 				(void) msg("%s\n", m[i]->msg);
34770Sstevel@tonic-gate 			}
34780Sstevel@tonic-gate 			break;
34790Sstevel@tonic-gate 
34800Sstevel@tonic-gate 		default:
34810Sstevel@tonic-gate 			break;
34820Sstevel@tonic-gate 		}
34830Sstevel@tonic-gate 	}
34840Sstevel@tonic-gate 	return (0);
34850Sstevel@tonic-gate }
34860Sstevel@tonic-gate 
34870Sstevel@tonic-gate /*
34880Sstevel@tonic-gate  * Cron creates process for other than job. Mail process is the
34890Sstevel@tonic-gate  * one which rinfo does not cover. Therefore, miscpid will keep
34900Sstevel@tonic-gate  * track of the pids executed from cron. Otherwise, we will see
34910Sstevel@tonic-gate  * "unexpected pid returned.." messages appear in the log file.
34920Sstevel@tonic-gate  */
34930Sstevel@tonic-gate static void
miscpid_insert(pid_t pid)34940Sstevel@tonic-gate miscpid_insert(pid_t pid)
34950Sstevel@tonic-gate {
34960Sstevel@tonic-gate 	struct miscpid *mp;
34970Sstevel@tonic-gate 
34980Sstevel@tonic-gate 	mp = xmalloc(sizeof (*mp));
34990Sstevel@tonic-gate 	mp->pid = pid;
35000Sstevel@tonic-gate 	mp->next = miscpid_head;
35010Sstevel@tonic-gate 	miscpid_head = mp;
35020Sstevel@tonic-gate }
35030Sstevel@tonic-gate 
35040Sstevel@tonic-gate static int
miscpid_delete(pid_t pid)35050Sstevel@tonic-gate miscpid_delete(pid_t pid)
35060Sstevel@tonic-gate {
35070Sstevel@tonic-gate 	struct miscpid *mp, *omp;
35080Sstevel@tonic-gate 	int found = 0;
35090Sstevel@tonic-gate 
35100Sstevel@tonic-gate 	omp = NULL;
35110Sstevel@tonic-gate 	for (mp = miscpid_head; mp != NULL; mp = mp->next) {
35120Sstevel@tonic-gate 		if (mp->pid == pid) {
35130Sstevel@tonic-gate 			found = 1;
35140Sstevel@tonic-gate 			break;
35150Sstevel@tonic-gate 		}
35160Sstevel@tonic-gate 		omp = mp;
35170Sstevel@tonic-gate 	}
35180Sstevel@tonic-gate 	if (found) {
35190Sstevel@tonic-gate 		if (omp != NULL)
35200Sstevel@tonic-gate 			omp->next = mp->next;
35210Sstevel@tonic-gate 		else
35220Sstevel@tonic-gate 			miscpid_head = NULL;
35230Sstevel@tonic-gate 		free(mp);
35240Sstevel@tonic-gate 	}
35250Sstevel@tonic-gate 	return (found);
35260Sstevel@tonic-gate }
35270Sstevel@tonic-gate 
35280Sstevel@tonic-gate /*
35290Sstevel@tonic-gate  * Establish contract terms such that all children are in abandoned
35300Sstevel@tonic-gate  * process contracts.
35310Sstevel@tonic-gate  */
3532523Sbasabi static void
contract_set_template(void)3533523Sbasabi contract_set_template(void)
35340Sstevel@tonic-gate {
35350Sstevel@tonic-gate 	int fd;
35360Sstevel@tonic-gate 
35370Sstevel@tonic-gate 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
35380Sstevel@tonic-gate 		crabort("cannot open process contract template",
35390Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35400Sstevel@tonic-gate 
35410Sstevel@tonic-gate 	if (ct_pr_tmpl_set_param(fd, 0) ||
35420Sstevel@tonic-gate 	    ct_tmpl_set_informative(fd, 0) ||
35430Sstevel@tonic-gate 	    ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR))
35440Sstevel@tonic-gate 		crabort("cannot establish contract template terms",
35450Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35460Sstevel@tonic-gate 
35470Sstevel@tonic-gate 	if (ct_tmpl_activate(fd))
35480Sstevel@tonic-gate 		crabort("cannot activate contract template",
35490Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35500Sstevel@tonic-gate 
35510Sstevel@tonic-gate 	(void) close(fd);
35520Sstevel@tonic-gate }
35530Sstevel@tonic-gate 
35540Sstevel@tonic-gate /*
35550Sstevel@tonic-gate  * Clear active process contract template.
35560Sstevel@tonic-gate  */
3557523Sbasabi static void
contract_clear_template(void)3558523Sbasabi contract_clear_template(void)
35590Sstevel@tonic-gate {
35600Sstevel@tonic-gate 	int fd;
35610Sstevel@tonic-gate 
35620Sstevel@tonic-gate 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
35630Sstevel@tonic-gate 		crabort("cannot open process contract template",
35640Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35650Sstevel@tonic-gate 
35660Sstevel@tonic-gate 	if (ct_tmpl_clear(fd))
35670Sstevel@tonic-gate 		crabort("cannot clear contract template",
35680Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35690Sstevel@tonic-gate 
35700Sstevel@tonic-gate 	(void) close(fd);
35710Sstevel@tonic-gate }
35720Sstevel@tonic-gate 
35730Sstevel@tonic-gate /*
35740Sstevel@tonic-gate  * Abandon latest process contract unconditionally.  If we have leaked [some
35750Sstevel@tonic-gate  * critical amount], exit such that the kernel reaps our contracts.
35760Sstevel@tonic-gate  */
35770Sstevel@tonic-gate static void
contract_abandon_latest(pid_t pid)35780Sstevel@tonic-gate contract_abandon_latest(pid_t pid)
35790Sstevel@tonic-gate {
35800Sstevel@tonic-gate 	int r;
35810Sstevel@tonic-gate 	ctid_t id;
35820Sstevel@tonic-gate 	static uint_t cts_lost;
35830Sstevel@tonic-gate 
35840Sstevel@tonic-gate 	if (cts_lost > MAX_LOST_CONTRACTS)
35850Sstevel@tonic-gate 		crabort("repeated failure to abandon contracts",
35860Sstevel@tonic-gate 		    REMOVE_FIFO | CONSOLE_MSG);
35870Sstevel@tonic-gate 
35880Sstevel@tonic-gate 	if (r = contract_latest(&id)) {
35891315Sbasabi 		msg("could not obtain latest contract for "
35901315Sbasabi 		    "PID %ld: %s", pid, strerror(r));
35910Sstevel@tonic-gate 		cts_lost++;
35920Sstevel@tonic-gate 		return;
35930Sstevel@tonic-gate 	}
35940Sstevel@tonic-gate 
35950Sstevel@tonic-gate 	if (r = contract_abandon_id(id)) {
35960Sstevel@tonic-gate 		msg("could not abandon latest contract %ld: %s", id,
35970Sstevel@tonic-gate 		    strerror(r));
35980Sstevel@tonic-gate 		cts_lost++;
35990Sstevel@tonic-gate 		return;
36000Sstevel@tonic-gate 	}
36010Sstevel@tonic-gate }
36028439SChris.Gerhard@sun.com 
36038439SChris.Gerhard@sun.com static struct shared *
create_shared(void * obj,void * (* obj_alloc)(void * obj),void (* obj_free)(void *))36048439SChris.Gerhard@sun.com create_shared(void *obj, void * (*obj_alloc)(void *obj),
36058439SChris.Gerhard@sun.com 	void (*obj_free)(void *))
36068439SChris.Gerhard@sun.com {
36078439SChris.Gerhard@sun.com 	struct shared *out;
36088439SChris.Gerhard@sun.com 
36098439SChris.Gerhard@sun.com 	if ((out = xmalloc(sizeof (struct shared))) == NULL) {
36108439SChris.Gerhard@sun.com 		return (NULL);
36118439SChris.Gerhard@sun.com 	}
36128439SChris.Gerhard@sun.com 	if ((out->obj = obj_alloc(obj)) == NULL) {
36138439SChris.Gerhard@sun.com 		free(out);
36148439SChris.Gerhard@sun.com 		return (NULL);
36158439SChris.Gerhard@sun.com 	}
36168439SChris.Gerhard@sun.com 	out->count = 1;
36178439SChris.Gerhard@sun.com 	out->free = obj_free;
36188439SChris.Gerhard@sun.com 
36198439SChris.Gerhard@sun.com 	return (out);
36208439SChris.Gerhard@sun.com }
36218439SChris.Gerhard@sun.com 
36228439SChris.Gerhard@sun.com static struct shared *
create_shared_str(char * str)36238439SChris.Gerhard@sun.com create_shared_str(char *str)
36248439SChris.Gerhard@sun.com {
36258439SChris.Gerhard@sun.com 	return (create_shared(str, (void *(*)(void *))strdup, free));
36268439SChris.Gerhard@sun.com }
36278439SChris.Gerhard@sun.com 
36288439SChris.Gerhard@sun.com static struct shared *
dup_shared(struct shared * obj)36298439SChris.Gerhard@sun.com dup_shared(struct shared *obj)
36308439SChris.Gerhard@sun.com {
36318439SChris.Gerhard@sun.com 	if (obj != NULL) {
36328439SChris.Gerhard@sun.com 		obj->count++;
36338439SChris.Gerhard@sun.com 	}
36348439SChris.Gerhard@sun.com 	return (obj);
36358439SChris.Gerhard@sun.com }
36368439SChris.Gerhard@sun.com 
36378439SChris.Gerhard@sun.com static void
rel_shared(struct shared * obj)36388439SChris.Gerhard@sun.com rel_shared(struct shared *obj)
36398439SChris.Gerhard@sun.com {
36408439SChris.Gerhard@sun.com 	if (obj && (--obj->count) == 0) {
36418439SChris.Gerhard@sun.com 		obj->free(obj->obj);
36428439SChris.Gerhard@sun.com 		free(obj);
36438439SChris.Gerhard@sun.com 	}
36448439SChris.Gerhard@sun.com }
36458439SChris.Gerhard@sun.com 
36468439SChris.Gerhard@sun.com static void *
get_obj(struct shared * obj)36478439SChris.Gerhard@sun.com get_obj(struct shared *obj)
36488439SChris.Gerhard@sun.com {
36498439SChris.Gerhard@sun.com 	return (obj->obj);
36508439SChris.Gerhard@sun.com }
3651