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