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