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 2008 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 struct shared {
    173 	int count;			/* usage count */
    174 	void (*free)(void *obj);	/* routine that will free obj */
    175 	void *obj;			/* object */
    176 };
    177 
    178 struct event {
    179 	time_t time;	/* time of the event	*/
    180 	short etype;	/* what type of event; 0=cron, 1=at	*/
    181 	char *cmd;	/* command for cron, job name for at	*/
    182 	struct usr *u;	/* ptr to the owner (usr) of this event	*/
    183 	struct event *link;	/* ptr to another event for this user */
    184 	union {
    185 		struct { /* for crontab events */
    186 			char *minute;	/*  (these	*/
    187 			char *hour;	/*   fields	*/
    188 			char *daymon;	/*   are	*/
    189 			char *month;	/*   from	*/
    190 			char *dayweek;	/*   crontab)	*/
    191 			char *input;	/* ptr to stdin	*/
    192 			struct shared *tz;	/* timezone of this event */
    193 			struct shared *home;	/* directory for this event */
    194 			struct shared *shell;	/* shell for this event */
    195 		} ct;
    196 		struct { /* for at events */
    197 			short exists;	/* for revising at events	*/
    198 			int eventid;	/* for el_remove-ing at events	*/
    199 		} at;
    200 	} of;
    201 };
    202 
    203 struct usr {
    204 	char *name;	/* name of user (e.g. "root")	*/
    205 	char *home;	/* home directory for user	*/
    206 	uid_t uid;	/* user id	*/
    207 	gid_t gid;	/* group id	*/
    208 	int aruncnt;	/* counter for running jobs per uid */
    209 	int cruncnt;	/* counter for running cron jobs per uid */
    210 	int ctid;	/* for el_remove-ing crontab events */
    211 	short ctexists;	/* for revising crontab events	*/
    212 	struct event *ctevents;	/* list of this usr's crontab events */
    213 	struct event *atevents;	/* list of this usr's at events */
    214 	struct usr *nextusr;
    215 };	/* ptr to next user	*/
    216 
    217 static struct	queue
    218 {
    219 	int njob;	/* limit */
    220 	int nice;	/* nice for execution */
    221 	int nwait;	/* wait time to next execution attempt */
    222 	int nrun;	/* number running */
    223 }
    224 	qd = {100, 2, 60},		/* default values for queue defs */
    225 	qt[NQUEUE];
    226 static struct	queue	qq;
    227 
    228 static struct runinfo
    229 {
    230 	pid_t	pid;
    231 	short	que;
    232 	struct  usr *rusr;	/* pointer to usr struct */
    233 	char	*outfile;	/* file where stdout & stderr are trapped */
    234 	short	jobtype;	/* what type of event: 0=cron, 1=at */
    235 	char	*jobname;	/* command for "cron", jobname for "at" */
    236 	int	mailwhendone;	/* 1 = send mail even if no ouptut */
    237 	struct runinfo *next;
    238 }	*rthead;
    239 
    240 static struct miscpid {
    241 	pid_t		pid;
    242 	struct miscpid	*next;
    243 }	*miscpid_head;
    244 
    245 static pid_t cron_pid;	/* own pid */
    246 static char didfork = 0; /* flag to see if I'm process group leader */
    247 static int msgfd;	/* file descriptor for fifo queue */
    248 static int ecid = 1;	/* event class id for el_remove(); MUST be set to 1 */
    249 static int delayed;	/* is job being rescheduled or did it run first time */
    250 static int cwd;		/* current working directory */
    251 static struct event *next_event;	/* the next event to execute	*/
    252 static struct usr *uhead;		/* ptr to the list of users	*/
    253 
    254 /* Variables for error handling at reading crontabs. */
    255 static char cte_intro[] = "Line(s) with errors:\n\n";
    256 static char cte_trail1[] = "\nMax number of errors encountered.";
    257 static char cte_trail2[] = " Evaluation of crontab aborted.\n";
    258 static int cte_free = MAILBINITFREE;	/* Free buffer space */
    259 static char *cte_text = NULL;		/* Text buffer pointer */
    260 static char *cte_lp;			/* Next free line in cte_text */
    261 static int cte_nvalid;			/* Valid lines found */
    262 
    263 /* user's default environment for the shell */
    264 #define	ROOTPATH	"PATH=/usr/sbin:/usr/bin"
    265 #define	NONROOTPATH	"PATH=/usr/bin:"
    266 
    267 static char *Def_supath	= NULL;
    268 static char *Def_path		= NULL;
    269 static char path[LINE_MAX]	= "PATH=";
    270 static char supath[LINE_MAX]	= "PATH=";
    271 static char homedir[LINE_MAX]	= ENV_HOME;
    272 static char logname[LINE_MAX]	= "LOGNAME=";
    273 static char tzone[LINE_MAX]	= ENV_TZ;
    274 static char *envinit[] = {
    275 	homedir,
    276 	logname,
    277 	ROOTPATH,
    278 	"SHELL=/usr/bin/sh",
    279 	tzone,
    280 	NULL
    281 };
    282 
    283 extern char **environ;
    284 
    285 #define	DEFTZ		"GMT"
    286 static	int	log = 0;
    287 static	char	hzname[10];
    288 
    289 static void cronend(int);
    290 static void thaw_handler(int);
    291 static void child_handler(int);
    292 static void child_sigreset(void);
    293 
    294 static void mod_ctab(char *, time_t);
    295 static void mod_atjob(char *, time_t);
    296 static void add_atevent(struct usr *, char *, time_t, int);
    297 static void rm_ctevents(struct usr *);
    298 static void cleanup(struct runinfo *rn, int r);
    299 static void crabort(char *, int);
    300 static void msg(char *fmt, ...);
    301 static void ignore_msg(char *, char *, struct event *);
    302 static void logit(int, struct runinfo *, int);
    303 static void parsqdef(char *);
    304 static void defaults();
    305 static void initialize(int);
    306 static void quedefs(int);
    307 static int idle(long);
    308 static struct usr *find_usr(char *);
    309 static int ex(struct event *e);
    310 static void read_dirs(int);
    311 static void mail(char *, char *, int);
    312 static char *next_field(int, int);
    313 static void readcron(struct usr *, time_t);
    314 static int next_ge(int, char *);
    315 static void free_if_unused(struct usr *);
    316 static void del_atjob(char *, char *);
    317 static void del_ctab(char *);
    318 static void resched(int);
    319 static int msg_wait(long);
    320 static struct runinfo *rinfo_get(pid_t);
    321 static void rinfo_free(struct runinfo *rp);
    322 static void mail_result(struct usr *p, struct runinfo *pr, size_t filesize);
    323 static time_t next_time(struct event *, time_t);
    324 static time_t get_switching_time(int, time_t);
    325 static time_t xmktime(struct tm *);
    326 static void process_msg(struct message *, time_t);
    327 static void reap_child(void);
    328 static void miscpid_insert(pid_t);
    329 static int miscpid_delete(pid_t);
    330 static void contract_set_template(void);
    331 static void contract_clear_template(void);
    332 static void contract_abandon_latest(pid_t);
    333 
    334 static void cte_init(void);
    335 static void cte_add(int, char *);
    336 static void cte_valid(void);
    337 static int cte_istoomany(void);
    338 static void cte_sendmail(char *);
    339 
    340 static int set_user_cred(const struct usr *, struct project *);
    341 
    342 static struct shared *create_shared_str(char *str);
    343 static struct shared *dup_shared(struct shared *obj);
    344 static void rel_shared(struct shared *obj);
    345 static void *get_obj(struct shared *obj);
    346 /*
    347  * last_time is set immediately prior to exection of an event (via ex())
    348  * to indicate the last time an event was executed.  This was (surely)
    349  * it's original intended use.
    350  */
    351 static time_t last_time, init_time, t_old;
    352 static int reset_needed; /* set to 1 when cron(1M) needs to re-initialize */
    353 
    354 static int		accept_sigcld, notifypipe[2];
    355 static sigset_t		defmask, childmask;
    356 
    357 /*
    358  * BSM hooks
    359  */
    360 extern int	audit_cron_session(char *, char *, uid_t, gid_t, char *);
    361 extern void	audit_cron_new_job(char *, int, void *);
    362 extern void	audit_cron_bad_user(char *);
    363 extern void	audit_cron_user_acct_expired(char *);
    364 extern int	audit_cron_create_anc_file(char *, char *, char *, uid_t);
    365 extern int	audit_cron_delete_anc_file(char *, char *);
    366 extern int	audit_cron_is_anc_name(char *);
    367 extern int	audit_cron_mode();
    368 
    369 static int cron_conv(int, struct pam_message **,
    370 		struct pam_response **, void *);
    371 
    372 static struct pam_conv pam_conv = {cron_conv, NULL};
    373 static pam_handle_t *pamh;	/* Authentication handle */
    374 
    375 /*
    376  * Function to help check a user's credentials.
    377  */
    378 
    379 static int verify_user_cred(struct usr *u);
    380 
    381 /*
    382  * Values returned by verify_user_cred and set_user_cred:
    383  */
    384 
    385 #define	VUC_OK		0
    386 #define	VUC_BADUSER	1
    387 #define	VUC_NOTINGROUP	2
    388 #define	VUC_EXPIRED	3
    389 #define	VUC_NEW_AUTH	4
    390 
    391 /*
    392  * Modes of process_anc_files function
    393  */
    394 #define	CRON_ANC_DELETE	1
    395 #define	CRON_ANC_CREATE	0
    396 
    397 /*
    398  * Functions to remove a user or job completely from the running database.
    399  */
    400 static void clean_out_atjobs(struct usr *u);
    401 static void clean_out_ctab(struct usr *u);
    402 static void clean_out_user(struct usr *u);
    403 static void cron_unlink(char *name);
    404 static void process_anc_files(int);
    405 
    406 /*
    407  * functions in elm.c
    408  */
    409 extern void el_init(int, time_t, time_t, int);
    410 extern int el_add(void *, time_t, int);
    411 extern void el_remove(int, int);
    412 extern int el_empty(void);
    413 extern void *el_first(void);
    414 extern void el_delete(void);
    415 
    416 static int valid_entry(char *, int);
    417 static struct usr *create_ulist(char *, int);
    418 static void init_cronevent(char *, int);
    419 static void init_atevent(char *, time_t, int, int);
    420 static void update_atevent(struct usr *, char *, time_t, int);
    421 
    422 int
    423 main(int argc, char *argv[])
    424 {
    425 	time_t t;
    426 	time_t ne_time;		/* amt of time until next event execution */
    427 	time_t newtime, lastmtime = 0L;
    428 	struct usr *u;
    429 	struct event *e, *e2, *eprev;
    430 	struct stat buf;
    431 	pid_t rfork;
    432 	struct sigaction act;
    433 
    434 	/*
    435 	 * reset_needed is set to 1 whenever el_add() finds out that a cron
    436 	 * job is scheduled to be run before the time when cron(1M) daemon
    437 	 * initialized.
    438 	 * Other cases where a reset is needed is when ex() finds that the
    439 	 * event to be executed is being run at the wrong time, or when idle()
    440 	 * determines that time was reset.
    441 	 * We immediately return to the top of the while (TRUE) loop in
    442 	 * main() where the event list is cleared and rebuilt, and reset_needed
    443 	 * is set back to 0.
    444 	 */
    445 	reset_needed = 0;
    446 
    447 	/*
    448 	 * Only the privileged user can run this command.
    449 	 */
    450 	if (getuid() != 0)
    451 		crabort(NOTALLOWED, 0);
    452 
    453 begin:
    454 	(void) setlocale(LC_ALL, "");
    455 	/* fork unless 'nofork' is specified */
    456 	if ((argc <= 1) || (strcmp(argv[1], "nofork"))) {
    457 		if (rfork = fork()) {
    458 			if (rfork == (pid_t)-1) {
    459 				(void) sleep(30);
    460 				goto begin;
    461 			}
    462 			return (0);
    463 		}
    464 		didfork++;
    465 		(void) setpgrp();	/* detach cron from console */
    466 	}
    467 
    468 	(void) umask(022);
    469 	(void) signal(SIGHUP, SIG_IGN);
    470 	(void) signal(SIGINT, SIG_IGN);
    471 	(void) signal(SIGQUIT, SIG_IGN);
    472 	(void) signal(SIGTERM, cronend);
    473 
    474 	defaults();
    475 	initialize(1);
    476 	quedefs(DEFAULT);	/* load default queue definitions */
    477 	cron_pid = getpid();
    478 	msg("*** cron started ***   pid = %d", cron_pid);
    479 	(void) sigset(SIGTHAW, thaw_handler);
    480 	/*
    481 	 * setup SIGCLD handler/mask
    482 	 */
    483 	act.sa_handler = child_handler;
    484 	act.sa_flags = 0;
    485 	(void) sigemptyset(&act.sa_mask);
    486 	(void) sigaddset(&act.sa_mask, SIGCLD);
    487 	(void) sigaction(SIGCLD, &act, NULL);
    488 	(void) sigemptyset(&childmask);
    489 	(void) sigaddset(&childmask, SIGCLD);
    490 	(void) sigprocmask(SIG_BLOCK, &childmask, &defmask);
    491 
    492 	if (pipe(notifypipe) != 0) {
    493 		crabort("cannot create pipe", REMOVE_FIFO|CONSOLE_MSG);
    494 	}
    495 	/*
    496 	 * will set O_NONBLOCK, so that the write() from child_handler
    497 	 * never be blocked.
    498 	 */
    499 	(void) fcntl(notifypipe[0], F_SETFL, O_WRONLY|O_NONBLOCK);
    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 			/* the time was set backwards or forward */
    508 			msg("time was reset, re-initializing");
    509 			el_delete();
    510 			u = uhead;
    511 			while (u != NULL) {
    512 				rm_ctevents(u);
    513 				e = u->atevents;
    514 				while (e != NULL) {
    515 					free(e->cmd);
    516 					e2 = e->link;
    517 					free(e);
    518 					e = e2;
    519 				}
    520 				u->atevents = NULL;
    521 				u = u->nextusr;
    522 			}
    523 			(void) close(msgfd);
    524 			initialize(0);
    525 			t = time(NULL);
    526 			last_time = t;
    527 			/*
    528 			 * reset_needed might have been set in the functions
    529 			 * call path from initialize()
    530 			 */
    531 			if (reset_needed) {
    532 				continue;
    533 			}
    534 		}
    535 		t_old = t;
    536 
    537 		if (next_event == NULL && !el_empty()) {
    538 			next_event = (struct event *)el_first();
    539 		}
    540 		if (next_event == NULL) {
    541 			ne_time = INFINITY;
    542 		} else {
    543 			ne_time = next_event->time - t;
    544 #ifdef DEBUG
    545 			cftime(timebuf, "%C", &next_event->time);
    546 			(void) fprintf(stderr, "next_time=%ld %s\n",
    547 			    next_event->time, timebuf);
    548 #endif
    549 		}
    550 		if (ne_time > 0) {
    551 			/*
    552 			 * reset_needed may be set in the functions call path
    553 			 * from idle()
    554 			 */
    555 			if (idle(ne_time) || reset_needed) {
    556 				reset_needed = 1;
    557 				continue;
    558 			}
    559 		}
    560 
    561 		if (stat(QUEDEFS, &buf)) {
    562 			msg("cannot stat QUEDEFS file");
    563 		} else if (lastmtime != buf.st_mtime) {
    564 			quedefs(LOAD);
    565 			lastmtime = buf.st_mtime;
    566 		}
    567 
    568 		last_time = next_event->time; /* save execution time */
    569 
    570 		/*
    571 		 * reset_needed may be set in the functions call path
    572 		 * from ex()
    573 		 */
    574 		if (ex(next_event) || reset_needed) {
    575 			reset_needed = 1;
    576 			continue;
    577 		}
    578 
    579 		switch (next_event->etype) {
    580 		case CRONEVENT:
    581 			/* add cronevent back into the main event list */
    582 			if (delayed) {
    583 				delayed = 0;
    584 				break;
    585 			}
    586 
    587 			/*
    588 			 * check if time(0)< last_time. if so, then the
    589 			 * system clock has gone backwards. to prevent this
    590 			 * job from being started twice, we reschedule this
    591 			 * job for the >>next time after last_time<<, and
    592 			 * then set next_event->time to this. note that
    593 			 * crontab's resolution is 1 minute.
    594 			 */
    595 
    596 			if (last_time > time(NULL)) {
    597 				msg(CLOCK_DRIFT);
    598 				/*
    599 				 * bump up to next 30 second
    600 				 * increment
    601 				 * 1 <= newtime <= 30
    602 				 */
    603 				newtime = 30 - (last_time % 30);
    604 				newtime += last_time;
    605 
    606 				/*
    607 				 * get the next scheduled event,
    608 				 * not the one that we just
    609 				 * kicked off!
    610 				 */
    611 				next_event->time =
    612 				    next_time(next_event, newtime);
    613 				t_old = time(NULL);
    614 			} else {
    615 				next_event->time =
    616 				    next_time(next_event, (time_t)0);
    617 			}
    618 #ifdef DEBUG
    619 			cftime(timebuf, "%C", &next_event->time);
    620 			(void) fprintf(stderr,
    621 			    "pushing back cron event %s at %ld (%s)\n",
    622 			    next_event->cmd, next_event->time, timebuf);
    623 #endif
    624 
    625 			switch (el_add(next_event, next_event->time,
    626 			    (next_event->u)->ctid)) {
    627 			case -1:
    628 				ignore_msg("main", "cron", next_event);
    629 				break;
    630 			case -2: /* event time lower than init time */
    631 				reset_needed = 1;
    632 				break;
    633 			}
    634 			break;
    635 		default:
    636 			/* remove at or batch job from system */
    637 			if (delayed) {
    638 				delayed = 0;
    639 				break;
    640 			}
    641 			eprev = NULL;
    642 			e = (next_event->u)->atevents;
    643 			while (e != NULL) {
    644 				if (e == next_event) {
    645 					if (eprev == NULL)
    646 						(e->u)->atevents = e->link;
    647 					else
    648 						eprev->link = e->link;
    649 					free(e->cmd);
    650 					free(e);
    651 					break;
    652 				} else {
    653 					eprev = e;
    654 					e = e->link;
    655 				}
    656 			}
    657 			break;
    658 		}
    659 		next_event = NULL;
    660 	}
    661 
    662 	/*NOTREACHED*/
    663 }
    664 
    665 static void
    666 initialize(int firstpass)
    667 {
    668 #ifdef DEBUG
    669 	(void) fprintf(stderr, "in initialize\n");
    670 #endif
    671 	if (firstpass) {
    672 		/* for mail(1), make sure messages come from root */
    673 		if (putenv("LOGNAME=root") != 0) {
    674 			crabort("cannot expand env variable",
    675 			    REMOVE_FIFO|CONSOLE_MSG);
    676 		}
    677 		if (access(FIFO, R_OK) == -1) {
    678 			if (errno == ENOENT) {
    679 				if (mknod(FIFO, S_IFIFO|0600, 0) != 0)
    680 					crabort("cannot create fifo queue",
    681 					    REMOVE_FIFO|CONSOLE_MSG);
    682 			} else {
    683 				if (NOFORK) {
    684 					/* didn't fork... init(1M) is waiting */
    685 					(void) sleep(60);
    686 				}
    687 				perror("FIFO");
    688 				crabort("cannot access fifo queue",
    689 				    REMOVE_FIFO|CONSOLE_MSG);
    690 			}
    691 		} else {
    692 			if (NOFORK) {
    693 				/* didn't fork... init(1M) is waiting */
    694 				(void) sleep(60);
    695 				/*
    696 				 * the wait is painful, but we don't want
    697 				 * init respawning this quickly
    698 				 */
    699 			}
    700 			crabort("cannot start cron; FIFO exists", CONSOLE_MSG);
    701 		}
    702 	}
    703 
    704 	if ((msgfd = open(FIFO, O_RDWR)) < 0) {
    705 		perror("! open");
    706 		crabort("cannot open fifo queue", REMOVE_FIFO|CONSOLE_MSG);
    707 	}
    708 
    709 	init_time = time(NULL);
    710 	el_init(8, init_time, (time_t)(60*60*24), 10);
    711 
    712 	init_time = time(NULL);
    713 	el_init(8, init_time, (time_t)(60*60*24), 10);
    714 
    715 	/*
    716 	 * read directories, create users list, and add events to the
    717 	 * main event list. Only zero user list on firstpass.
    718 	 */
    719 	if (firstpass)
    720 		uhead = NULL;
    721 	read_dirs(firstpass);
    722 	next_event = NULL;
    723 
    724 	if (!firstpass)
    725 		return;
    726 
    727 	/* stdout is log file */
    728 	if (freopen(ACCTFILE, "a", stdout) == NULL)
    729 		(void) fprintf(stderr, "cannot open %s\n", ACCTFILE);
    730 
    731 	/* log should be root-only */
    732 	(void) fchmod(1, S_IRUSR|S_IWUSR);
    733 
    734 	/* stderr also goes to ACCTFILE */
    735 	(void) close(fileno(stderr));
    736 	(void) dup(1);
    737 	/* null for stdin */
    738 	(void) freopen("/dev/null", "r", stdin);
    739 
    740 	contract_set_template();
    741 }
    742 
    743 static void
    744 read_dirs(int first)
    745 {
    746 	DIR		*dir;
    747 	struct dirent	*dp;
    748 	char		*ptr;
    749 	int		jobtype;
    750 	time_t		tim;
    751 
    752 
    753 	if (chdir(CRONDIR) == -1)
    754 		crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG);
    755 	cwd = CRON;
    756 	if ((dir = opendir(".")) == NULL)
    757 		crabort(NOREADDIR, REMOVE_FIFO|CONSOLE_MSG);
    758 	while ((dp = readdir(dir)) != NULL) {
    759 		if (!valid_entry(dp->d_name, CRONEVENT))
    760 			continue;
    761 		init_cronevent(dp->d_name, first);
    762 	}
    763 	(void) closedir(dir);
    764 
    765 	if (chdir(ATDIR) == -1) {
    766 		msg("cannot chdir to at directory");
    767 		return;
    768 	}
    769 	if ((dir = opendir(".")) == NULL) {
    770 		msg("cannot read at at directory");
    771 		return;
    772 	}
    773 	cwd = AT;
    774 	while ((dp = readdir(dir)) != NULL) {
    775 		if (!valid_entry(dp->d_name, ATEVENT))
    776 			continue;
    777 		ptr = dp->d_name;
    778 		if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
    779 			continue;
    780 		ptr++;
    781 		if (!isalpha(*ptr))
    782 			continue;
    783 		jobtype = *ptr - 'a';
    784 		if (jobtype >= NQUEUE) {
    785 			cron_unlink(dp->d_name);
    786 			continue;
    787 		}
    788 		init_atevent(dp->d_name, tim, jobtype, first);
    789 	}
    790 	(void) closedir(dir);
    791 }
    792 
    793 static int
    794 valid_entry(char *name, int type)
    795 {
    796 	struct stat	buf;
    797 
    798 	if (strcmp(name, ".") == 0 ||
    799 	    strcmp(name, "..") == 0)
    800 		return (0);
    801 
    802 	/* skip over ancillary file names */
    803 	if (audit_cron_is_anc_name(name))
    804 		return (0);
    805 
    806 	if (stat(name, &buf)) {
    807 		mail(name, BADSTAT, ERR_UNIXERR);
    808 		cron_unlink(name);
    809 		return (0);
    810 	}
    811 	if (!S_ISREG(buf.st_mode)) {
    812 		mail(name, BADTYPE, ERR_NOTREG);
    813 		cron_unlink(name);
    814 		return (0);
    815 	}
    816 	if (type == ATEVENT) {
    817 		if (!(buf.st_mode & ISUID)) {
    818 			cron_unlink(name);
    819 			return (0);
    820 		}
    821 	}
    822 	return (1);
    823 }
    824 
    825 struct usr *
    826 create_ulist(char *name, int type)
    827 {
    828 	struct usr	*u;
    829 
    830 	u = xmalloc(sizeof (struct usr));
    831 	u->name = xmalloc(strlen(name) + 1);
    832 	(void) strcpy(u->name, name);
    833 	u->home = NULL;
    834 	if (type == CRONEVENT) {
    835 		u->ctexists = TRUE;
    836 		u->ctid = ecid++;
    837 	} else {
    838 		u->ctexists = FALSE;
    839 		u->ctid = 0;
    840 	}
    841 	u->ctevents = NULL;
    842 	u->atevents = NULL;
    843 	u->aruncnt = 0;
    844 	u->cruncnt = 0;
    845 	u->nextusr = uhead;
    846 	uhead = u;
    847 	return (u);
    848 }
    849 
    850 void
    851 init_cronevent(char *name, int first)
    852 {
    853 	struct usr	*u;
    854 
    855 	if (first) {
    856 		u = create_ulist(name, CRONEVENT);
    857 		readcron(u, 0);
    858 	} else {
    859 		if ((u = find_usr(name)) == NULL) {
    860 			u = create_ulist(name, CRONEVENT);
    861 			readcron(u, 0);
    862 		} else {
    863 			u->ctexists = TRUE;
    864 			rm_ctevents(u);
    865 			el_remove(u->ctid, 0);
    866 			readcron(u, 0);
    867 		}
    868 	}
    869 }
    870 
    871 void
    872 init_atevent(char *name, time_t tim, int jobtype, int first)
    873 {
    874 	struct usr	*u;
    875 
    876 	if (first) {
    877 		u = create_ulist(name, ATEVENT);
    878 		add_atevent(u, name, tim, jobtype);
    879 	} else {
    880 		if ((u = find_usr(name)) == NULL) {
    881 			u = create_ulist(name, ATEVENT);
    882 			add_atevent(u, name, tim, jobtype);
    883 		} else {
    884 			update_atevent(u, name, tim, jobtype);
    885 		}
    886 	}
    887 }
    888 
    889 static void
    890 mod_ctab(char *name, time_t reftime)
    891 {
    892 	struct	passwd	*pw;
    893 	struct	stat	buf;
    894 	struct	usr	*u;
    895 	char	namebuf[LINE_MAX];
    896 	char	*pname;
    897 
    898 	/* skip over ancillary file names */
    899 	if (audit_cron_is_anc_name(name))
    900 		return;
    901 
    902 	if ((pw = getpwnam(name)) == NULL) {
    903 		msg("No such user as %s - cron entries not created", name);
    904 		return;
    905 	}
    906 	if (cwd != CRON) {
    907 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
    908 		    CRONDIR, name) >= sizeof (namebuf)) {
    909 			msg("Too long path name %s - cron entries not created",
    910 			    namebuf);
    911 			return;
    912 		}
    913 		pname = namebuf;
    914 	} else {
    915 		pname = name;
    916 	}
    917 	/*
    918 	 * a warning message is given by the crontab command so there is
    919 	 * no need to give one here......  use this code if you only want
    920 	 * users with a login shell of /usr/bin/sh to use cron
    921 	 */
    922 #ifdef BOURNESHELLONLY
    923 	if ((strcmp(pw->pw_shell, "") != 0) &&
    924 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
    925 		mail(name, BADSHELL, ERR_CANTEXECCRON);
    926 		cron_unlink(pname);
    927 		return;
    928 	}
    929 #endif
    930 	if (stat(pname, &buf)) {
    931 		mail(name, BADSTAT, ERR_UNIXERR);
    932 		cron_unlink(pname);
    933 		return;
    934 	}
    935 	if (!S_ISREG(buf.st_mode)) {
    936 		mail(name, BADTYPE, ERR_CRONTABENT);
    937 		return;
    938 	}
    939 	if ((u = find_usr(name)) == NULL) {
    940 #ifdef DEBUG
    941 		(void) fprintf(stderr, "new user (%s) with a crontab\n", name);
    942 #endif
    943 		u = create_ulist(name, CRONEVENT);
    944 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
    945 		(void) strcpy(u->home, pw->pw_dir);
    946 		u->uid = pw->pw_uid;
    947 		u->gid = pw->pw_gid;
    948 		readcron(u, reftime);
    949 	} else {
    950 		u->uid = pw->pw_uid;
    951 		u->gid = pw->pw_gid;
    952 		if (u->home != NULL) {
    953 			if (strcmp(u->home, pw->pw_dir) != 0) {
    954 				free(u->home);
    955 				u->home = xmalloc(strlen(pw->pw_dir) + 1);
    956 				(void) strcpy(u->home, pw->pw_dir);
    957 			}
    958 		} else {
    959 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
    960 			(void) strcpy(u->home, pw->pw_dir);
    961 		}
    962 		u->ctexists = TRUE;
    963 		if (u->ctid == 0) {
    964 #ifdef DEBUG
    965 			(void) fprintf(stderr, "%s now has a crontab\n",
    966 			    u->name);
    967 #endif
    968 			/* user didnt have a crontab last time */
    969 			u->ctid = ecid++;
    970 			u->ctevents = NULL;
    971 			readcron(u, reftime);
    972 			return;
    973 		}
    974 #ifdef DEBUG
    975 		(void) fprintf(stderr, "%s has revised his crontab\n", u->name);
    976 #endif
    977 		rm_ctevents(u);
    978 		el_remove(u->ctid, 0);
    979 		readcron(u, reftime);
    980 	}
    981 }
    982 
    983 /* ARGSUSED */
    984 static void
    985 mod_atjob(char *name, time_t reftime)
    986 {
    987 	char	*ptr;
    988 	time_t	tim;
    989 	struct	passwd	*pw;
    990 	struct	stat	buf;
    991 	struct	usr	*u;
    992 	char	namebuf[PATH_MAX];
    993 	char	*pname;
    994 	int	jobtype;
    995 
    996 	ptr = name;
    997 	if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
    998 		return;
    999 	ptr++;
   1000 	if (!isalpha(*ptr))
   1001 		return;
   1002 	jobtype = *ptr - 'a';
   1003 
   1004 	/* check for audit ancillary file */
   1005 	if (audit_cron_is_anc_name(name))
   1006 		return;
   1007 
   1008 	if (cwd != AT) {
   1009 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name)
   1010 		    >= sizeof (namebuf)) {
   1011 			return;
   1012 		}
   1013 		pname = namebuf;
   1014 	} else {
   1015 		pname = name;
   1016 	}
   1017 	if (stat(pname, &buf) || jobtype >= NQUEUE) {
   1018 		cron_unlink(pname);
   1019 		return;
   1020 	}
   1021 	if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) {
   1022 		cron_unlink(pname);
   1023 		return;
   1024 	}
   1025 	if ((pw = getpwuid(buf.st_uid)) == NULL) {
   1026 		cron_unlink(pname);
   1027 		return;
   1028 	}
   1029 	/*
   1030 	 * a warning message is given by the at command so there is no
   1031 	 * need to give one here......use this code if you only want
   1032 	 * users with a login shell of /usr/bin/sh to use cron
   1033 	 */
   1034 #ifdef BOURNESHELLONLY
   1035 	if ((strcmp(pw->pw_shell, "") != 0) &&
   1036 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
   1037 		mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT);
   1038 		cron_unlink(pname);
   1039 		return;
   1040 	}
   1041 #endif
   1042 	if ((u = find_usr(pw->pw_name)) == NULL) {
   1043 #ifdef DEBUG
   1044 		(void) fprintf(stderr, "new user (%s) with an at job = %s\n",
   1045 		    pw->pw_name, name);
   1046 #endif
   1047 		u = create_ulist(pw->pw_name, ATEVENT);
   1048 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
   1049 		(void) strcpy(u->home, pw->pw_dir);
   1050 		u->uid = pw->pw_uid;
   1051 		u->gid = pw->pw_gid;
   1052 		add_atevent(u, name, tim, jobtype);
   1053 	} else {
   1054 		u->uid = pw->pw_uid;
   1055 		u->gid = pw->pw_gid;
   1056 		free(u->home);
   1057 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
   1058 		(void) strcpy(u->home, 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 	rp = rinfo_get(0); /* allocating a new runinfo struct */
   2211 
   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 static int
   2570 idle(long t)
   2571 {
   2572 	time_t	now;
   2573 
   2574 	while (t > 0L) {
   2575 
   2576 		if (msg_wait(t) != 0) {
   2577 			/* we need to run next job immediately */
   2578 			return (0);
   2579 		}
   2580 
   2581 		reap_child();
   2582 
   2583 		now = time(NULL);
   2584 		if (last_time > now) {
   2585 			/* clock has been reset */
   2586 			return (1);
   2587 		}
   2588 
   2589 		if (next_event == NULL && !el_empty()) {
   2590 			next_event = (struct event *)el_first();
   2591 		}
   2592 		if (next_event == NULL)
   2593 			t = INFINITY;
   2594 		else
   2595 			t = (long)next_event->time - now;
   2596 	}
   2597 	return (0);
   2598 }
   2599 
   2600 /*
   2601  * This used to be in the idle(), but moved to the separate function.
   2602  * This called from various place when cron needs to reap the
   2603  * child. It includes the situation that cron hit maxrun, and needs
   2604  * to reschedule the job.
   2605  */
   2606 static void
   2607 reap_child()
   2608 {
   2609 	pid_t	pid;
   2610 	int	prc;
   2611 	struct	runinfo	*rp;
   2612 
   2613 	for (;;) {
   2614 		pid = waitpid((pid_t)-1, &prc, WNOHANG);
   2615 		if (pid <= 0)
   2616 			break;
   2617 #ifdef DEBUG
   2618 		fprintf(stderr,
   2619 		    "wait returned %x for process %d\n", prc, pid);
   2620 #endif
   2621 		if ((rp = rinfo_get(pid)) == NULL) {
   2622 			if (miscpid_delete(pid) == 0) {
   2623 				/* not found in anywhere */
   2624 				msg(PIDERR, pid);
   2625 			}
   2626 		} else if (rp->que == ZOMB) {
   2627 			(void) unlink(rp->outfile);
   2628 			rinfo_free(rp);
   2629 		} else {
   2630 			cleanup(rp, prc);
   2631 		}
   2632 	}
   2633 }
   2634 
   2635 static void
   2636 cleanup(struct runinfo *pr, int rc)
   2637 {
   2638 	int	nextfork = 1;
   2639 	struct	usr	*p;
   2640 	struct	stat	buf;
   2641 
   2642 	logit(ECHAR, pr, rc);
   2643 	--qt[pr->que].nrun;
   2644 	p = pr->rusr;
   2645 	if (pr->que != CRONEVENT)
   2646 		--p->aruncnt;
   2647 	else
   2648 		--p->cruncnt;
   2649 
   2650 	if (lstat(pr->outfile, &buf) == 0) {
   2651 		if (!S_ISLNK(buf.st_mode) &&
   2652 		    (buf.st_size > 0 || pr->mailwhendone)) {
   2653 			/* mail user stdout and stderr */
   2654 			for (;;) {
   2655 				if ((pr->pid = fork()) < 0) {
   2656 					/*
   2657 					 * if fork fails try forever in doubling
   2658 					 * retry times, up to 16 seconds
   2659 					 */
   2660 					(void) sleep(nextfork);
   2661 					if (nextfork < 16)
   2662 						nextfork += nextfork;
   2663 					continue;
   2664 				} else if (pr->pid == 0) {
   2665 					child_sigreset();
   2666 					contract_clear_template();
   2667 
   2668 					mail_result(p, pr, buf.st_size);
   2669 					/* NOTREACHED */
   2670 				} else {
   2671 					contract_abandon_latest(pr->pid);
   2672 					pr->que = ZOMB;
   2673 					break;
   2674 				}
   2675 			}
   2676 		} else {
   2677 			(void) unlink(pr->outfile);
   2678 			rinfo_free(pr);
   2679 		}
   2680 	} else {
   2681 		rinfo_free(pr);
   2682 	}
   2683 
   2684 	free_if_unused(p);
   2685 }
   2686 
   2687 /*
   2688  * Mail stdout and stderr of a job to user. Get uid for real user and become
   2689  * that person. We do this so that mail won't come from root since this
   2690  * could be a security hole. If failure, quit - don't send mail as root.
   2691  */
   2692 static void
   2693 mail_result(struct usr *p, struct runinfo *pr, size_t filesize)
   2694 {
   2695 	struct	passwd	*ruser_ids;
   2696 	FILE	*mailpipe;
   2697 	FILE	*st;
   2698 	struct utsname	name;
   2699 	int	nbytes;
   2700 	char	iobuf[BUFSIZ];
   2701 	char	*cmd;
   2702 
   2703 	(void) uname(&name);
   2704 	if ((ruser_ids = getpwnam(p->name)) == NULL)
   2705 		exit(0);
   2706 	(void) setuid(ruser_ids->pw_uid);
   2707 
   2708 	cmd = xmalloc(strlen(MAIL) + strlen(p->name)+2);
   2709 	(void) sprintf(cmd, "%s %s", MAIL, p->name);
   2710 	mailpipe = popen(cmd, "w");
   2711 	free(cmd);
   2712 	if (mailpipe == NULL)
   2713 		exit(127);
   2714 	(void) fprintf(mailpipe, "To: %s\n", p->name);
   2715 	if (pr->jobtype == CRONEVENT) {
   2716 		(void) fprintf(mailpipe, CRONOUT);
   2717 		(void) fprintf(mailpipe, "Your \"cron\" job on %s\n",
   2718 		    name.nodename);
   2719 		if (pr->jobname != NULL) {
   2720 			(void) fprintf(mailpipe, "%s\n\n", pr->jobname);
   2721 		}
   2722 	} else {
   2723 		(void) fprintf(mailpipe, "Subject: Output from \"at\" job\n\n");
   2724 		(void) fprintf(mailpipe, "Your \"at\" job on %s\n",
   2725 		    name.nodename);
   2726 		if (pr->jobname != NULL) {
   2727 			(void) fprintf(mailpipe, "\"%s\"\n\n", pr->jobname);
   2728 		}
   2729 	}
   2730 	/* Tmp. file is fopen'ed w/ "r",  secure open */
   2731 	if (filesize > 0 &&
   2732 	    (st = fopen(pr->outfile, "r")) != NULL) {
   2733 		(void) fprintf(mailpipe,
   2734 		    "produced the following output:\n\n");
   2735 		while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0)
   2736 			(void) fwrite(iobuf, sizeof (char), nbytes, mailpipe);
   2737 		(void) fclose(st);
   2738 	} else {
   2739 		(void) fprintf(mailpipe, "completed.\n");
   2740 	}
   2741 	(void) pclose(mailpipe);
   2742 	exit(0);
   2743 }
   2744 
   2745 static int
   2746 msg_wait(long tim)
   2747 {
   2748 	struct	message	msg;
   2749 	int	cnt;
   2750 	time_t	reftime;
   2751 	struct	pollfd pfd[2];
   2752 	int64_t	tl;
   2753 	int	timeout;
   2754 	static int	pending_msg;
   2755 	static time_t	pending_reftime;
   2756 
   2757 	if (pending_msg) {
   2758 		process_msg(&msgbuf, pending_reftime);
   2759 		pending_msg = 0;
   2760 		return (0);
   2761 	}
   2762 
   2763 	/*
   2764 	 * We are opening the signal mask to receive SIGCLD. The notifypipe
   2765 	 * is used to avoid race condition between SIGCLD and poll system
   2766 	 * call.
   2767 	 * If SIGCLD is delivered in poll(), poll will be interrupted, and
   2768 	 * we will return to idle() to reap the dead children.
   2769 	 * If SIGCLD is delivered between sigprocmask() below and poll(),
   2770 	 * there is no way we can detect the SIGCLD because poll() won't
   2771 	 * be interrupted. In such case, the dead children can't be wait'ed
   2772 	 * until poll returns by timeout or a new job. To avoid this race
   2773 	 * condition, child_handler write to the notifypipe, so that
   2774 	 * poll() will be able to return with POLLIN which indicates that
   2775 	 * we have received SIGCLD.
   2776 	 *
   2777 	 * Since the notifypipe is used to just let poll return from
   2778 	 * system call, the data in the pipe won't be read. Therefore,
   2779 	 * any data in the pipe needs to be flushed before opening signal
   2780 	 * mask.
   2781 	 *
   2782 	 * Note that we can probably re-write this code with pselect()
   2783 	 * which can handle this situation easily.
   2784 	 */
   2785 	(void) ioctl(notifypipe[0], I_FLUSH, FLUSHW);
   2786 
   2787 	pfd[0].fd = msgfd;
   2788 	pfd[0].events = POLLIN;
   2789 	pfd[1].fd = notifypipe[1];
   2790 	pfd[1].events = POLLIN;
   2791 
   2792 #ifdef CRON_MAXSLEEP
   2793 	/*
   2794 	 * CRON_MAXSLEEP can be defined to have cron periodically wake
   2795 	 * up, so that cron can detect a change of TOD and adjust the
   2796 	 * sleep time accordingly.
   2797 	 */
   2798 	tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim;
   2799 #endif
   2800 	tl = (tim == INFINITY) ? -1ll : (int64_t)tim * 1000;
   2801 
   2802 	accept_sigcld = 1;
   2803 	(void) sigprocmask(SIG_UNBLOCK, &childmask, NULL);
   2804 	do {
   2805 		timeout = (tl > INT_MAX ? INT_MAX : (int)tl);
   2806 		tl -= timeout;
   2807 		cnt = poll(pfd, 2, timeout);
   2808 		if (cnt == -1 && errno != EINTR) {
   2809 			perror("! poll");
   2810 		}
   2811 	} while (tl > 0 && cnt == 0);
   2812 	(void) sigprocmask(SIG_BLOCK, &childmask, NULL);
   2813 	accept_sigcld = 0;
   2814 
   2815 	/*
   2816 	 * poll timeout or interrupted.
   2817 	 */
   2818 	if (cnt <= 0)
   2819 		return (0);
   2820 
   2821 	/*
   2822 	 * Not the timeout or new job, but a SIGCLD has been delivered.
   2823 	 */
   2824 	if ((pfd[0].revents & POLLIN) == 0)
   2825 		return (0);
   2826 
   2827 	errno = 0;
   2828 	if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) {
   2829 		if (cnt != -1 || errno != EAGAIN)
   2830 			perror("! read");
   2831 		return (0);
   2832 	}
   2833 	reftime = time(NULL);
   2834 	if (next_event != NULL && reftime >= next_event->time) {
   2835 		/*
   2836 		 * we need to run the job before reloading crontab.
   2837 		 */
   2838 		(void) memcpy(&msgbuf, &msg, sizeof (msg));
   2839 		pending_msg = 1;
   2840 		pending_reftime = reftime;
   2841 		return (1);
   2842 	}
   2843 	process_msg(&msg, reftime);
   2844 	return (0);
   2845 }
   2846 
   2847 /*
   2848  * process the message supplied via pipe. This will be called either
   2849  * immediately after cron read the message from pipe, or idle time
   2850  * if the message was pending due to the job execution.
   2851  */
   2852 static void
   2853 process_msg(struct message *pmsg, time_t reftime)
   2854 {
   2855 	if (pmsg->etype == NULL)
   2856 		return;
   2857 
   2858 	switch (pmsg->etype) {
   2859 	case AT:
   2860 		if (pmsg->action == DELETE)
   2861 			del_atjob(pmsg->fname, pmsg->logname);
   2862 		else
   2863 			mod_atjob(pmsg->fname, (time_t)0);
   2864 		break;
   2865 	case CRON:
   2866 		if (pmsg->action == DELETE)
   2867 			del_ctab(pmsg->fname);
   2868 		else
   2869 			mod_ctab(pmsg->fname, reftime);
   2870 		break;
   2871 	default:
   2872 		msg("message received - bad format");
   2873 		break;
   2874 	}
   2875 	if (next_event != NULL) {
   2876 		if (next_event->etype == CRONEVENT) {
   2877 			switch (el_add(next_event, next_event->time,
   2878 			    (next_event->u)->ctid)) {
   2879 			case -1:
   2880 				ignore_msg("process_msg", "cron", next_event);
   2881 				break;
   2882 			case -2: /* event time lower than init time */
   2883 				reset_needed = 1;
   2884 				break;
   2885 			}
   2886 		} else { /* etype == ATEVENT */
   2887 			if (el_add(next_event, next_event->time,
   2888 			    next_event->of.at.eventid) < 0) {
   2889 				ignore_msg("process_msg", "at", next_event);
   2890 			}
   2891 		}
   2892 		next_event = NULL;
   2893 	}
   2894 	(void) fflush(stdout);
   2895 	pmsg->etype = NULL;
   2896 }
   2897 
   2898 /*
   2899  * Allocate a new or find an existing runinfo structure
   2900  */
   2901 static struct runinfo *
   2902 rinfo_get(pid_t pid)
   2903 {
   2904 	struct runinfo *rp;
   2905 
   2906 	if (pid == 0) {		/* allocate a new entry */
   2907 		rp = xcalloc(1, sizeof (struct runinfo));
   2908 		rp->next = rthead;	/* link the entry into the list */
   2909 		rthead = rp;
   2910 		return (rp);
   2911 	}
   2912 	/* search the list for an existing entry */
   2913 	for (rp = rthead; rp != NULL; rp = rp->next) {
   2914 		if (rp->pid == pid)
   2915 			break;
   2916 	}
   2917 	return (rp);
   2918 }
   2919 
   2920 /*
   2921  * Free a runinfo structure and its associated memory
   2922  */
   2923 static void
   2924 rinfo_free(struct runinfo *entry)
   2925 {
   2926 	struct runinfo **rpp;
   2927 	struct runinfo *rp;
   2928 
   2929 #ifdef DEBUG
   2930 	(void) fprintf(stderr, "freeing job %s\n", entry->jobname);
   2931 #endif
   2932 	for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) {
   2933 		if (rp == entry) {
   2934 			*rpp = rp->next;	/* unlink the entry */
   2935 			free(rp->outfile);
   2936 			free(rp->jobname);
   2937 			free(rp);
   2938 			break;
   2939 		}
   2940 	}
   2941 }
   2942 
   2943 /* ARGSUSED */
   2944 static void
   2945 thaw_handler(int sig)
   2946 {
   2947 	;
   2948 }
   2949 
   2950 
   2951 /* ARGSUSED */
   2952 static void
   2953 cronend(int sig)
   2954 {
   2955 	crabort("SIGTERM", REMOVE_FIFO);
   2956 }
   2957 
   2958 /*ARGSUSED*/
   2959 static void
   2960 child_handler(int sig)
   2961 {
   2962 	/*
   2963 	 * Just in case someone changes the signal mask.
   2964 	 * we don't want to notify the SIGCLD.
   2965 	 */
   2966 	if (accept_sigcld) {
   2967 		(void) write(notifypipe[0], &sig, 1);
   2968 	}
   2969 }
   2970 
   2971 static void
   2972 child_sigreset(void)
   2973 {
   2974 	(void) signal(SIGCLD, SIG_DFL);
   2975 	(void) sigprocmask(SIG_SETMASK, &defmask, NULL);
   2976 }
   2977 
   2978 /*
   2979  * crabort() - handle exits out of cron
   2980  */
   2981 static void
   2982 crabort(char *mssg, int action)
   2983 {
   2984 	int	c;
   2985 
   2986 	if (action & REMOVE_FIFO) {
   2987 		/* FIFO vanishes when cron finishes */
   2988 		if (unlink(FIFO) < 0)
   2989 			perror("cron could not unlink FIFO");
   2990 	}
   2991 
   2992 	if (action & CONSOLE_MSG) {
   2993 		/* write error msg to console */
   2994 		if ((c = open(CONSOLE, O_WRONLY)) >= 0) {
   2995 			(void) write(c, "cron aborted: ", 14);
   2996 			(void) write(c, mssg, strlen(mssg));
   2997 			(void) write(c, "\n", 1);
   2998 			(void) close(c);
   2999 		}
   3000 	}
   3001 
   3002 	/* always log the message */
   3003 	msg(mssg);
   3004 	msg("******* CRON ABORTED ********");
   3005 	exit(1);
   3006 }
   3007 
   3008 /*
   3009  * msg() - time-stamped error reporting function
   3010  */
   3011 /*PRINTFLIKE1*/
   3012 static void
   3013 msg(char *fmt, ...)
   3014 {
   3015 	va_list args;
   3016 	time_t	t;
   3017 
   3018 	t = time(NULL);
   3019 
   3020 	(void) fflush(stdout);
   3021 
   3022 	(void) fprintf(stderr, "! ");
   3023 
   3024 	va_start(args, fmt);
   3025 	(void) vfprintf(stderr, fmt, args);
   3026 	va_end(args);
   3027 
   3028 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
   3029 	(void) fprintf(stderr, " %s\n", timebuf);
   3030 
   3031 	(void) fflush(stderr);
   3032 }
   3033 
   3034 static void
   3035 ignore_msg(char *func_name, char *job_type, struct event *event)
   3036 {
   3037 	msg("%s: ignoring %s job (user: %s, cmd: %s, time: %ld)",
   3038 	    func_name, job_type,
   3039 	    event->u->name ? event->u->name : "unknown",
   3040 	    event->cmd ? event->cmd : "unknown",
   3041 	    event->time);
   3042 }
   3043 
   3044 static void
   3045 logit(int cc, struct runinfo *rp, int rc)
   3046 {
   3047 	time_t t;
   3048 	int    ret;
   3049 
   3050 	if (!log)
   3051 		return;
   3052 
   3053 	t = time(NULL);
   3054 	if (cc == BCHAR)
   3055 		(void) printf("%c  CMD: %s\n", cc, next_event->cmd);
   3056 	(void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
   3057 	(void) printf("%c  %.8s %u %c %s",
   3058 	    cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf);
   3059 	if ((ret = TSTAT(rc)) != 0)
   3060 		(void) printf(" ts=%d", ret);
   3061 	if ((ret = RCODE(rc)) != 0)
   3062 		(void) printf(" rc=%d", ret);
   3063 	(void) putchar('\n');
   3064 	(void) fflush(stdout);
   3065 }
   3066 
   3067 static void
   3068 resched(int delay)
   3069 {
   3070 	time_t	nt;
   3071 
   3072 	/* run job at a later time */
   3073 	nt = next_event->time + delay;
   3074 	if (next_event->etype == CRONEVENT) {
   3075 		next_event->time = next_time(next_event, (time_t)0);
   3076 		if (nt < next_event->time)
   3077 			next_event->time = nt;
   3078 		switch (el_add(next_event, next_event->time,
   3079 		    (next_event->u)->ctid)) {
   3080 		case -1:
   3081 			ignore_msg("resched", "cron", next_event);
   3082 			break;
   3083 		case -2: /* event time lower than init time */
   3084 			reset_needed = 1;
   3085 			break;
   3086 		}
   3087 		delayed = 1;
   3088 		msg("rescheduling a cron job");
   3089 		return;
   3090 	}
   3091 	add_atevent(next_event->u, next_event->cmd, nt, next_event->etype);
   3092 	msg("rescheduling at job");
   3093 }
   3094 
   3095 static void
   3096 quedefs(int action)
   3097 {
   3098 	int	i;
   3099 	int	j;
   3100 	char	qbuf[QBUFSIZ];
   3101 	FILE	*fd;
   3102 
   3103 	/* set up default queue definitions */
   3104 	for (i = 0; i < NQUEUE; i++) {
   3105 		qt[i].njob = qd.njob;
   3106 		qt[i].nice = qd.nice;
   3107 		qt[i].nwait = qd.nwait;
   3108 	}
   3109 	if (action == DEFAULT)
   3110 		return;
   3111 	if ((fd = fopen(QUEDEFS, "r")) == NULL) {
   3112 		msg("cannot open quedefs file");
   3113 		msg("using default queue definitions");
   3114 		return;
   3115 	}
   3116 	while (fgets(qbuf, QBUFSIZ, fd) != NULL) {
   3117 		if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.')
   3118 			continue;
   3119 		parsqdef(&qbuf[2]);
   3120 		qt[j].njob = qq.njob;
   3121 		qt[j].nice = qq.nice;
   3122 		qt[j].nwait = qq.nwait;
   3123 	}
   3124 	(void) fclose(fd);
   3125 }
   3126 
   3127 static void
   3128 parsqdef(char *name)
   3129 {
   3130 	int i;
   3131 
   3132 	qq = qd;
   3133 	while (*name) {
   3134 		i = 0;
   3135 		while (isdigit(*name)) {
   3136 			i *= 10;
   3137 			i += *name++ - '0';
   3138 		}
   3139 		switch (*name++) {
   3140 		case JOBF:
   3141 			qq.njob = i;
   3142 			break;
   3143 		case NICEF:
   3144 			qq.nice = i;
   3145 			break;
   3146 		case WAITF:
   3147 			qq.nwait = i;
   3148 			break;
   3149 		}
   3150 	}
   3151 }
   3152 
   3153 /*
   3154  * defaults - read defaults from /etc/default/cron
   3155  */
   3156 static void
   3157 defaults()
   3158 {
   3159 	int  flags;
   3160 	char *deflog;
   3161 	char *hz, *tz;
   3162 
   3163 	/*
   3164 	 * get HZ value for environment
   3165 	 */
   3166 	if ((hz = getenv("HZ")) == (char *)NULL)
   3167 		(void) sprintf(hzname, "HZ=%d", HZ);
   3168 	else
   3169 		(void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz);
   3170 	/*
   3171 	 * get TZ value for environment
   3172 	 */
   3173 	(void) snprintf(tzone, sizeof (tzone), "TZ=%s",
   3174 	    ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ);
   3175 
   3176 	if (defopen(DEFFILE) == 0) {
   3177 		/* ignore case */
   3178 		flags = defcntl(DC_GETFLAGS, 0);
   3179 		TURNOFF(flags, DC_CASE);
   3180 		(void) defcntl(DC_SETFLAGS, flags);
   3181 
   3182 		if (((deflog = defread("CRONLOG=")) == NULL) ||
   3183 		    (*deflog == 'N') || (*deflog == 'n'))
   3184 			log = 0;
   3185 		else
   3186 			log = 1;
   3187 		/* fix for 1087611 - allow paths to be set in defaults file */
   3188 		if ((Def_path = defread("PATH=")) != NULL) {
   3189 			(void) strlcat(path, Def_path, LINE_MAX);
   3190 		} else {
   3191 			(void) strlcpy(path, NONROOTPATH, LINE_MAX);
   3192 		}
   3193 		if ((Def_supath = defread("SUPATH=")) != NULL) {
   3194 			(void) strlcat(supath, Def_supath, LINE_MAX);
   3195 		} else {
   3196 			(void) strlcpy(supath, ROOTPATH, LINE_MAX);
   3197 		}
   3198 		(void) defopen(NULL);
   3199 	}
   3200 }
   3201 
   3202 /*
   3203  * Determine if a user entry for a job is still ok.  The method used here
   3204  * is a lot (about 75x) faster than using setgrent() / getgrent()
   3205  * endgrent().  It should be safe because we use the sysconf to determine
   3206  * the max, and it tolerates the max being 0.
   3207  */
   3208 
   3209 static int
   3210 verify_user_cred(struct usr *u)
   3211 {
   3212 	struct passwd *pw;
   3213 	size_t numUsrGrps = 0;
   3214 	size_t numOrigGrps = 0;
   3215 	size_t i;
   3216 	int retval;
   3217 
   3218 	/*
   3219 	 * Maximum number of groups a user may be in concurrently.  This
   3220 	 * is a value which we obtain at runtime through a sysconf()
   3221 	 * call.
   3222 	 */
   3223 
   3224 	static size_t nGroupsMax = (size_t)-1;
   3225 
   3226 	/*
   3227 	 * Arrays for cron user's group list, constructed at startup to
   3228 	 * be nGroupsMax elements long, used for verifying user
   3229 	 * credentials prior to execution.
   3230 	 */
   3231 
   3232 	static gid_t *UsrGrps;
   3233 	static gid_t *OrigGrps;
   3234 
   3235 	if ((pw = getpwnam(u->name)) == NULL)
   3236 		return (VUC_BADUSER);
   3237 	if (u->home != NULL) {
   3238 		if (strcmp(u->home, pw->pw_dir) != 0) {
   3239 			free(u->home);
   3240 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
   3241 			(void) strcpy(u->home, pw->pw_dir);
   3242 		}
   3243 	} else {
   3244 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
   3245 		(void) strcpy(u->home, pw->pw_dir);
   3246 	}
   3247 	if (u->uid != pw->pw_uid)
   3248 		u->uid = pw->pw_uid;
   3249 	if (u->gid != pw->pw_gid)
   3250 		u->gid  = pw->pw_gid;
   3251 
   3252 	/*
   3253 	 * Create the group id lists needed for job credential
   3254 	 * verification.
   3255 	 */
   3256 
   3257 	if (nGroupsMax == (size_t)-1) {
   3258 		if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) {
   3259 			UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t));
   3260 			OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t));
   3261 		}
   3262 
   3263 #ifdef DEBUG
   3264 		(void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax);
   3265 #endif
   3266 	}
   3267 
   3268 #ifdef DEBUG
   3269 	(void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name,
   3270 	    pw->pw_uid);
   3271 	(void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, "
   3272 	    "u->gid = %d\n", pw->pw_gid, u->gid);
   3273 #endif
   3274 
   3275 	retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP;
   3276 
   3277 	if (nGroupsMax > 0) {
   3278 		numOrigGrps = getgroups(nGroupsMax, OrigGrps);
   3279 
   3280 		(void) initgroups(pw->pw_name, pw->pw_gid);
   3281 		numUsrGrps = getgroups(nGroupsMax, UsrGrps);
   3282 
   3283 		for (i = 0; i < numUsrGrps; i++) {
   3284 			if (UsrGrps[i] == u->gid) {
   3285 				retval = VUC_OK;
   3286 				break;
   3287 			}
   3288 		}
   3289 
   3290 		if (OrigGrps) {
   3291 			(void) setgroups(numOrigGrps, OrigGrps);
   3292 		}
   3293 	}
   3294 
   3295 #ifdef DEBUG
   3296 	(void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval);
   3297 #endif
   3298 
   3299 	return (retval);
   3300 }
   3301 
   3302 static int
   3303 set_user_cred(const struct usr *u, struct project *pproj)
   3304 {
   3305 	static char *progname = "cron";
   3306 	int r = 0, rval = 0;
   3307 
   3308 	if ((r = pam_start(progname, u->name, &pam_conv, &pamh))
   3309 	    != PAM_SUCCESS) {
   3310 #ifdef DEBUG
   3311 		msg("pam_start returns %d\n", r);
   3312 #endif
   3313 		rval = VUC_BADUSER;
   3314 		goto set_eser_cred_exit;
   3315 	}
   3316 
   3317 	r = pam_acct_mgmt(pamh, 0);
   3318 #ifdef DEBUG
   3319 	msg("pam_acc_mgmt returns %d\n", r);
   3320 #endif
   3321 	if (r == PAM_ACCT_EXPIRED) {
   3322 		rval = VUC_EXPIRED;
   3323 		goto set_eser_cred_exit;
   3324 	}
   3325 	if (r == PAM_NEW_AUTHTOK_REQD) {
   3326 		rval = VUC_NEW_AUTH;
   3327 		goto set_eser_cred_exit;
   3328 	}
   3329 	if (r != PAM_SUCCESS) {
   3330 		rval = VUC_BADUSER;
   3331 		goto set_eser_cred_exit;
   3332 	}
   3333 
   3334 	if (pproj != NULL) {
   3335 		size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name);
   3336 		char *buf = alloca(sz);
   3337 
   3338 		(void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name);
   3339 		(void) pam_set_item(pamh, PAM_RESOURCE, buf);
   3340 	}
   3341 
   3342 	r = pam_setcred(pamh, PAM_ESTABLISH_CRED);
   3343 	if (r != PAM_SUCCESS)
   3344 		rval = VUC_BADUSER;
   3345 
   3346 set_eser_cred_exit:
   3347 	(void) pam_end(pamh, r);
   3348 	return (rval);
   3349 }
   3350 
   3351 static void
   3352 clean_out_user(struct usr *u)
   3353 {
   3354 	if (next_event->u == u) {
   3355 		next_event = NULL;
   3356 	}
   3357 
   3358 	clean_out_ctab(u);
   3359 	clean_out_atjobs(u);
   3360 	free_if_unused(u);
   3361 }
   3362 
   3363 static void
   3364 clean_out_atjobs(struct usr *u)
   3365 {
   3366 	struct event *ev, *pv;
   3367 
   3368 	for (pv = NULL, ev = u->atevents;
   3369 	    ev != NULL;
   3370 	    pv = ev, ev = ev->link, free(pv)) {
   3371 		el_remove(ev->of.at.eventid, 1);
   3372 		if (cwd == AT)
   3373 			cron_unlink(ev->cmd);
   3374 		else {
   3375 			char buf[PATH_MAX];
   3376 			if (strlen(ATDIR) + strlen(ev->cmd) + 2
   3377 			    < PATH_MAX) {
   3378 				(void) sprintf(buf, "%s/%s", ATDIR, ev->cmd);
   3379 				cron_unlink(buf);
   3380 			}
   3381 		}
   3382 		free(ev->cmd);
   3383 	}
   3384 
   3385 	u->atevents = NULL;
   3386 }
   3387 
   3388 static void
   3389 clean_out_ctab(struct usr *u)
   3390 {
   3391 	rm_ctevents(u);
   3392 	el_remove(u->ctid, 0);
   3393 	u->ctid = 0;
   3394 	u->ctexists = 0;
   3395 }
   3396 
   3397 static void
   3398 cron_unlink(char *name)
   3399 {
   3400 	int r;
   3401 
   3402 	r = unlink(name);
   3403 	if (r == 0 || (r == -1 && errno == ENOENT)) {
   3404 		(void) audit_cron_delete_anc_file(name, NULL);
   3405 	}
   3406 }
   3407 
   3408 static void
   3409 create_anc_ctab(struct event *e)
   3410 {
   3411 	if (audit_cron_create_anc_file(e->u->name,
   3412 	    (cwd == CRON) ? NULL:CRONDIR,
   3413 	    e->u->name, e->u->uid) == -1) {
   3414 		process_anc_files(CRON_ANC_DELETE);
   3415 		crabort("cannot create ancillary files for crontabs",
   3416 		    REMOVE_FIFO|CONSOLE_MSG);
   3417 	}
   3418 }
   3419 
   3420 static void
   3421 delete_anc_ctab(struct event *e)
   3422 {
   3423 	(void) audit_cron_delete_anc_file(e->u->name,
   3424 	    (cwd == CRON) ? NULL:CRONDIR);
   3425 }
   3426 
   3427 static void
   3428 create_anc_atjob(struct event *e)
   3429 {
   3430 	if (!e->of.at.exists)
   3431 		return;
   3432 
   3433 	if (audit_cron_create_anc_file(e->cmd,
   3434 	    (cwd == AT) ? NULL:ATDIR,
   3435 	    e->u->name, e->u->uid) == -1) {
   3436 		process_anc_files(CRON_ANC_DELETE);
   3437 		crabort("cannot create ancillary files for atjobs",
   3438 		    REMOVE_FIFO|CONSOLE_MSG);
   3439 	}
   3440 }
   3441 
   3442 static void
   3443 delete_anc_atjob(struct event *e)
   3444 {
   3445 	if (!e->of.at.exists)
   3446 		return;
   3447 
   3448 	(void) audit_cron_delete_anc_file(e->cmd,
   3449 	    (cwd == AT) ? NULL:ATDIR);
   3450 }
   3451 
   3452 
   3453 static void
   3454 process_anc_files(int del)
   3455 {
   3456 	struct usr	*u = uhead;
   3457 	struct event	*e;
   3458 
   3459 	if (!audit_cron_mode())
   3460 		return;
   3461 
   3462 	for (;;) {
   3463 		if (u->ctexists && u->ctevents != NULL) {
   3464 			e = u->ctevents;
   3465 			for (;;) {
   3466 				if (del)
   3467 					delete_anc_ctab(e);
   3468 				else
   3469 					create_anc_ctab(e);
   3470 				if ((e = e->link) == NULL)
   3471 					break;
   3472 			}
   3473 		}
   3474 
   3475 		if (u->atevents != NULL) {
   3476 			e = u->atevents;
   3477 			for (;;) {
   3478 				if (del)
   3479 					delete_anc_atjob(e);
   3480 				else
   3481 					create_anc_atjob(e);
   3482 				if ((e = e->link) == NULL)
   3483 					break;
   3484 			}
   3485 		}
   3486 
   3487 		if ((u = u->nextusr)  == NULL)
   3488 			break;
   3489 	}
   3490 }
   3491 
   3492 /*ARGSUSED*/
   3493 static int
   3494 cron_conv(int num_msg, struct pam_message **msgs,
   3495     struct pam_response **response, void *appdata_ptr)
   3496 {
   3497 	struct pam_message	**m = msgs;
   3498 	int i;
   3499 
   3500 	for (i = 0; i < num_msg; i++) {
   3501 		switch (m[i]->msg_style) {
   3502 		case PAM_ERROR_MSG:
   3503 		case PAM_TEXT_INFO:
   3504 			if (m[i]->msg != NULL) {
   3505 				(void) msg("%s\n", m[i]->msg);
   3506 			}
   3507 			break;
   3508 
   3509 		default:
   3510 			break;
   3511 		}
   3512 	}
   3513 	return (0);
   3514 }
   3515 
   3516 /*
   3517  * Cron creates process for other than job. Mail process is the
   3518  * one which rinfo does not cover. Therefore, miscpid will keep
   3519  * track of the pids executed from cron. Otherwise, we will see
   3520  * "unexpected pid returned.." messages appear in the log file.
   3521  */
   3522 static void
   3523 miscpid_insert(pid_t pid)
   3524 {
   3525 	struct miscpid *mp;
   3526 
   3527 	mp = xmalloc(sizeof (*mp));
   3528 	mp->pid = pid;
   3529 	mp->next = miscpid_head;
   3530 	miscpid_head = mp;
   3531 }
   3532 
   3533 static int
   3534 miscpid_delete(pid_t pid)
   3535 {
   3536 	struct miscpid *mp, *omp;
   3537 	int found = 0;
   3538 
   3539 	omp = NULL;
   3540 	for (mp = miscpid_head; mp != NULL; mp = mp->next) {
   3541 		if (mp->pid == pid) {
   3542 			found = 1;
   3543 			break;
   3544 		}
   3545 		omp = mp;
   3546 	}
   3547 	if (found) {
   3548 		if (omp != NULL)
   3549 			omp->next = mp->next;
   3550 		else
   3551 			miscpid_head = NULL;
   3552 		free(mp);
   3553 	}
   3554 	return (found);
   3555 }
   3556 
   3557 /*
   3558  * Establish contract terms such that all children are in abandoned
   3559  * process contracts.
   3560  */
   3561 static void
   3562 contract_set_template(void)
   3563 {
   3564 	int fd;
   3565 
   3566 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
   3567 		crabort("cannot open process contract template",
   3568 		    REMOVE_FIFO | CONSOLE_MSG);
   3569 
   3570 	if (ct_pr_tmpl_set_param(fd, 0) ||
   3571 	    ct_tmpl_set_informative(fd, 0) ||
   3572 	    ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR))
   3573 		crabort("cannot establish contract template terms",
   3574 		    REMOVE_FIFO | CONSOLE_MSG);
   3575 
   3576 	if (ct_tmpl_activate(fd))
   3577 		crabort("cannot activate contract template",
   3578 		    REMOVE_FIFO | CONSOLE_MSG);
   3579 
   3580 	(void) close(fd);
   3581 }
   3582 
   3583 /*
   3584  * Clear active process contract template.
   3585  */
   3586 static void
   3587 contract_clear_template(void)
   3588 {
   3589 	int fd;
   3590 
   3591 	if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
   3592 		crabort("cannot open process contract template",
   3593 		    REMOVE_FIFO | CONSOLE_MSG);
   3594 
   3595 	if (ct_tmpl_clear(fd))
   3596 		crabort("cannot clear contract template",
   3597 		    REMOVE_FIFO | CONSOLE_MSG);
   3598 
   3599 	(void) close(fd);
   3600 }
   3601 
   3602 /*
   3603  * Abandon latest process contract unconditionally.  If we have leaked [some
   3604  * critical amount], exit such that the kernel reaps our contracts.
   3605  */
   3606 static void
   3607 contract_abandon_latest(pid_t pid)
   3608 {
   3609 	int r;
   3610 	ctid_t id;
   3611 	static uint_t cts_lost;
   3612 
   3613 	if (cts_lost > MAX_LOST_CONTRACTS)
   3614 		crabort("repeated failure to abandon contracts",
   3615 		    REMOVE_FIFO | CONSOLE_MSG);
   3616 
   3617 	if (r = contract_latest(&id)) {
   3618 		msg("could not obtain latest contract for "
   3619 		    "PID %ld: %s", pid, strerror(r));
   3620 		cts_lost++;
   3621 		return;
   3622 	}
   3623 
   3624 	if (r = contract_abandon_id(id)) {
   3625 		msg("could not abandon latest contract %ld: %s", id,
   3626 		    strerror(r));
   3627 		cts_lost++;
   3628 		return;
   3629 	}
   3630 }
   3631 
   3632 static struct shared *
   3633 create_shared(void *obj, void * (*obj_alloc)(void *obj),
   3634 	void (*obj_free)(void *))
   3635 {
   3636 	struct shared *out;
   3637 
   3638 	if ((out = xmalloc(sizeof (struct shared))) == NULL) {
   3639 		return (NULL);
   3640 	}
   3641 	if ((out->obj = obj_alloc(obj)) == NULL) {
   3642 		free(out);
   3643 		return (NULL);
   3644 	}
   3645 	out->count = 1;
   3646 	out->free = obj_free;
   3647 
   3648 	return (out);
   3649 }
   3650 
   3651 static struct shared *
   3652 create_shared_str(char *str)
   3653 {
   3654 	return (create_shared(str, (void *(*)(void *))strdup, free));
   3655 }
   3656 
   3657 static struct shared *
   3658 dup_shared(struct shared *obj)
   3659 {
   3660 	if (obj != NULL) {
   3661 		obj->count++;
   3662 	}
   3663 	return (obj);
   3664 }
   3665 
   3666 static void
   3667 rel_shared(struct shared *obj)
   3668 {
   3669 	if (obj && (--obj->count) == 0) {
   3670 		obj->free(obj->obj);
   3671 		free(obj);
   3672 	}
   3673 }
   3674 
   3675 static void *
   3676 get_obj(struct shared *obj)
   3677 {
   3678 	return (obj->obj);
   3679 }
   3680