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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     33 
     34 #ifdef lint
     35 /* make lint happy */
     36 #define	__EXTENSIONS__
     37 #endif
     38 
     39 #include <sys/contract/process.h>
     40 #include <sys/ctfs.h>
     41 #include <sys/param.h>
     42 #include <sys/resource.h>
     43 #include <sys/stat.h>
     44 #include <sys/task.h>
     45 #include <sys/time.h>
     46 #include <sys/types.h>
     47 #include <sys/utsname.h>
     48 #include <sys/wait.h>
     49 
     50 #include <security/pam_appl.h>
     51 
     52 #include <alloca.h>
     53 #include <ctype.h>
     54 #include <deflt.h>
     55 #include <dirent.h>
     56 #include <errno.h>
     57 #include <fcntl.h>
     58 #include <grp.h>
     59 #include <libcontract.h>
     60 #include <libcontract_priv.h>
     61 #include <limits.h>
     62 #include <locale.h>
     63 #include <poll.h>
     64 #include <project.h>
     65 #include <pwd.h>
     66 #include <signal.h>
     67 #include <stdarg.h>
     68 #include <stdio.h>
     69 #include <stdlib.h>
     70 #include <string.h>
     71 #include <stropts.h>
     72 #include <time.h>
     73 #include <unistd.h>
     74 
     75 #include "cron.h"
     76 
     77 /*
     78  * #define	DEBUG
     79  */
     80 
     81 #define	MAIL		"/usr/bin/mail"	/* mail program to use */
     82 #define	CONSOLE		"/dev/console"	/* where messages go when cron dies */
     83 
     84 #define	TMPINFILE	"/tmp/crinXXXXXX"  /* file to put stdin in for cmd  */
     85 #define	TMPDIR		"/tmp"
     86 #define	PFX		"crout"
     87 #define	TMPOUTFILE	"/tmp/croutXXXXXX" /* file to place stdout, stderr */
     88 
     89 #define	INMODE		00400		/* mode for stdin file	*/
     90 #define	OUTMODE		00600		/* mode for stdout file */
     91 #define	ISUID		S_ISUID		/* mode for verifing at jobs */
     92 
     93 #define	INFINITY	2147483647L	/* upper bound on time	*/
     94 #define	CUSHION		180L
     95 #define	ZOMB		100		/* proc slot used for mailing output */
     96 
     97 #define	JOBF		'j'
     98 #define	NICEF		'n'
     99 #define	USERF		'u'
    100 #define	WAITF		'w'
    101 
    102 #define	BCHAR		'>'
    103 #define	ECHAR		'<'
    104 
    105 #define	DEFAULT		0
    106 #define	LOAD		1
    107 #define	QBUFSIZ		80
    108 
    109 /* Defined actions for crabort() routine */
    110 #define	NO_ACTION	000
    111 #define	REMOVE_FIFO	001
    112 #define	CONSOLE_MSG	002
    113 
    114 #define	BADCD		"can't change directory to the crontab directory."
    115 #define	NOREADDIR	"can't read the crontab directory."
    116 
    117 #define	BADJOBOPEN	"unable to read your at job."
    118 #define	BADSHELL	"because your login shell \
    119 isn't /usr/bin/sh, you can't use cron."
    120 
    121 #define	BADSTAT		"can't access your crontab or at-job file. Resubmit it."
    122 #define	BADPROJID	"can't set project id for your job."
    123 #define	CANTCDHOME	"can't change directory to your home directory.\
    124 \nYour commands will not be executed."
    125 #define	CANTEXECSH	"unable to exec the shell for one of your commands."
    126 #define	NOREAD		"can't read your crontab file.  Resubmit it."
    127 #define	BADTYPE		"crontab or at-job file is not a regular file.\n"
    128 #define	NOSTDIN		"unable to create a standard input file for \
    129 one of your crontab commands. \
    130 \nThat command was not executed."
    131 
    132 #define	NOTALLOWED	"you are not authorized to use cron.  Sorry."
    133 #define	STDERRMSG	"\n\n********************************************\
    134 *****\nCron: The previous message is the \
    135 standard output and standard error \
    136 \nof one of your cron commands.\n"
    137 
    138 #define	STDOUTERR	"one of your commands generated output or errors, \
    139 but cron was unable to mail you this output.\
    140 \nRemember to redirect standard output and standard \
    141 error for each of your commands."
    142 
    143 #define	CLOCK_DRIFT	"clock time drifted backwards after event!\n"
    144 #define	PIDERR		"unexpected pid returned %d (ignored)"
    145 #define	CRONTABERR	"Subject: Your crontab file has an error in it\n\n"
    146 #define	CRONOUT		"Subject: Output from \"cron\" command\n\n"
    147 #define	MALLOCERR	"out of space, cannot create new string\n"
    148 
    149 #define	DIDFORK didfork
    150 #define	NOFORK !didfork
    151 
    152 #define	MAILBUFLEN	(8*1024)
    153 #define	LINELIMIT	80
    154 #define	MAILBINITFREE	(MAILBUFLEN - (sizeof (cte_intro) - 1) \
    155 	    - (sizeof (cte_trail1) - 1) - (sizeof (cte_trail2) - 1) - 1)
    156 
    157 #define	ERR_CRONTABENT	0	/* error in crontab file entry */
    158 #define	ERR_UNIXERR	1	/* error in some system call */
    159 #define	ERR_CANTEXECCRON 2	/* error setting up "cron" job environment */
    160 #define	ERR_CANTEXECAT	3	/* error setting up "at" job environment */
    161 #define	ERR_NOTREG	4	/* error not a regular file */
    162 
    163 #define	PROJECT		"project="
    164 
    165 #define	MAX_LOST_CONTRACTS	2048	/* reset if this many failed abandons */
    166 
    167 #define	FORMAT	"%a %b %e %H:%M:%S %Y"
    168 static char	timebuf[80];
    169 
    170 struct event {
    171 	time_t time;	/* time of the event	*/
    172 	short etype;	/* what type of event; 0=cron, 1=at	*/
    173 	char *cmd;	/* command for cron, job name for at	*/
    174 	struct usr *u;	/* ptr to the owner (usr) of this event	*/
    175 	struct event *link;	/* ptr to another event for this user */
    176 	union {
    177 		struct { /* for crontab events */
    178 			char *minute;	/*  (these	*/
    179 			char *hour;	/*   fields	*/
    180 			char *daymon;	/*   are	*/
    181 			char *month;	/*   from	*/
    182 			char *dayweek;	/*   crontab)	*/
    183 			char *input;	/* ptr to stdin	*/
    184 		} ct;
    185 		struct { /* for at events */
    186 			short exists;	/* for revising at events	*/
    187 			int eventid;	/* for el_remove-ing at events	*/
    188 		} at;
    189 	} of;
    190 };
    191 
    192 struct usr {
    193 	char *name;	/* name of user (e.g. "root")	*/
    194 	char *home;	/* home directory for user	*/
    195 	uid_t uid;	/* user id	*/
    196 	gid_t gid;	/* group id	*/
    197 	int aruncnt;	/* counter for running jobs per uid */
    198 	int cruncnt;	/* counter for running cron jobs per uid */
    199 	int ctid;	/* for el_remove-ing crontab events */
    200 	short ctexists;	/* for revising crontab events	*/
    201 	struct event *ctevents;	/* list of this usr's crontab events */
    202 	struct event *atevents;	/* list of this usr's at events */
    203 	struct usr *nextusr;
    204 };	/* ptr to next user	*/
    205 
    206 static struct	queue
    207 {
    208 	int njob;	/* limit */
    209 	int nice;	/* nice for execution */
    210 	int nwait;	/* wait time to next execution attempt */
    211 	int nrun;	/* number running */
    212 }
    213 	qd = {100, 2, 60},		/* default values for queue defs */
    214 	qt[NQUEUE];
    215 static struct	queue	qq;
    216 
    217 static struct runinfo
    218 {
    219 	pid_t	pid;
    220 	short	que;
    221 	struct  usr *rusr;	/* pointer to usr struct */
    222 	char	*outfile;	/* file where stdout & stderr are trapped */
    223 	short	jobtype;	/* what type of event: 0=cron, 1=at */
    224 	char	*jobname;	/* command for "cron", jobname for "at" */
    225 	int	mailwhendone;	/* 1 = send mail even if no ouptut */
    226 	struct runinfo *next;
    227 }	*rthead;
    228 
    229 static struct miscpid {
    230 	pid_t		pid;
    231 	struct miscpid	*next;
    232 }	*miscpid_head;
    233 
    234 static pid_t cron_pid;	/* own pid */
    235 static char didfork = 0; /* flag to see if I'm process group leader */
    236 static int msgfd;	/* file descriptor for fifo queue */
    237 static int ecid = 1;	/* event class id for el_remove(); MUST be set to 1 */
    238 static int delayed;	/* is job being rescheduled or did it run first time */
    239 static int cwd;		/* current working directory */
    240 static struct event *next_event;	/* the next event to execute	*/
    241 static struct usr *uhead;		/* ptr to the list of users	*/
    242 
    243 /* Variables for error handling at reading crontabs. */
    244 static char cte_intro[] = "Line(s) with errors:\n\n";
    245 static char cte_trail1[] = "\nMax number of errors encountered.";
    246 static char cte_trail2[] = " Evaluation of crontab aborted.\n";
    247 static int cte_free = MAILBINITFREE;	/* Free buffer space */
    248 static char *cte_text = NULL;		/* Text buffer pointer */
    249 static char *cte_lp;			/* Next free line in cte_text */
    250 static int cte_nvalid;			/* Valid lines found */
    251 
    252 /* user's default environment for the shell */
    253 #define	ROOTPATH	"PATH=/usr/sbin:/usr/bin"
    254 #define	NONROOTPATH	"PATH=/usr/bin:"
    255 
    256 static char *Def_supath	= NULL;
    257 static char *Def_path		= NULL;
    258 static char path[LINE_MAX]	= "PATH=";
    259 static char supath[LINE_MAX]	= "PATH=";
    260 static char homedir[LINE_MAX]	= "HOME=";
    261 static char logname[LINE_MAX]	= "LOGNAME=";
    262 static char tzone[LINE_MAX]	= "TZ=";
    263 static char *envinit[] = {
    264 	homedir,
    265 	logname,
    266 	ROOTPATH,
    267 	"SHELL=/usr/bin/sh",
    268 	tzone,
    269 	NULL
    270 };
    271 
    272 extern char **environ;
    273 
    274 #define	DEFTZ		"GMT"
    275 static	int	log = 0;
    276 static	char	hzname[10];
    277 
    278 static void cronend(int);
    279 static void thaw_handler(int);
    280 static void child_handler(int);
    281 static void child_sigreset(void);
    282 
    283 static void mod_ctab(char *, time_t);
    284 static void mod_atjob(char *, time_t);
    285 static void add_atevent(struct usr *, char *, time_t, int);
    286 static void rm_ctevents(struct usr *);
    287 static void cleanup(struct runinfo *rn, int r);
    288 static void crabort(char *, int);
    289 static void msg(char *fmt, ...);
    290 static void logit(int, struct runinfo *, int);
    291 static void parsqdef(char *);
    292 static void defaults();
    293 static void initialize(int);
    294 static void quedefs(int);
    295 static int idle(long);
    296 static struct usr *find_usr(char *);
    297 static int ex(struct event *e);
    298 static void read_dirs(int);
    299 static void mail(char *, char *, int);
    300 static char *next_field(int, int);
    301 static void readcron(struct usr *, time_t);
    302 static int next_ge(int, char *);
    303 static void free_if_unused(struct usr *);
    304 static void del_atjob(char *, char *);
    305 static void del_ctab(char *);
    306 static void resched(int);
    307 static int msg_wait(long);
    308 static struct runinfo *rinfo_get(pid_t);
    309 static void rinfo_free(struct runinfo *rp);
    310 static void mail_result(struct usr *p, struct runinfo *pr, size_t filesize);
    311 static time_t next_time(struct event *, time_t);
    312 static time_t get_switching_time(int, time_t);
    313 static time_t xmktime(struct tm *);
    314 static void process_msg(struct message *, time_t);
    315 static void reap_child(void);
    316 static void miscpid_insert(pid_t);
    317 static int miscpid_delete(pid_t);
    318 static void contract_set_template(void);
    319 static void contract_clear_template(void);
    320 static void contract_abandon_latest(pid_t);
    321 
    322 static void cte_init(void);
    323 static void cte_add(int, char *);
    324 static void cte_valid(void);
    325 static int cte_istoomany(void);
    326 static void cte_sendmail(char *);
    327 
    328 static int set_user_cred(const struct usr *, struct project *);
    329 
    330 /*
    331  * last_time is set immediately prior to exection of an event (via ex())
    332  * to indicate the last time an event was executed.  This was (surely)
    333  * it's original intended use.
    334  */
    335 static time_t last_time, init_time, t_old;
    336 
    337 static int		accept_sigcld, notifypipe[2];
    338 static sigset_t		defmask, childmask;
    339 
    340 /*
    341  * BSM hooks
    342  */
    343 extern int	audit_cron_session(char *, char *, uid_t, gid_t, char *);
    344 extern void	audit_cron_new_job(char *, int, void *);
    345 extern void	audit_cron_bad_user(char *);
    346 extern void	audit_cron_user_acct_expired(char *);
    347 extern int	audit_cron_create_anc_file(char *, char *, char *, uid_t);
    348 extern int	audit_cron_delete_anc_file(char *, char *);
    349 extern int	audit_cron_is_anc_name(char *);
    350 extern int	audit_cron_mode();
    351 
    352 static int cron_conv(int, struct pam_message **,
    353 		struct pam_response **, void *);
    354 
    355 static struct pam_conv pam_conv = {cron_conv, NULL};
    356 static pam_handle_t *pamh;	/* Authentication handle */
    357 
    358 /*
    359  * Function to help check a user's credentials.
    360  */
    361 
    362 static int verify_user_cred(struct usr *u);
    363 
    364 /*
    365  * Values returned by verify_user_cred and set_user_cred:
    366  */
    367 
    368 #define	VUC_OK		0
    369 #define	VUC_BADUSER	1
    370 #define	VUC_NOTINGROUP	2
    371 #define	VUC_EXPIRED	3
    372 #define	VUC_NEW_AUTH	4
    373 
    374 /*
    375  * Modes of process_anc_files function
    376  */
    377 #define	CRON_ANC_DELETE	1
    378 #define	CRON_ANC_CREATE	0
    379 
    380 /*
    381  * Functions to remove a user or job completely from the running database.
    382  */
    383 static void clean_out_atjobs(struct usr *u);
    384 static void clean_out_ctab(struct usr *u);
    385 static void clean_out_user(struct usr *u);
    386 static void cron_unlink(char *name);
    387 static void process_anc_files(int);
    388 
    389 /*
    390  * functions in elm.c
    391  */
    392 extern void el_init(int, time_t, time_t, int);
    393 extern void el_add(void *, time_t, int);
    394 extern void el_remove(int, int);
    395 extern int el_empty(void);
    396 extern void *el_first(void);
    397 extern void el_delete(void);
    398 
    399 static int valid_entry(char *, int);
    400 static struct usr *create_ulist(char *, int);
    401 static void init_cronevent(char *, int);
    402 static void init_atevent(char *, time_t, int, int);
    403 static void update_atevent(struct usr *, char *, time_t, int);
    404 
    405 int
    406 main(int argc, char *argv[])
    407 {
    408 	time_t t;
    409 	time_t ne_time;		/* amt of time until next event execution */
    410 	time_t newtime, lastmtime = 0L;
    411 	struct usr *u;
    412 	struct event *e, *e2, *eprev;
    413 	struct stat buf;
    414 	pid_t rfork;
    415 	struct sigaction act;
    416 
    417 	/*
    418 	 * reset is set to 1 via the return from ex() should ex() find
    419 	 * that the event to be executed is being run at the wrong time.
    420 	 * We immediately return to the top of the while (TRUE) loop in
    421 	 * main() where the event list is cleared and rebuilt, and reset
    422 	 * is set back to 0.
    423 	 */
    424 	int reset = 0;
    425 
    426 	/*
    427 	 * Only the privileged user can run this command.
    428 	 */
    429 	if (getuid() != 0)
    430 		crabort(NOTALLOWED, 0);
    431 
    432 begin:
    433 	(void) setlocale(LC_ALL, "");
    434 	/* fork unless 'nofork' is specified */
    435 	if ((argc <= 1) || (strcmp(argv[1], "nofork"))) {
    436 		if (rfork = fork()) {
    437 			if (rfork == (pid_t)-1) {
    438 				(void) sleep(30);
    439 				goto begin;
    440 			}
    441 			return (0);
    442 		}
    443 		didfork++;
    444 		(void) setpgrp();	/* detach cron from console */
    445 	}
    446 
    447 	(void) umask(022);
    448 	(void) signal(SIGHUP, SIG_IGN);
    449 	(void) signal(SIGINT, SIG_IGN);
    450 	(void) signal(SIGQUIT, SIG_IGN);
    451 	(void) signal(SIGTERM, cronend);
    452 
    453 	defaults();
    454 	initialize(1);
    455 	quedefs(DEFAULT);	/* load default queue definitions */
    456 	cron_pid = getpid();
    457 	msg("*** cron started ***   pid = %d", cron_pid);
    458 	(void) sigset(SIGTHAW, thaw_handler);
    459 	/*
    460 	 * setup SIGCLD handler/mask
    461 	 */
    462 	act.sa_handler = child_handler;
    463 	act.sa_flags = 0;
    464 	(void) sigemptyset(&act.sa_mask);
    465 	(void) sigaddset(&act.sa_mask, SIGCLD);
    466 	(void) sigaction(SIGCLD, &act, NULL);
    467 	(void) sigemptyset(&childmask);
    468 	(void) sigaddset(&childmask, SIGCLD);
    469 	(void) sigprocmask(SIG_BLOCK, &childmask, &defmask);
    470 
    471 	if (pipe(notifypipe) != 0) {
    472 		crabort("cannot create pipe", REMOVE_FIFO|CONSOLE_MSG);
    473 	}
    474 	/*
    475 	 * will set O_NONBLOCK, so that the write() from child_handler
    476 	 * never be blocked.
    477 	 */
    478 	(void) fcntl(notifypipe[0], F_SETFL, O_WRONLY|O_NONBLOCK);
    479 
    480 	t_old = time(NULL);
    481 	last_time = t_old;
    482 	for (;;) {		/* MAIN LOOP */
    483 		t = time(NULL);
    484 		if ((t_old > t) || (t-last_time > CUSHION) || reset) {
    485 			reset = 0;
    486 			/* the time was set backwards or forward */
    487 			el_delete();
    488 			u = uhead;
    489 			while (u != NULL) {
    490 				rm_ctevents(u);
    491 				e = u->atevents;
    492 				while (e != NULL) {
    493 					free(e->cmd);
    494 					e2 = e->link;
    495 					free(e);
    496 					e = e2;
    497 				}
    498 				u->atevents = NULL;
    499 				u = u->nextusr;
    500 			}
    501 			(void) close(msgfd);
    502 			initialize(0);
    503 			t = time(NULL);
    504 			last_time = t;
    505 		}
    506 		t_old = t;
    507 
    508 		if (next_event == NULL && !el_empty()) {
    509 			next_event = (struct event *)el_first();
    510 		}
    511 		if (next_event == NULL) {
    512 			ne_time = INFINITY;
    513 		} else {
    514 			ne_time = next_event->time - t;
    515 #ifdef DEBUG
    516 			cftime(timebuf, "%C", &next_event->time);
    517 			(void) fprintf(stderr, "next_time=%ld %s\n",
    518 			    next_event->time, timebuf);
    519 #endif
    520 		}
    521 		if (ne_time > 0) {
    522 			if ((reset = idle(ne_time)) != 0)
    523 				continue;
    524 		}
    525 
    526 		if (stat(QUEDEFS, &buf)) {
    527 			msg("cannot stat QUEDEFS file");
    528 		} else if (lastmtime != buf.st_mtime) {
    529 			quedefs(LOAD);
    530 			lastmtime = buf.st_mtime;
    531 		}
    532 
    533 		last_time = next_event->time; /* save execution time */
    534 
    535 		if (reset = ex(next_event))
    536 			continue;
    537 
    538 		switch (next_event->etype) {
    539 		case CRONEVENT:
    540 			/* add cronevent back into the main event list */
    541 			if (delayed) {
    542 				delayed = 0;
    543 				break;
    544 			}
    545 
    546 			/*
    547 			 * check if time(0)< last_time. if so, then the
    548 			 * system clock has gone backwards. to prevent this
    549 			 * job from being started twice, we reschedule this
    550 			 * job for the >>next time after last_time<<, and
    551 			 * then set next_event->time to this. note that
    552 			 * crontab's resolution is 1 minute.
    553 			 */
    554 
    555 			if (last_time > time(NULL)) {
    556 				msg(CLOCK_DRIFT);
    557 				/*
    558 				 * bump up to next 30 second
    559 				 * increment
    560 				 * 1 <= newtime <= 30
    561 				 */
    562 				newtime = 30 - (last_time % 30);
    563 				newtime += last_time;
    564 
    565 				/*
    566 				 * get the next scheduled event,
    567 				 * not the one that we just
    568 				 * kicked off!
    569 				 */
    570 				next_event->time =
    571 				    next_time(next_event, newtime);
    572 				t_old = time(NULL);
    573 			} else {
    574 				next_event->time =
    575 				    next_time(next_event, (time_t)0);
    576 			}
    577 #ifdef DEBUG
    578 			cftime(timebuf, "%C", &next_event->time);
    579 			(void) fprintf(stderr,
    580 			    "pushing back cron event %s at %ld (%s)\n",
    581 			    next_event->cmd, next_event->time, timebuf);
    582 #endif
    583 
    584 			el_add(next_event, next_event->time,
    585 			    (next_event->u)->ctid);
    586 			break;
    587 		default:
    588 			/* remove at or batch job from system */
    589 			if (delayed) {
    590 				delayed = 0;
    591 				break;
    592 			}
    593 			eprev = NULL;
    594 			e = (next_event->u)->atevents;
    595 			while (e != NULL) {
    596 				if (e == next_event) {
    597 					if (eprev == NULL)
    598 						(e->u)->atevents = e->link;
    599 					else
    600 						eprev->link = e->link;
    601 					free(e->cmd);
    602 					free(e);
    603 					break;
    604 				} else {
    605 					eprev = e;
    606 					e = e->link;
    607 				}
    608 			}
    609 			break;
    610 		}
    611 		next_event = NULL;
    612 	}
    613 
    614 	/*NOTREACHED*/
    615 }
    616 
    617 static void
    618 initialize(int firstpass)
    619 {
    620 #ifdef DEBUG
    621 	(void) fprintf(stderr, "in initialize\n");
    622 #endif
    623 	init_time = time(NULL);
    624 	el_init(8, init_time, (time_t)(60*60*24), 10);
    625 	if (firstpass) {
    626 		/* for mail(1), make sure messages come from root */
    627 		if (putenv("LOGNAME=root") != 0) {
    628 			crabort("cannot expand env variable",
    629 			    REMOVE_FIFO|CONSOLE_MSG);
    630 		}
    631 		if (access(FIFO, R_OK) == -1) {
    632 			if (errno == ENOENT) {
    633 				if (mknod(FIFO, S_IFIFO|0600, 0) != 0)
    634 					crabort("cannot create fifo queue",
    635 					    REMOVE_FIFO|CONSOLE_MSG);
    636 			} else {
    637 				if (NOFORK) {
    638 					/* didn't fork... init(1M) is waiting */
    639 					(void) sleep(60);
    640 				}
    641 				perror("FIFO");
    642 				crabort("cannot access fifo queue",
    643 				    REMOVE_FIFO|CONSOLE_MSG);
    644 			}
    645 		} else {
    646 			if (NOFORK) {
    647 				/* didn't fork... init(1M) is waiting */
    648 				(void) sleep(60);
    649 				/*
    650 				 * the wait is painful, but we don't want
    651 				 * init respawning this quickly
    652 				 */
    653 			}
    654 			crabort("cannot start cron; FIFO exists", CONSOLE_MSG);
    655 		}
    656 	}
    657 
    658 	if ((msgfd = open(FIFO, O_RDWR)) < 0) {
    659 		perror("! open");
    660 		crabort("cannot open fifo queue", REMOVE_FIFO|CONSOLE_MSG);
    661 	}
    662 
    663 	/*
    664 	 * read directories, create users list, and add events to the
    665 	 * main event list. Only zero user list on firstpass.
    666 	 */
    667 	if (firstpass)
    668 		uhead = NULL;
    669 	read_dirs(firstpass);
    670 	next_event = NULL;
    671 
    672 	if (!firstpass)
    673 		return;
    674 
    675 	/* stdout is log file */
    676 	if (freopen(ACCTFILE, "a", stdout) == NULL)
    677 		(void) fprintf(stderr, "cannot open %s\n", ACCTFILE);
    678 
    679 	/* log should be root-only */
    680 	(void) fchmod(1, S_IRUSR|S_IWUSR);
    681 
    682 	/* stderr also goes to ACCTFILE */
    683 	(void) close(fileno(stderr));
    684 	(void) dup(1);
    685 
    686 	/* null for stdin */
    687 	(void) freopen("/dev/null", "r", stdin);
    688 
    689 	contract_set_template();
    690 }
    691 
    692 static void
    693 read_dirs(int first)
    694 {
    695 	DIR		*dir;
    696 	struct dirent	*dp;
    697 	char		*ptr;
    698 	int		jobtype;
    699 	time_t		tim;
    700 
    701 
    702 	if (chdir(CRONDIR) == -1)
    703 		crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG);
    704 	cwd = CRON;
    705 	if ((dir = opendir(".")) == NULL)
    706 		crabort(NOREADDIR, REMOVE_FIFO|CONSOLE_MSG);
    707 	while ((dp = readdir(dir)) != NULL) {
    708 		if (!valid_entry(dp->d_name, CRONEVENT))
    709 			continue;
    710 		init_cronevent(dp->d_name, first);
    711 	}
    712 	(void) closedir(dir);
    713 
    714 	if (chdir(ATDIR) == -1) {
    715 		msg("cannot chdir to at directory");
    716 		return;
    717 	}
    718 	if ((dir = opendir(".")) == NULL) {
    719 		msg("cannot read at at directory");
    720 		return;
    721 	}
    722 	cwd = AT;
    723 	while ((dp = readdir(dir)) != NULL) {
    724 		if (!valid_entry(dp->d_name, ATEVENT))
    725 			continue;
    726 		ptr = dp->d_name;
    727 		if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
    728 			continue;
    729 		ptr++;
    730 		if (!isalpha(*ptr))
    731 			continue;
    732 		jobtype = *ptr - 'a';
    733 		if (jobtype >= NQUEUE) {
    734 			cron_unlink(dp->d_name);
    735 			continue;
    736 		}
    737 		init_atevent(dp->d_name, tim, jobtype, first);
    738 	}
    739 	(void) closedir(dir);
    740 }
    741 
    742 static int
    743 valid_entry(char *name, int type)
    744 {
    745 	struct stat	buf;
    746 
    747 	if (strcmp(name, ".") == 0 ||
    748 	    strcmp(name, "..") == 0)
    749 		return (0);
    750 
    751 	/* skip over ancillary file names */
    752 	if (audit_cron_is_anc_name(name))
    753 		return (0);
    754 
    755 	if (stat(name, &buf)) {
    756 		mail(name, BADSTAT, ERR_UNIXERR);
    757 		cron_unlink(name);
    758 		return (0);
    759 	}
    760 	if (!S_ISREG(buf.st_mode)) {
    761 		mail(name, BADTYPE, ERR_NOTREG);
    762 		cron_unlink(name);
    763 		return (0);
    764 	}
    765 	if (type == ATEVENT) {
    766 		if (!(buf.st_mode & ISUID)) {
    767 			cron_unlink(name);
    768 			return (0);
    769 		}
    770 	}
    771 	return (1);
    772 }
    773 
    774 struct usr *
    775 create_ulist(char *name, int type)
    776 {
    777 	struct usr	*u;
    778 
    779 	u = xmalloc(sizeof (struct usr));
    780 	u->name = xmalloc(strlen(name) + 1);
    781 	(void) strcpy(u->name, name);
    782 	u->home = NULL;
    783 	if (type == CRONEVENT) {
    784 		u->ctexists = TRUE;
    785 		u->ctid = ecid++;
    786 	} else {
    787 		u->ctexists = FALSE;
    788 		u->ctid = 0;
    789 	}
    790 	u->ctevents = NULL;
    791 	u->atevents = NULL;
    792 	u->aruncnt = 0;
    793 	u->cruncnt = 0;
    794 	u->nextusr = uhead;
    795 	uhead = u;
    796 	return (u);
    797 }
    798 
    799 void
    800 init_cronevent(char *name, int first)
    801 {
    802 	struct usr	*u;
    803 
    804 	if (first) {
    805 		u = create_ulist(name, CRONEVENT);
    806 		readcron(u, 0);
    807 	} else {
    808 		if ((u = find_usr(name)) == NULL) {
    809 			u = create_ulist(name, CRONEVENT);
    810 			readcron(u, 0);
    811 		} else {
    812 			u->ctexists = TRUE;
    813 			rm_ctevents(u);
    814 			el_remove(u->ctid, 0);
    815 			readcron(u, 0);
    816 		}
    817 	}
    818 }
    819 
    820 void
    821 init_atevent(char *name, time_t tim, int jobtype, int first)
    822 {
    823 	struct usr	*u;
    824 
    825 	if (first) {
    826 		u = create_ulist(name, ATEVENT);
    827 		add_atevent(u, name, tim, jobtype);
    828 	} else {
    829 		if ((u = find_usr(name)) == NULL) {
    830 			u = create_ulist(name, ATEVENT);
    831 			add_atevent(u, name, tim, jobtype);
    832 		} else {
    833 			update_atevent(u, name, tim, jobtype);
    834 		}
    835 	}
    836 }
    837 
    838 static void
    839 mod_ctab(char *name, time_t reftime)
    840 {
    841 	struct	passwd	*pw;
    842 	struct	stat	buf;
    843 	struct	usr	*u;
    844 	char	namebuf[PATH_MAX];
    845 	char	*pname;
    846 
    847 	/* skip over ancillary file names */
    848 	if (audit_cron_is_anc_name(name))
    849 		return;
    850 
    851 	if ((pw = getpwnam(name)) == NULL) {
    852 		msg("No such user as %s - cron entries not created", name);
    853 		return;
    854 	}
    855 	if (cwd != CRON) {
    856 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
    857 		    CRONDIR, name) >= sizeof (namebuf)) {
    858 			msg("Too long path name %s - cron entries not created",
    859 			    namebuf);
    860 			return;
    861 		}
    862 		pname = namebuf;
    863 	} else {
    864 		pname = name;
    865 	}
    866 	/*
    867 	 * a warning message is given by the crontab command so there is
    868 	 * no need to give one here......  use this code if you only want
    869 	 * users with a login shell of /usr/bin/sh to use cron
    870 	 */
    871 #ifdef BOURNESHELLONLY
    872 	if ((strcmp(pw->pw_shell, "") != 0) &&
    873 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
    874 		mail(name, BADSHELL, ERR_CANTEXECCRON);
    875 		cron_unlink(pname);
    876 		return;
    877 	}
    878 #endif
    879 	if (stat(pname, &buf)) {
    880 		mail(name, BADSTAT, ERR_UNIXERR);
    881 		cron_unlink(pname);
    882 		return;
    883 	}
    884 	if (!S_ISREG(buf.st_mode)) {
    885 		mail(name, BADTYPE, ERR_CRONTABENT);
    886 		return;
    887 	}
    888 	if ((u = find_usr(name)) == NULL) {
    889 #ifdef DEBUG
    890 		(void) fprintf(stderr, "new user (%s) with a crontab\n", name);
    891 #endif
    892 		u = create_ulist(name, CRONEVENT);
    893 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
    894 		(void) strcpy(u->home, pw->pw_dir);
    895 		u->uid = pw->pw_uid;
    896 		u->gid = pw->pw_gid;
    897 		readcron(u, reftime);
    898 	} else {
    899 		u->uid = pw->pw_uid;
    900 		u->gid = pw->pw_gid;
    901 		if (u->home != NULL) {
    902 			if (strcmp(u->home, pw->pw_dir) != 0) {
    903 				free(u->home);
    904 				u->home = xmalloc(strlen(pw->pw_dir) + 1);
    905 				(void) strcpy(u->home, pw->pw_dir);
    906 			}
    907 		} else {
    908 			u->home = xmalloc(strlen(pw->pw_dir) + 1);
    909 			(void) strcpy(u->home, pw->pw_dir);
    910 		}
    911 		u->ctexists = TRUE;
    912 		if (u->ctid == 0) {
    913 #ifdef DEBUG
    914 			(void) fprintf(stderr, "%s now has a crontab\n",
    915 			    u->name);
    916 #endif
    917 			/* user didnt have a crontab last time */
    918 			u->ctid = ecid++;
    919 			u->ctevents = NULL;
    920 			readcron(u, reftime);
    921 			return;
    922 		}
    923 #ifdef DEBUG
    924 		(void) fprintf(stderr, "%s has revised his crontab\n", u->name);
    925 #endif
    926 		rm_ctevents(u);
    927 		el_remove(u->ctid, 0);
    928 		readcron(u, reftime);
    929 	}
    930 }
    931 
    932 /* ARGSUSED */
    933 static void
    934 mod_atjob(char *name, time_t reftime)
    935 {
    936 	char	*ptr;
    937 	time_t	tim;
    938 	struct	passwd	*pw;
    939 	struct	stat	buf;
    940 	struct	usr	*u;
    941 	char	namebuf[PATH_MAX];
    942 	char	*pname;
    943 	int	jobtype;
    944 
    945 	ptr = name;
    946 	if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
    947 		return;
    948 	ptr++;
    949 	if (!isalpha(*ptr))
    950 		return;
    951 	jobtype = *ptr - 'a';
    952 
    953 	/* check for audit ancillary file */
    954 	if (audit_cron_is_anc_name(name))
    955 		return;
    956 
    957 	if (cwd != AT) {
    958 		if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name)
    959 		    >= sizeof (namebuf)) {
    960 			return;
    961 		}
    962 		pname = namebuf;
    963 	} else {
    964 		pname = name;
    965 	}
    966 	if (stat(pname, &buf) || jobtype >= NQUEUE) {
    967 		cron_unlink(pname);
    968 		return;
    969 	}
    970 	if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) {
    971 		cron_unlink(pname);
    972 		return;
    973 	}
    974 	if ((pw = getpwuid(buf.st_uid)) == NULL) {
    975 		cron_unlink(pname);
    976 		return;
    977 	}
    978 	/*
    979 	 * a warning message is given by the at command so there is no
    980 	 * need to give one here......use this code if you only want
    981 	 * users with a login shell of /usr/bin/sh to use cron
    982 	 */
    983 #ifdef BOURNESHELLONLY
    984 	if ((strcmp(pw->pw_shell, "") != 0) &&
    985 	    (strcmp(pw->pw_shell, SHELL) != 0)) {
    986 		mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT);
    987 		cron_unlink(pname);
    988 		return;
    989 	}
    990 #endif
    991 	if ((u = find_usr(pw->pw_name)) == NULL) {
    992 #ifdef DEBUG
    993 		(void) fprintf(stderr, "new user (%s) with an at job = %s\n",
    994 		    pw->pw_name, name);
    995 #endif
    996 		u = create_ulist(pw->pw_name, ATEVENT);
    997 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
    998 		(void) strcpy(u->home, pw->pw_dir);
    999 		u->uid = pw->pw_uid;
   1000 		u->gid = pw->pw_gid;
   1001 		add_atevent(u, name, tim, jobtype);
   1002 	} else {
   1003 		u->uid = pw->pw_uid;
   1004 		u->gid = pw->pw_gid;
   1005 		free(u->home);
   1006 		u->home = xmalloc(strlen(pw->pw_dir) + 1);
   1007 		(void) strcpy(u->home, pw->pw_dir);
   1008 		update_atevent(u, name, tim, jobtype);
   1009 	}
   1010 }
   1011 
   1012 static void
   1013 add_atevent(struct usr *u, char *job, time_t tim, int jobtype)
   1014 {
   1015 	struct event *e;
   1016 
   1017 	e = xmalloc(sizeof (struct event));
   1018 	e->etype = jobtype;
   1019 	e->cmd = xmalloc(strlen(job) + 1);
   1020 	(void) strcpy(e->cmd, job);
   1021 	e->u = u;
   1022 	e->link = u->atevents;
   1023 	u->atevents = e;
   1024 	e->of.at.exists = TRUE;
   1025 	e->of.at.eventid = ecid++;
   1026 	if (tim < init_time)	/* old job */
   1027 		e->time = init_time;
   1028 	else
   1029 		e->time = tim;
   1030 #ifdef DEBUG
   1031 	(void) fprintf(stderr, "add_atevent: user=%s, job=%s, time=%ld\n",
   1032 	    u->name, e->cmd, e->time);
   1033 #endif
   1034 	el_add(e, e->time, e->of.at.eventid);
   1035 }
   1036 
   1037 void
   1038 update_atevent(struct usr *u, char *name, time_t tim, int jobtype)
   1039 {
   1040 	struct event *e;
   1041 
   1042 	e = u->atevents;
   1043 	while (e != NULL) {
   1044 		if (strcmp(e->cmd, name) == 0) {
   1045 			e->of.at.exists = TRUE;
   1046 			break;
   1047 		} else {
   1048 			e = e->link;
   1049 		}
   1050 	}
   1051 	if (e == NULL) {
   1052 #if