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