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