10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
55558Sbasabi * Common Development and Distribution License (the "License").
65558Sbasabi * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*11115SNobutomo.Nakano@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
270Sstevel@tonic-gate /* All Rights Reserved */
280Sstevel@tonic-gate
290Sstevel@tonic-gate /* Copyright (c) 1987, 1988 Microsoft Corporation */
300Sstevel@tonic-gate /* All Rights Reserved */
310Sstevel@tonic-gate
320Sstevel@tonic-gate #ifdef lint
330Sstevel@tonic-gate /* make lint happy */
340Sstevel@tonic-gate #define __EXTENSIONS__
350Sstevel@tonic-gate #endif
360Sstevel@tonic-gate
370Sstevel@tonic-gate #include <sys/contract/process.h>
380Sstevel@tonic-gate #include <sys/ctfs.h>
390Sstevel@tonic-gate #include <sys/param.h>
400Sstevel@tonic-gate #include <sys/resource.h>
410Sstevel@tonic-gate #include <sys/stat.h>
420Sstevel@tonic-gate #include <sys/task.h>
430Sstevel@tonic-gate #include <sys/time.h>
440Sstevel@tonic-gate #include <sys/types.h>
450Sstevel@tonic-gate #include <sys/utsname.h>
460Sstevel@tonic-gate #include <sys/wait.h>
470Sstevel@tonic-gate
480Sstevel@tonic-gate #include <security/pam_appl.h>
490Sstevel@tonic-gate
500Sstevel@tonic-gate #include <alloca.h>
510Sstevel@tonic-gate #include <ctype.h>
520Sstevel@tonic-gate #include <deflt.h>
530Sstevel@tonic-gate #include <dirent.h>
540Sstevel@tonic-gate #include <errno.h>
550Sstevel@tonic-gate #include <fcntl.h>
560Sstevel@tonic-gate #include <grp.h>
570Sstevel@tonic-gate #include <libcontract.h>
580Sstevel@tonic-gate #include <libcontract_priv.h>
590Sstevel@tonic-gate #include <limits.h>
600Sstevel@tonic-gate #include <locale.h>
610Sstevel@tonic-gate #include <poll.h>
620Sstevel@tonic-gate #include <project.h>
630Sstevel@tonic-gate #include <pwd.h>
640Sstevel@tonic-gate #include <signal.h>
650Sstevel@tonic-gate #include <stdarg.h>
660Sstevel@tonic-gate #include <stdio.h>
670Sstevel@tonic-gate #include <stdlib.h>
680Sstevel@tonic-gate #include <string.h>
690Sstevel@tonic-gate #include <stropts.h>
700Sstevel@tonic-gate #include <time.h>
710Sstevel@tonic-gate #include <unistd.h>
728439SChris.Gerhard@sun.com #include <libzoneinfo.h>
730Sstevel@tonic-gate
740Sstevel@tonic-gate #include "cron.h"
750Sstevel@tonic-gate
760Sstevel@tonic-gate /*
770Sstevel@tonic-gate * #define DEBUG
780Sstevel@tonic-gate */
790Sstevel@tonic-gate
800Sstevel@tonic-gate #define MAIL "/usr/bin/mail" /* mail program to use */
810Sstevel@tonic-gate #define CONSOLE "/dev/console" /* where messages go when cron dies */
820Sstevel@tonic-gate
830Sstevel@tonic-gate #define TMPINFILE "/tmp/crinXXXXXX" /* file to put stdin in for cmd */
840Sstevel@tonic-gate #define TMPDIR "/tmp"
850Sstevel@tonic-gate #define PFX "crout"
860Sstevel@tonic-gate #define TMPOUTFILE "/tmp/croutXXXXXX" /* file to place stdout, stderr */
870Sstevel@tonic-gate
880Sstevel@tonic-gate #define INMODE 00400 /* mode for stdin file */
890Sstevel@tonic-gate #define OUTMODE 00600 /* mode for stdout file */
900Sstevel@tonic-gate #define ISUID S_ISUID /* mode for verifing at jobs */
910Sstevel@tonic-gate
920Sstevel@tonic-gate #define INFINITY 2147483647L /* upper bound on time */
930Sstevel@tonic-gate #define CUSHION 180L
940Sstevel@tonic-gate #define ZOMB 100 /* proc slot used for mailing output */
950Sstevel@tonic-gate
960Sstevel@tonic-gate #define JOBF 'j'
970Sstevel@tonic-gate #define NICEF 'n'
980Sstevel@tonic-gate #define USERF 'u'
990Sstevel@tonic-gate #define WAITF 'w'
1000Sstevel@tonic-gate
1010Sstevel@tonic-gate #define BCHAR '>'
1020Sstevel@tonic-gate #define ECHAR '<'
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate #define DEFAULT 0
1050Sstevel@tonic-gate #define LOAD 1
1060Sstevel@tonic-gate #define QBUFSIZ 80
1070Sstevel@tonic-gate
1080Sstevel@tonic-gate /* Defined actions for crabort() routine */
1090Sstevel@tonic-gate #define NO_ACTION 000
1100Sstevel@tonic-gate #define REMOVE_FIFO 001
1110Sstevel@tonic-gate #define CONSOLE_MSG 002
1120Sstevel@tonic-gate
1130Sstevel@tonic-gate #define BADCD "can't change directory to the crontab directory."
1140Sstevel@tonic-gate #define NOREADDIR "can't read the crontab directory."
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate #define BADJOBOPEN "unable to read your at job."
1170Sstevel@tonic-gate #define BADSHELL "because your login shell \
1180Sstevel@tonic-gate isn't /usr/bin/sh, you can't use cron."
1190Sstevel@tonic-gate
1205558Sbasabi #define BADSTAT "can't access your crontab or at-job file. Resubmit it."
1210Sstevel@tonic-gate #define BADPROJID "can't set project id for your job."
1228439SChris.Gerhard@sun.com #define CANTCDHOME "can't change directory to %s.\
1230Sstevel@tonic-gate \nYour commands will not be executed."
1248439SChris.Gerhard@sun.com #define CANTEXECSH "unable to exec the shell, %s, for one of your \
1258439SChris.Gerhard@sun.com commands."
1268439SChris.Gerhard@sun.com #define CANT_STR_LEN (sizeof (CANTEXECSH) > sizeof (CANTCDHOME) ? \
1278439SChris.Gerhard@sun.com sizeof (CANTEXECSH) : sizeof (CANTCDHOME))
1280Sstevel@tonic-gate #define NOREAD "can't read your crontab file. Resubmit it."
1295558Sbasabi #define BADTYPE "crontab or at-job file is not a regular file.\n"
1300Sstevel@tonic-gate #define NOSTDIN "unable to create a standard input file for \
1310Sstevel@tonic-gate one of your crontab commands. \
1320Sstevel@tonic-gate \nThat command was not executed."
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate #define NOTALLOWED "you are not authorized to use cron. Sorry."
1350Sstevel@tonic-gate #define STDERRMSG "\n\n********************************************\
1360Sstevel@tonic-gate *****\nCron: The previous message is the \
1370Sstevel@tonic-gate standard output and standard error \
1380Sstevel@tonic-gate \nof one of your cron commands.\n"
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate #define STDOUTERR "one of your commands generated output or errors, \
1410Sstevel@tonic-gate but cron was unable to mail you this output.\
1420Sstevel@tonic-gate \nRemember to redirect standard output and standard \
1430Sstevel@tonic-gate error for each of your commands."
1440Sstevel@tonic-gate
1450Sstevel@tonic-gate #define CLOCK_DRIFT "clock time drifted backwards after event!\n"
1460Sstevel@tonic-gate #define PIDERR "unexpected pid returned %d (ignored)"
1470Sstevel@tonic-gate #define CRONTABERR "Subject: Your crontab file has an error in it\n\n"
1480Sstevel@tonic-gate #define CRONOUT "Subject: Output from \"cron\" command\n\n"
1490Sstevel@tonic-gate #define MALLOCERR "out of space, cannot create new string\n"
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate #define DIDFORK didfork
1520Sstevel@tonic-gate #define NOFORK !didfork
1530Sstevel@tonic-gate
1540Sstevel@tonic-gate #define MAILBUFLEN (8*1024)
1550Sstevel@tonic-gate #define LINELIMIT 80
1560Sstevel@tonic-gate #define MAILBINITFREE (MAILBUFLEN - (sizeof (cte_intro) - 1) \
1570Sstevel@tonic-gate - (sizeof (cte_trail1) - 1) - (sizeof (cte_trail2) - 1) - 1)
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate #define ERR_CRONTABENT 0 /* error in crontab file entry */
1600Sstevel@tonic-gate #define ERR_UNIXERR 1 /* error in some system call */
1610Sstevel@tonic-gate #define ERR_CANTEXECCRON 2 /* error setting up "cron" job environment */
1620Sstevel@tonic-gate #define ERR_CANTEXECAT 3 /* error setting up "at" job environment */
1635558Sbasabi #define ERR_NOTREG 4 /* error not a regular file */
1640Sstevel@tonic-gate
1650Sstevel@tonic-gate #define PROJECT "project="
1660Sstevel@tonic-gate
1670Sstevel@tonic-gate #define MAX_LOST_CONTRACTS 2048 /* reset if this many failed abandons */
1680Sstevel@tonic-gate
1690Sstevel@tonic-gate #define FORMAT "%a %b %e %H:%M:%S %Y"
1700Sstevel@tonic-gate static char timebuf[80];
1710Sstevel@tonic-gate
172*11115SNobutomo.Nakano@Sun.COM static struct message msgbuf;
173*11115SNobutomo.Nakano@Sun.COM
1748439SChris.Gerhard@sun.com struct shared {
1758439SChris.Gerhard@sun.com int count; /* usage count */
1768439SChris.Gerhard@sun.com void (*free)(void *obj); /* routine that will free obj */
1778439SChris.Gerhard@sun.com void *obj; /* object */
1788439SChris.Gerhard@sun.com };
1798439SChris.Gerhard@sun.com
1800Sstevel@tonic-gate struct event {
1810Sstevel@tonic-gate time_t time; /* time of the event */
1820Sstevel@tonic-gate short etype; /* what type of event; 0=cron, 1=at */
1830Sstevel@tonic-gate char *cmd; /* command for cron, job name for at */
1840Sstevel@tonic-gate struct usr *u; /* ptr to the owner (usr) of this event */
1850Sstevel@tonic-gate struct event *link; /* ptr to another event for this user */
1860Sstevel@tonic-gate union {
1870Sstevel@tonic-gate struct { /* for crontab events */
1880Sstevel@tonic-gate char *minute; /* (these */
1890Sstevel@tonic-gate char *hour; /* fields */
1900Sstevel@tonic-gate char *daymon; /* are */
1910Sstevel@tonic-gate char *month; /* from */
1920Sstevel@tonic-gate char *dayweek; /* crontab) */
1930Sstevel@tonic-gate char *input; /* ptr to stdin */
1948439SChris.Gerhard@sun.com struct shared *tz; /* timezone of this event */
1958439SChris.Gerhard@sun.com struct shared *home; /* directory for this event */
1968439SChris.Gerhard@sun.com struct shared *shell; /* shell for this event */
1970Sstevel@tonic-gate } ct;
1980Sstevel@tonic-gate struct { /* for at events */
1990Sstevel@tonic-gate short exists; /* for revising at events */
2000Sstevel@tonic-gate int eventid; /* for el_remove-ing at events */
2010Sstevel@tonic-gate } at;
2020Sstevel@tonic-gate } of;
2030Sstevel@tonic-gate };
2040Sstevel@tonic-gate
2050Sstevel@tonic-gate struct usr {
2060Sstevel@tonic-gate char *name; /* name of user (e.g. "root") */
2070Sstevel@tonic-gate char *home; /* home directory for user */
2080Sstevel@tonic-gate uid_t uid; /* user id */
2090Sstevel@tonic-gate gid_t gid; /* group id */
2100Sstevel@tonic-gate int aruncnt; /* counter for running jobs per uid */
2110Sstevel@tonic-gate int cruncnt; /* counter for running cron jobs per uid */
2120Sstevel@tonic-gate int ctid; /* for el_remove-ing crontab events */
2130Sstevel@tonic-gate short ctexists; /* for revising crontab events */
2140Sstevel@tonic-gate struct event *ctevents; /* list of this usr's crontab events */
2150Sstevel@tonic-gate struct event *atevents; /* list of this usr's at events */
2160Sstevel@tonic-gate struct usr *nextusr;
2170Sstevel@tonic-gate }; /* ptr to next user */
2180Sstevel@tonic-gate
2190Sstevel@tonic-gate static struct queue
2200Sstevel@tonic-gate {
2210Sstevel@tonic-gate int njob; /* limit */
2220Sstevel@tonic-gate int nice; /* nice for execution */
2230Sstevel@tonic-gate int nwait; /* wait time to next execution attempt */
2240Sstevel@tonic-gate int nrun; /* number running */
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate qd = {100, 2, 60}, /* default values for queue defs */
2270Sstevel@tonic-gate qt[NQUEUE];
2280Sstevel@tonic-gate static struct queue qq;
2290Sstevel@tonic-gate
230801Sbasabi static struct runinfo
2310Sstevel@tonic-gate {
2320Sstevel@tonic-gate pid_t pid;
2330Sstevel@tonic-gate short que;
2340Sstevel@tonic-gate struct usr *rusr; /* pointer to usr struct */
2350Sstevel@tonic-gate char *outfile; /* file where stdout & stderr are trapped */
2360Sstevel@tonic-gate short jobtype; /* what type of event: 0=cron, 1=at */
2370Sstevel@tonic-gate char *jobname; /* command for "cron", jobname for "at" */
2380Sstevel@tonic-gate int mailwhendone; /* 1 = send mail even if no ouptut */
239801Sbasabi struct runinfo *next;
240801Sbasabi } *rthead;
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate static struct miscpid {
2430Sstevel@tonic-gate pid_t pid;
2440Sstevel@tonic-gate struct miscpid *next;
2450Sstevel@tonic-gate } *miscpid_head;
2460Sstevel@tonic-gate
2470Sstevel@tonic-gate static pid_t cron_pid; /* own pid */
2480Sstevel@tonic-gate static char didfork = 0; /* flag to see if I'm process group leader */
2490Sstevel@tonic-gate static int msgfd; /* file descriptor for fifo queue */
2500Sstevel@tonic-gate static int ecid = 1; /* event class id for el_remove(); MUST be set to 1 */
2510Sstevel@tonic-gate static int delayed; /* is job being rescheduled or did it run first time */
2520Sstevel@tonic-gate static int cwd; /* current working directory */
2530Sstevel@tonic-gate static struct event *next_event; /* the next event to execute */
2540Sstevel@tonic-gate static struct usr *uhead; /* ptr to the list of users */
2550Sstevel@tonic-gate
2560Sstevel@tonic-gate /* Variables for error handling at reading crontabs. */
2570Sstevel@tonic-gate static char cte_intro[] = "Line(s) with errors:\n\n";
2580Sstevel@tonic-gate static char cte_trail1[] = "\nMax number of errors encountered.";
2590Sstevel@tonic-gate static char cte_trail2[] = " Evaluation of crontab aborted.\n";
2600Sstevel@tonic-gate static int cte_free = MAILBINITFREE; /* Free buffer space */
2610Sstevel@tonic-gate static char *cte_text = NULL; /* Text buffer pointer */
2620Sstevel@tonic-gate static char *cte_lp; /* Next free line in cte_text */
2630Sstevel@tonic-gate static int cte_nvalid; /* Valid lines found */
2640Sstevel@tonic-gate
2650Sstevel@tonic-gate /* user's default environment for the shell */
2660Sstevel@tonic-gate #define ROOTPATH "PATH=/usr/sbin:/usr/bin"
2670Sstevel@tonic-gate #define NONROOTPATH "PATH=/usr/bin:"
2680Sstevel@tonic-gate
2690Sstevel@tonic-gate static char *Def_supath = NULL;
2700Sstevel@tonic-gate static char *Def_path = NULL;
2710Sstevel@tonic-gate static char path[LINE_MAX] = "PATH=";
2720Sstevel@tonic-gate static char supath[LINE_MAX] = "PATH=";
2738439SChris.Gerhard@sun.com static char homedir[LINE_MAX] = ENV_HOME;
2740Sstevel@tonic-gate static char logname[LINE_MAX] = "LOGNAME=";
2758439SChris.Gerhard@sun.com static char tzone[LINE_MAX] = ENV_TZ;
2760Sstevel@tonic-gate static char *envinit[] = {
2770Sstevel@tonic-gate homedir,
2780Sstevel@tonic-gate logname,
2790Sstevel@tonic-gate ROOTPATH,
2800Sstevel@tonic-gate "SHELL=/usr/bin/sh",
2810Sstevel@tonic-gate tzone,
2820Sstevel@tonic-gate NULL
2830Sstevel@tonic-gate };
2840Sstevel@tonic-gate
2850Sstevel@tonic-gate extern char **environ;
2860Sstevel@tonic-gate
2870Sstevel@tonic-gate #define DEFTZ "GMT"
2880Sstevel@tonic-gate static int log = 0;
2890Sstevel@tonic-gate static char hzname[10];
2900Sstevel@tonic-gate
2910Sstevel@tonic-gate static void cronend(int);
2920Sstevel@tonic-gate static void thaw_handler(int);
2930Sstevel@tonic-gate static void child_handler(int);
2940Sstevel@tonic-gate static void child_sigreset(void);
2950Sstevel@tonic-gate
2960Sstevel@tonic-gate static void mod_ctab(char *, time_t);
2970Sstevel@tonic-gate static void mod_atjob(char *, time_t);
2980Sstevel@tonic-gate static void add_atevent(struct usr *, char *, time_t, int);
2990Sstevel@tonic-gate static void rm_ctevents(struct usr *);
3000Sstevel@tonic-gate static void cleanup(struct runinfo *rn, int r);
3010Sstevel@tonic-gate static void crabort(char *, int);
3020Sstevel@tonic-gate static void msg(char *fmt, ...);
3037879SSerge.Dussud@Sun.COM static void ignore_msg(char *, char *, struct event *);
3040Sstevel@tonic-gate static void logit(int, struct runinfo *, int);
3050Sstevel@tonic-gate static void parsqdef(char *);
3060Sstevel@tonic-gate static void defaults();
3070Sstevel@tonic-gate static void initialize(int);
3080Sstevel@tonic-gate static void quedefs(int);
3090Sstevel@tonic-gate static int idle(long);
3100Sstevel@tonic-gate static struct usr *find_usr(char *);
3110Sstevel@tonic-gate static int ex(struct event *e);
3125558Sbasabi static void read_dirs(int);
3130Sstevel@tonic-gate static void mail(char *, char *, int);
3140Sstevel@tonic-gate static char *next_field(int, int);
3150Sstevel@tonic-gate static void readcron(struct usr *, time_t);
3160Sstevel@tonic-gate static int next_ge(int, char *);
3170Sstevel@tonic-gate static void free_if_unused(struct usr *);
3180Sstevel@tonic-gate static void del_atjob(char *, char *);
3190Sstevel@tonic-gate static void del_ctab(char *);
3200Sstevel@tonic-gate static void resched(int);
3210Sstevel@tonic-gate static int msg_wait(long);
3220Sstevel@tonic-gate static struct runinfo *rinfo_get(pid_t);
3230Sstevel@tonic-gate static void rinfo_free(struct runinfo *rp);
3240Sstevel@tonic-gate static void mail_result(struct usr *p, struct runinfo *pr, size_t filesize);
3250Sstevel@tonic-gate static time_t next_time(struct event *, time_t);
3260Sstevel@tonic-gate static time_t get_switching_time(int, time_t);
3270Sstevel@tonic-gate static time_t xmktime(struct tm *);
3280Sstevel@tonic-gate static void process_msg(struct message *, time_t);
3290Sstevel@tonic-gate static void reap_child(void);
3300Sstevel@tonic-gate static void miscpid_insert(pid_t);
3310Sstevel@tonic-gate static int miscpid_delete(pid_t);
332523Sbasabi static void contract_set_template(void);
333523Sbasabi static void contract_clear_template(void);
3340Sstevel@tonic-gate static void contract_abandon_latest(pid_t);
3350Sstevel@tonic-gate
3360Sstevel@tonic-gate static void cte_init(void);
3370Sstevel@tonic-gate static void cte_add(int, char *);
3380Sstevel@tonic-gate static void cte_valid(void);
3390Sstevel@tonic-gate static int cte_istoomany(void);
3400Sstevel@tonic-gate static void cte_sendmail(char *);
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate static int set_user_cred(const struct usr *, struct project *);
3430Sstevel@tonic-gate
3448439SChris.Gerhard@sun.com static struct shared *create_shared_str(char *str);
3458439SChris.Gerhard@sun.com static struct shared *dup_shared(struct shared *obj);
3468439SChris.Gerhard@sun.com static void rel_shared(struct shared *obj);
3478439SChris.Gerhard@sun.com static void *get_obj(struct shared *obj);
3480Sstevel@tonic-gate /*
3490Sstevel@tonic-gate * last_time is set immediately prior to exection of an event (via ex())
3500Sstevel@tonic-gate * to indicate the last time an event was executed. This was (surely)
3510Sstevel@tonic-gate * it's original intended use.
3520Sstevel@tonic-gate */
3530Sstevel@tonic-gate static time_t last_time, init_time, t_old;
3547879SSerge.Dussud@Sun.COM static int reset_needed; /* set to 1 when cron(1M) needs to re-initialize */
3550Sstevel@tonic-gate
356*11115SNobutomo.Nakano@Sun.COM static int refresh;
357*11115SNobutomo.Nakano@Sun.COM static sigset_t defmask, sigmask;
3580Sstevel@tonic-gate
3590Sstevel@tonic-gate /*
3600Sstevel@tonic-gate * BSM hooks
3610Sstevel@tonic-gate */
3620Sstevel@tonic-gate extern int audit_cron_session(char *, char *, uid_t, gid_t, char *);
3630Sstevel@tonic-gate extern void audit_cron_new_job(char *, int, void *);
3640Sstevel@tonic-gate extern void audit_cron_bad_user(char *);
3650Sstevel@tonic-gate extern void audit_cron_user_acct_expired(char *);
3660Sstevel@tonic-gate extern int audit_cron_create_anc_file(char *, char *, char *, uid_t);
3670Sstevel@tonic-gate extern int audit_cron_delete_anc_file(char *, char *);
3680Sstevel@tonic-gate extern int audit_cron_is_anc_name(char *);
3690Sstevel@tonic-gate extern int audit_cron_mode();
3700Sstevel@tonic-gate
3710Sstevel@tonic-gate static int cron_conv(int, struct pam_message **,
3720Sstevel@tonic-gate struct pam_response **, void *);
3730Sstevel@tonic-gate
3740Sstevel@tonic-gate static struct pam_conv pam_conv = {cron_conv, NULL};
3750Sstevel@tonic-gate static pam_handle_t *pamh; /* Authentication handle */
3760Sstevel@tonic-gate
3770Sstevel@tonic-gate /*
3780Sstevel@tonic-gate * Function to help check a user's credentials.
3790Sstevel@tonic-gate */
3800Sstevel@tonic-gate
3815558Sbasabi static int verify_user_cred(struct usr *u);
3820Sstevel@tonic-gate
3830Sstevel@tonic-gate /*
3840Sstevel@tonic-gate * Values returned by verify_user_cred and set_user_cred:
3850Sstevel@tonic-gate */
3860Sstevel@tonic-gate
3870Sstevel@tonic-gate #define VUC_OK 0
3880Sstevel@tonic-gate #define VUC_BADUSER 1
3890Sstevel@tonic-gate #define VUC_NOTINGROUP 2
3900Sstevel@tonic-gate #define VUC_EXPIRED 3
3910Sstevel@tonic-gate #define VUC_NEW_AUTH 4
3920Sstevel@tonic-gate
3930Sstevel@tonic-gate /*
3940Sstevel@tonic-gate * Modes of process_anc_files function
3950Sstevel@tonic-gate */
3960Sstevel@tonic-gate #define CRON_ANC_DELETE 1
3970Sstevel@tonic-gate #define CRON_ANC_CREATE 0
3980Sstevel@tonic-gate
3990Sstevel@tonic-gate /*
4000Sstevel@tonic-gate * Functions to remove a user or job completely from the running database.
4010Sstevel@tonic-gate */
4020Sstevel@tonic-gate static void clean_out_atjobs(struct usr *u);
4030Sstevel@tonic-gate static void clean_out_ctab(struct usr *u);
4040Sstevel@tonic-gate static void clean_out_user(struct usr *u);
4050Sstevel@tonic-gate static void cron_unlink(char *name);
4060Sstevel@tonic-gate static void process_anc_files(int);
4070Sstevel@tonic-gate
4080Sstevel@tonic-gate /*
4090Sstevel@tonic-gate * functions in elm.c
4100Sstevel@tonic-gate */
4110Sstevel@tonic-gate extern void el_init(int, time_t, time_t, int);
4127879SSerge.Dussud@Sun.COM extern int el_add(void *, time_t, int);
4130Sstevel@tonic-gate extern void el_remove(int, int);
4140Sstevel@tonic-gate extern int el_empty(void);
4150Sstevel@tonic-gate extern void *el_first(void);
4160Sstevel@tonic-gate extern void el_delete(void);
4170Sstevel@tonic-gate
4185558Sbasabi static int valid_entry(char *, int);
4195558Sbasabi static struct usr *create_ulist(char *, int);
4205558Sbasabi static void init_cronevent(char *, int);
4215558Sbasabi static void init_atevent(char *, time_t, int, int);
4225558Sbasabi static void update_atevent(struct usr *, char *, time_t, int);
4235558Sbasabi
4240Sstevel@tonic-gate int
main(int argc,char * argv[])4250Sstevel@tonic-gate main(int argc, char *argv[])
4260Sstevel@tonic-gate {
4270Sstevel@tonic-gate time_t t;
4280Sstevel@tonic-gate time_t ne_time; /* amt of time until next event execution */
4290Sstevel@tonic-gate time_t newtime, lastmtime = 0L;
4300Sstevel@tonic-gate struct usr *u;
4310Sstevel@tonic-gate struct event *e, *e2, *eprev;
4320Sstevel@tonic-gate struct stat buf;
4330Sstevel@tonic-gate pid_t rfork;
4340Sstevel@tonic-gate struct sigaction act;
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate /*
4377879SSerge.Dussud@Sun.COM * reset_needed is set to 1 whenever el_add() finds out that a cron
4387879SSerge.Dussud@Sun.COM * job is scheduled to be run before the time when cron(1M) daemon
4397879SSerge.Dussud@Sun.COM * initialized.
4407879SSerge.Dussud@Sun.COM * Other cases where a reset is needed is when ex() finds that the
4417879SSerge.Dussud@Sun.COM * event to be executed is being run at the wrong time, or when idle()
4427879SSerge.Dussud@Sun.COM * determines that time was reset.
4430Sstevel@tonic-gate * We immediately return to the top of the while (TRUE) loop in
4447879SSerge.Dussud@Sun.COM * main() where the event list is cleared and rebuilt, and reset_needed
4450Sstevel@tonic-gate * is set back to 0.
4460Sstevel@tonic-gate */
4477879SSerge.Dussud@Sun.COM reset_needed = 0;
4480Sstevel@tonic-gate
4490Sstevel@tonic-gate /*
4500Sstevel@tonic-gate * Only the privileged user can run this command.
4510Sstevel@tonic-gate */
4520Sstevel@tonic-gate if (getuid() != 0)
4530Sstevel@tonic-gate crabort(NOTALLOWED, 0);
4540Sstevel@tonic-gate
4550Sstevel@tonic-gate begin:
4560Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
4570Sstevel@tonic-gate /* fork unless 'nofork' is specified */
4580Sstevel@tonic-gate if ((argc <= 1) || (strcmp(argv[1], "nofork"))) {
4590Sstevel@tonic-gate if (rfork = fork()) {
4600Sstevel@tonic-gate if (rfork == (pid_t)-1) {
4610Sstevel@tonic-gate (void) sleep(30);
4620Sstevel@tonic-gate goto begin;
4630Sstevel@tonic-gate }
4640Sstevel@tonic-gate return (0);
4650Sstevel@tonic-gate }
4660Sstevel@tonic-gate didfork++;
4670Sstevel@tonic-gate (void) setpgrp(); /* detach cron from console */
4680Sstevel@tonic-gate }
4690Sstevel@tonic-gate
4700Sstevel@tonic-gate (void) umask(022);
4710Sstevel@tonic-gate (void) signal(SIGHUP, SIG_IGN);
4720Sstevel@tonic-gate (void) signal(SIGINT, SIG_IGN);
4730Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_IGN);
4740Sstevel@tonic-gate (void) signal(SIGTERM, cronend);
4750Sstevel@tonic-gate
4760Sstevel@tonic-gate defaults();
4770Sstevel@tonic-gate initialize(1);
4780Sstevel@tonic-gate quedefs(DEFAULT); /* load default queue definitions */
4790Sstevel@tonic-gate cron_pid = getpid();
4800Sstevel@tonic-gate msg("*** cron started *** pid = %d", cron_pid);
481*11115SNobutomo.Nakano@Sun.COM
482*11115SNobutomo.Nakano@Sun.COM /* setup THAW handler */
483*11115SNobutomo.Nakano@Sun.COM act.sa_handler = thaw_handler;
484*11115SNobutomo.Nakano@Sun.COM act.sa_flags = 0;
485*11115SNobutomo.Nakano@Sun.COM (void) sigemptyset(&act.sa_mask);
486*11115SNobutomo.Nakano@Sun.COM (void) sigaction(SIGTHAW, &act, NULL);
487*11115SNobutomo.Nakano@Sun.COM
488*11115SNobutomo.Nakano@Sun.COM /* setup CHLD handler */
4890Sstevel@tonic-gate act.sa_handler = child_handler;
4900Sstevel@tonic-gate act.sa_flags = 0;
4910Sstevel@tonic-gate (void) sigemptyset(&act.sa_mask);
4920Sstevel@tonic-gate (void) sigaddset(&act.sa_mask, SIGCLD);
4930Sstevel@tonic-gate (void) sigaction(SIGCLD, &act, NULL);
494*11115SNobutomo.Nakano@Sun.COM
495*11115SNobutomo.Nakano@Sun.COM (void) sigemptyset(&defmask);
496*11115SNobutomo.Nakano@Sun.COM (void) sigemptyset(&sigmask);
497*11115SNobutomo.Nakano@Sun.COM (void) sigaddset(&sigmask, SIGCLD);
498*11115SNobutomo.Nakano@Sun.COM (void) sigaddset(&sigmask, SIGTHAW);
499*11115SNobutomo.Nakano@Sun.COM (void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
5000Sstevel@tonic-gate
5017879SSerge.Dussud@Sun.COM t_old = init_time;
5020Sstevel@tonic-gate last_time = t_old;
5030Sstevel@tonic-gate for (;;) { /* MAIN LOOP */
5040Sstevel@tonic-gate t = time(NULL);
5057879SSerge.Dussud@Sun.COM if ((t_old > t) || (t-last_time > CUSHION) || reset_needed) {
5067879SSerge.Dussud@Sun.COM reset_needed = 0;
507*11115SNobutomo.Nakano@Sun.COM /*
508*11115SNobutomo.Nakano@Sun.COM * the time was set backwards or forward or
509*11115SNobutomo.Nakano@Sun.COM * refresh is requested.
510*11115SNobutomo.Nakano@Sun.COM */
511*11115SNobutomo.Nakano@Sun.COM if (refresh)
512*11115SNobutomo.Nakano@Sun.COM msg("re-scheduling jobs");
513*11115SNobutomo.Nakano@Sun.COM else
514*11115SNobutomo.Nakano@Sun.COM msg("time was reset, re-initializing");
5150Sstevel@tonic-gate el_delete();
5160Sstevel@tonic-gate u = uhead;
5170Sstevel@tonic-gate while (u != NULL) {
5180Sstevel@tonic-gate rm_ctevents(u);
5190Sstevel@tonic-gate e = u->atevents;
5200Sstevel@tonic-gate while (e != NULL) {
5210Sstevel@tonic-gate free(e->cmd);
5220Sstevel@tonic-gate e2 = e->link;
5230Sstevel@tonic-gate free(e);
5240Sstevel@tonic-gate e = e2;
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate u->atevents = NULL;
5270Sstevel@tonic-gate u = u->nextusr;
5280Sstevel@tonic-gate }
5290Sstevel@tonic-gate (void) close(msgfd);
5300Sstevel@tonic-gate initialize(0);
5310Sstevel@tonic-gate t = time(NULL);
5320Sstevel@tonic-gate last_time = t;
5337879SSerge.Dussud@Sun.COM /*
5347879SSerge.Dussud@Sun.COM * reset_needed might have been set in the functions
5357879SSerge.Dussud@Sun.COM * call path from initialize()
5367879SSerge.Dussud@Sun.COM */
5377879SSerge.Dussud@Sun.COM if (reset_needed) {
5387879SSerge.Dussud@Sun.COM continue;
5397879SSerge.Dussud@Sun.COM }
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate t_old = t;
5420Sstevel@tonic-gate
5430Sstevel@tonic-gate if (next_event == NULL && !el_empty()) {
5440Sstevel@tonic-gate next_event = (struct event *)el_first();
5450Sstevel@tonic-gate }
5460Sstevel@tonic-gate if (next_event == NULL) {
5470Sstevel@tonic-gate ne_time = INFINITY;
5480Sstevel@tonic-gate } else {
5490Sstevel@tonic-gate ne_time = next_event->time - t;
5500Sstevel@tonic-gate #ifdef DEBUG
5510Sstevel@tonic-gate cftime(timebuf, "%C", &next_event->time);
5520Sstevel@tonic-gate (void) fprintf(stderr, "next_time=%ld %s\n",
5535558Sbasabi next_event->time, timebuf);
5540Sstevel@tonic-gate #endif
5550Sstevel@tonic-gate }
5560Sstevel@tonic-gate if (ne_time > 0) {
5577879SSerge.Dussud@Sun.COM /*
5587879SSerge.Dussud@Sun.COM * reset_needed may be set in the functions call path
5597879SSerge.Dussud@Sun.COM * from idle()
5607879SSerge.Dussud@Sun.COM */
5617879SSerge.Dussud@Sun.COM if (idle(ne_time) || reset_needed) {
5627879SSerge.Dussud@Sun.COM reset_needed = 1;
5630Sstevel@tonic-gate continue;
5647879SSerge.Dussud@Sun.COM }
5650Sstevel@tonic-gate }
5660Sstevel@tonic-gate
5670Sstevel@tonic-gate if (stat(QUEDEFS, &buf)) {
5680Sstevel@tonic-gate msg("cannot stat QUEDEFS file");
5690Sstevel@tonic-gate } else if (lastmtime != buf.st_mtime) {
5700Sstevel@tonic-gate quedefs(LOAD);
5710Sstevel@tonic-gate lastmtime = buf.st_mtime;
5720Sstevel@tonic-gate }
5730Sstevel@tonic-gate
5740Sstevel@tonic-gate last_time = next_event->time; /* save execution time */
5750Sstevel@tonic-gate
5767879SSerge.Dussud@Sun.COM /*
5777879SSerge.Dussud@Sun.COM * reset_needed may be set in the functions call path
5787879SSerge.Dussud@Sun.COM * from ex()
5797879SSerge.Dussud@Sun.COM */
5807879SSerge.Dussud@Sun.COM if (ex(next_event) || reset_needed) {
5817879SSerge.Dussud@Sun.COM reset_needed = 1;
5820Sstevel@tonic-gate continue;
5837879SSerge.Dussud@Sun.COM }
5840Sstevel@tonic-gate
5850Sstevel@tonic-gate switch (next_event->etype) {
5860Sstevel@tonic-gate case CRONEVENT:
5870Sstevel@tonic-gate /* add cronevent back into the main event list */
5880Sstevel@tonic-gate if (delayed) {
5890Sstevel@tonic-gate delayed = 0;
5900Sstevel@tonic-gate break;
5910Sstevel@tonic-gate }
5920Sstevel@tonic-gate
5930Sstevel@tonic-gate /*
5940Sstevel@tonic-gate * check if time(0)< last_time. if so, then the
5950Sstevel@tonic-gate * system clock has gone backwards. to prevent this
5960Sstevel@tonic-gate * job from being started twice, we reschedule this
5970Sstevel@tonic-gate * job for the >>next time after last_time<<, and
5980Sstevel@tonic-gate * then set next_event->time to this. note that
5990Sstevel@tonic-gate * crontab's resolution is 1 minute.
6000Sstevel@tonic-gate */
6010Sstevel@tonic-gate
6020Sstevel@tonic-gate if (last_time > time(NULL)) {
6030Sstevel@tonic-gate msg(CLOCK_DRIFT);
6040Sstevel@tonic-gate /*
6050Sstevel@tonic-gate * bump up to next 30 second
6060Sstevel@tonic-gate * increment
6070Sstevel@tonic-gate * 1 <= newtime <= 30
6080Sstevel@tonic-gate */
6090Sstevel@tonic-gate newtime = 30 - (last_time % 30);
6100Sstevel@tonic-gate newtime += last_time;
6110Sstevel@tonic-gate
6120Sstevel@tonic-gate /*
6130Sstevel@tonic-gate * get the next scheduled event,
6140Sstevel@tonic-gate * not the one that we just
6150Sstevel@tonic-gate * kicked off!
6160Sstevel@tonic-gate */
6170Sstevel@tonic-gate next_event->time =
6185558Sbasabi next_time(next_event, newtime);
6190Sstevel@tonic-gate t_old = time(NULL);
6200Sstevel@tonic-gate } else {
6210Sstevel@tonic-gate next_event->time =
6225558Sbasabi next_time(next_event, (time_t)0);
6230Sstevel@tonic-gate }
6240Sstevel@tonic-gate #ifdef DEBUG
6250Sstevel@tonic-gate cftime(timebuf, "%C", &next_event->time);
6260Sstevel@tonic-gate (void) fprintf(stderr,
6275558Sbasabi "pushing back cron event %s at %ld (%s)\n",
6285558Sbasabi next_event->cmd, next_event->time, timebuf);
6290Sstevel@tonic-gate #endif
6300Sstevel@tonic-gate
6317879SSerge.Dussud@Sun.COM switch (el_add(next_event, next_event->time,
6327879SSerge.Dussud@Sun.COM (next_event->u)->ctid)) {
6337879SSerge.Dussud@Sun.COM case -1:
6347879SSerge.Dussud@Sun.COM ignore_msg("main", "cron", next_event);
6357879SSerge.Dussud@Sun.COM break;
6367879SSerge.Dussud@Sun.COM case -2: /* event time lower than init time */
6377879SSerge.Dussud@Sun.COM reset_needed = 1;
6387879SSerge.Dussud@Sun.COM break;
6397879SSerge.Dussud@Sun.COM }
6400Sstevel@tonic-gate break;
6410Sstevel@tonic-gate default:
6420Sstevel@tonic-gate /* remove at or batch job from system */
6430Sstevel@tonic-gate if (delayed) {
6440Sstevel@tonic-gate delayed = 0;
6450Sstevel@tonic-gate break;
6460Sstevel@tonic-gate }
6470Sstevel@tonic-gate eprev = NULL;
6480Sstevel@tonic-gate e = (next_event->u)->atevents;
6490Sstevel@tonic-gate while (e != NULL) {
6500Sstevel@tonic-gate if (e == next_event) {
6510Sstevel@tonic-gate if (eprev == NULL)
6520Sstevel@tonic-gate (e->u)->atevents = e->link;
6530Sstevel@tonic-gate else
6540Sstevel@tonic-gate eprev->link = e->link;
6550Sstevel@tonic-gate free(e->cmd);
6560Sstevel@tonic-gate free(e);
6570Sstevel@tonic-gate break;
6580Sstevel@tonic-gate } else {
6590Sstevel@tonic-gate eprev = e;
6600Sstevel@tonic-gate e = e->link;
6610Sstevel@tonic-gate }
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate break;
6640Sstevel@tonic-gate }
6650Sstevel@tonic-gate next_event = NULL;
6660Sstevel@tonic-gate }
6670Sstevel@tonic-gate
6680Sstevel@tonic-gate /*NOTREACHED*/
6690Sstevel@tonic-gate }
6700Sstevel@tonic-gate
6710Sstevel@tonic-gate static void
initialize(int firstpass)6720Sstevel@tonic-gate initialize(int firstpass)
6730Sstevel@tonic-gate {
6740Sstevel@tonic-gate #ifdef DEBUG
6750Sstevel@tonic-gate (void) fprintf(stderr, "in initialize\n");
6760Sstevel@tonic-gate #endif
6770Sstevel@tonic-gate if (firstpass) {
6780Sstevel@tonic-gate /* for mail(1), make sure messages come from root */
6790Sstevel@tonic-gate if (putenv("LOGNAME=root") != 0) {
6800Sstevel@tonic-gate crabort("cannot expand env variable",
6815558Sbasabi REMOVE_FIFO|CONSOLE_MSG);
6820Sstevel@tonic-gate }
6830Sstevel@tonic-gate if (access(FIFO, R_OK) == -1) {
6840Sstevel@tonic-gate if (errno == ENOENT) {
6850Sstevel@tonic-gate if (mknod(FIFO, S_IFIFO|0600, 0) != 0)
6860Sstevel@tonic-gate crabort("cannot create fifo queue",
6875558Sbasabi REMOVE_FIFO|CONSOLE_MSG);
6880Sstevel@tonic-gate } else {
6890Sstevel@tonic-gate if (NOFORK) {
6900Sstevel@tonic-gate /* didn't fork... init(1M) is waiting */
6910Sstevel@tonic-gate (void) sleep(60);
6920Sstevel@tonic-gate }
6930Sstevel@tonic-gate perror("FIFO");
6940Sstevel@tonic-gate crabort("cannot access fifo queue",
6955558Sbasabi REMOVE_FIFO|CONSOLE_MSG);
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate } else {
6980Sstevel@tonic-gate if (NOFORK) {
6990Sstevel@tonic-gate /* didn't fork... init(1M) is waiting */
7000Sstevel@tonic-gate (void) sleep(60);
7010Sstevel@tonic-gate /*
7020Sstevel@tonic-gate * the wait is painful, but we don't want
7030Sstevel@tonic-gate * init respawning this quickly
7040Sstevel@tonic-gate */
7050Sstevel@tonic-gate }
7060Sstevel@tonic-gate crabort("cannot start cron; FIFO exists", CONSOLE_MSG);
7070Sstevel@tonic-gate }
7080Sstevel@tonic-gate }
7090Sstevel@tonic-gate
7100Sstevel@tonic-gate if ((msgfd = open(FIFO, O_RDWR)) < 0) {
7110Sstevel@tonic-gate perror("! open");
7120Sstevel@tonic-gate crabort("cannot open fifo queue", REMOVE_FIFO|CONSOLE_MSG);
7130Sstevel@tonic-gate }
7140Sstevel@tonic-gate
7157879SSerge.Dussud@Sun.COM init_time = time(NULL);
7167879SSerge.Dussud@Sun.COM el_init(8, init_time, (time_t)(60*60*24), 10);
7177879SSerge.Dussud@Sun.COM
7188439SChris.Gerhard@sun.com init_time = time(NULL);
7198439SChris.Gerhard@sun.com el_init(8, init_time, (time_t)(60*60*24), 10);
7208439SChris.Gerhard@sun.com
7210Sstevel@tonic-gate /*
7220Sstevel@tonic-gate * read directories, create users list, and add events to the
7230Sstevel@tonic-gate * main event list. Only zero user list on firstpass.
7240Sstevel@tonic-gate */
7250Sstevel@tonic-gate if (firstpass)
7260Sstevel@tonic-gate uhead = NULL;
7275558Sbasabi read_dirs(firstpass);
7280Sstevel@tonic-gate next_event = NULL;
7290Sstevel@tonic-gate
7300Sstevel@tonic-gate if (!firstpass)
7310Sstevel@tonic-gate return;
7320Sstevel@tonic-gate
7330Sstevel@tonic-gate /* stdout is log file */
7340Sstevel@tonic-gate if (freopen(ACCTFILE, "a", stdout) == NULL)
7350Sstevel@tonic-gate (void) fprintf(stderr, "cannot open %s\n", ACCTFILE);
7360Sstevel@tonic-gate
7370Sstevel@tonic-gate /* log should be root-only */
7380Sstevel@tonic-gate (void) fchmod(1, S_IRUSR|S_IWUSR);
7390Sstevel@tonic-gate
7400Sstevel@tonic-gate /* stderr also goes to ACCTFILE */
7410Sstevel@tonic-gate (void) close(fileno(stderr));
7420Sstevel@tonic-gate (void) dup(1);
7430Sstevel@tonic-gate /* null for stdin */
7440Sstevel@tonic-gate (void) freopen("/dev/null", "r", stdin);
7450Sstevel@tonic-gate
7460Sstevel@tonic-gate contract_set_template();
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate
7490Sstevel@tonic-gate static void
read_dirs(int first)7505558Sbasabi read_dirs(int first)
7510Sstevel@tonic-gate {
7525558Sbasabi DIR *dir;
7535558Sbasabi struct dirent *dp;
7545558Sbasabi char *ptr;
7555558Sbasabi int jobtype;
7565558Sbasabi time_t tim;
7575558Sbasabi
7580Sstevel@tonic-gate
7590Sstevel@tonic-gate if (chdir(CRONDIR) == -1)
7600Sstevel@tonic-gate crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG);
7610Sstevel@tonic-gate cwd = CRON;
7620Sstevel@tonic-gate if ((dir = opendir(".")) == NULL)
7630Sstevel@tonic-gate crabort(NOREADDIR, REMOVE_FIFO|CONSOLE_MSG);
7645558Sbasabi while ((dp = readdir(dir)) != NULL) {
7655558Sbasabi if (!valid_entry(dp->d_name, CRONEVENT))
7665558Sbasabi continue;
7675558Sbasabi init_cronevent(dp->d_name, first);
7685558Sbasabi }
7690Sstevel@tonic-gate (void) closedir(dir);
7705558Sbasabi
7710Sstevel@tonic-gate if (chdir(ATDIR) == -1) {
7720Sstevel@tonic-gate msg("cannot chdir to at directory");
7730Sstevel@tonic-gate return;
7740Sstevel@tonic-gate }
7750Sstevel@tonic-gate if ((dir = opendir(".")) == NULL) {
7760Sstevel@tonic-gate msg("cannot read at at directory");
7770Sstevel@tonic-gate return;
7780Sstevel@tonic-gate }
7795558Sbasabi cwd = AT;
7805558Sbasabi while ((dp = readdir(dir)) != NULL) {
7815558Sbasabi if (!valid_entry(dp->d_name, ATEVENT))
7825558Sbasabi continue;
7835558Sbasabi ptr = dp->d_name;
7845558Sbasabi if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
7855558Sbasabi continue;
7865558Sbasabi ptr++;
7875558Sbasabi if (!isalpha(*ptr))
7885558Sbasabi continue;
7895558Sbasabi jobtype = *ptr - 'a';
7905558Sbasabi if (jobtype >= NQUEUE) {
7915558Sbasabi cron_unlink(dp->d_name);
7925558Sbasabi continue;
7935558Sbasabi }
7945558Sbasabi init_atevent(dp->d_name, tim, jobtype, first);
7955558Sbasabi }
7960Sstevel@tonic-gate (void) closedir(dir);
7970Sstevel@tonic-gate }
7980Sstevel@tonic-gate
7995558Sbasabi static int
valid_entry(char * name,int type)8005558Sbasabi valid_entry(char *name, int type)
8015558Sbasabi {
8025558Sbasabi struct stat buf;
8035558Sbasabi
8045558Sbasabi if (strcmp(name, ".") == 0 ||
8055558Sbasabi strcmp(name, "..") == 0)
8065558Sbasabi return (0);
8075558Sbasabi
8085558Sbasabi /* skip over ancillary file names */
8095558Sbasabi if (audit_cron_is_anc_name(name))
8105558Sbasabi return (0);
8115558Sbasabi
8125558Sbasabi if (stat(name, &buf)) {
8135558Sbasabi mail(name, BADSTAT, ERR_UNIXERR);
8145558Sbasabi cron_unlink(name);
8155558Sbasabi return (0);
8165558Sbasabi }
8175558Sbasabi if (!S_ISREG(buf.st_mode)) {
8185558Sbasabi mail(name, BADTYPE, ERR_NOTREG);
8195558Sbasabi cron_unlink(name);
8205558Sbasabi return (0);
8215558Sbasabi }
8225558Sbasabi if (type == ATEVENT) {
8235558Sbasabi if (!(buf.st_mode & ISUID)) {
8245558Sbasabi cron_unlink(name);
8255558Sbasabi return (0);
8265558Sbasabi }
8275558Sbasabi }
8285558Sbasabi return (1);
8295558Sbasabi }
8305558Sbasabi
8315558Sbasabi struct usr *
create_ulist(char * name,int type)8325558Sbasabi create_ulist(char *name, int type)
8330Sstevel@tonic-gate {
8345558Sbasabi struct usr *u;
8355558Sbasabi
836*11115SNobutomo.Nakano@Sun.COM u = xcalloc(1, sizeof (struct usr));
837*11115SNobutomo.Nakano@Sun.COM u->name = xstrdup(name);
8385558Sbasabi if (type == CRONEVENT) {
8395558Sbasabi u->ctexists = TRUE;
8405558Sbasabi u->ctid = ecid++;
8415558Sbasabi } else {
8425558Sbasabi u->ctexists = FALSE;
8435558Sbasabi u->ctid = 0;
8445558Sbasabi }
845*11115SNobutomo.Nakano@Sun.COM u->uid = (uid_t)-1;
846*11115SNobutomo.Nakano@Sun.COM u->gid = (uid_t)-1;
8475558Sbasabi u->nextusr = uhead;
8485558Sbasabi uhead = u;
8495558Sbasabi return (u);
8505558Sbasabi }
8515558Sbasabi
8525558Sbasabi void
init_cronevent(char * name,int first)8535558Sbasabi init_cronevent(char *name, int first)
8545558Sbasabi {
8555558Sbasabi struct usr *u;
8565558Sbasabi
8575558Sbasabi if (first) {
8585558Sbasabi u = create_ulist(name, CRONEVENT);
8595558Sbasabi readcron(u, 0);
8605558Sbasabi } else {
8615558Sbasabi if ((u = find_usr(name)) == NULL) {
8625558Sbasabi u = create_ulist(name, CRONEVENT);
8635558Sbasabi readcron(u, 0);
8645558Sbasabi } else {
8655558Sbasabi u->ctexists = TRUE;
8665558Sbasabi rm_ctevents(u);
8675558Sbasabi el_remove(u->ctid, 0);
8685558Sbasabi readcron(u, 0);
8690Sstevel@tonic-gate }
8705558Sbasabi }
8715558Sbasabi }
8725558Sbasabi
8735558Sbasabi void
init_atevent(char * name,time_t tim,int jobtype,int first)8745558Sbasabi init_atevent(char *name, time_t tim, int jobtype, int first)
8755558Sbasabi {
8765558Sbasabi struct usr *u;
8775558Sbasabi
8785558Sbasabi if (first) {
8795558Sbasabi u = create_ulist(name, ATEVENT);
8805558Sbasabi add_atevent(u, name, tim, jobtype);
8815558Sbasabi } else {
8825558Sbasabi if ((u = find_usr(name)) == NULL) {
8835558Sbasabi u = create_ulist(name, ATEVENT);
8845558Sbasabi add_atevent(u, name, tim, jobtype);
8855558Sbasabi } else {
8865558Sbasabi update_atevent(u, name, tim, jobtype);
8875558Sbasabi }
8880Sstevel@tonic-gate }
8890Sstevel@tonic-gate }
8900Sstevel@tonic-gate
8910Sstevel@tonic-gate static void
mod_ctab(char * name,time_t reftime)8920Sstevel@tonic-gate mod_ctab(char *name, time_t reftime)
8930Sstevel@tonic-gate {
8940Sstevel@tonic-gate struct passwd *pw;
8950Sstevel@tonic-gate struct stat buf;
8960Sstevel@tonic-gate struct usr *u;
8978439SChris.Gerhard@sun.com char namebuf[LINE_MAX];
8980Sstevel@tonic-gate char *pname;
8990Sstevel@tonic-gate
9000Sstevel@tonic-gate /* skip over ancillary file names */
9010Sstevel@tonic-gate if (audit_cron_is_anc_name(name))
9020Sstevel@tonic-gate return;
9030Sstevel@tonic-gate
9040Sstevel@tonic-gate if ((pw = getpwnam(name)) == NULL) {
9050Sstevel@tonic-gate msg("No such user as %s - cron entries not created", name);
9060Sstevel@tonic-gate return;
9070Sstevel@tonic-gate }
9080Sstevel@tonic-gate if (cwd != CRON) {
9090Sstevel@tonic-gate if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
9105558Sbasabi CRONDIR, name) >= sizeof (namebuf)) {
9110Sstevel@tonic-gate msg("Too long path name %s - cron entries not created",
9125558Sbasabi namebuf);
9130Sstevel@tonic-gate return;
9140Sstevel@tonic-gate }
9150Sstevel@tonic-gate pname = namebuf;
9160Sstevel@tonic-gate } else {
9170Sstevel@tonic-gate pname = name;
9180Sstevel@tonic-gate }
9190Sstevel@tonic-gate /*
9200Sstevel@tonic-gate * a warning message is given by the crontab command so there is
9210Sstevel@tonic-gate * no need to give one here...... use this code if you only want
9220Sstevel@tonic-gate * users with a login shell of /usr/bin/sh to use cron
9230Sstevel@tonic-gate */
9240Sstevel@tonic-gate #ifdef BOURNESHELLONLY
9250Sstevel@tonic-gate if ((strcmp(pw->pw_shell, "") != 0) &&
9260Sstevel@tonic-gate (strcmp(pw->pw_shell, SHELL) != 0)) {
9270Sstevel@tonic-gate mail(name, BADSHELL, ERR_CANTEXECCRON);
9280Sstevel@tonic-gate cron_unlink(pname);
9290Sstevel@tonic-gate return;
9300Sstevel@tonic-gate }
9310Sstevel@tonic-gate #endif
9320Sstevel@tonic-gate if (stat(pname, &buf)) {
9330Sstevel@tonic-gate mail(name, BADSTAT, ERR_UNIXERR);
9340Sstevel@tonic-gate cron_unlink(pname);
9350Sstevel@tonic-gate return;
9360Sstevel@tonic-gate }
9370Sstevel@tonic-gate if (!S_ISREG(buf.st_mode)) {
9380Sstevel@tonic-gate mail(name, BADTYPE, ERR_CRONTABENT);
9390Sstevel@tonic-gate return;
9400Sstevel@tonic-gate }
9410Sstevel@tonic-gate if ((u = find_usr(name)) == NULL) {
9420Sstevel@tonic-gate #ifdef DEBUG
9430Sstevel@tonic-gate (void) fprintf(stderr, "new user (%s) with a crontab\n", name);
9440Sstevel@tonic-gate #endif
9455558Sbasabi u = create_ulist(name, CRONEVENT);
9465558Sbasabi u->home = xmalloc(strlen(pw->pw_dir) + 1);
9470Sstevel@tonic-gate (void) strcpy(u->home, pw->pw_dir);
9480Sstevel@tonic-gate u->uid = pw->pw_uid;
9490Sstevel@tonic-gate u->gid = pw->pw_gid;
9500Sstevel@tonic-gate readcron(u, reftime);
9510Sstevel@tonic-gate } else {
9520Sstevel@tonic-gate u->uid = pw->pw_uid;
9530Sstevel@tonic-gate u->gid = pw->pw_gid;
9545581Sbasabi if (u->home != NULL) {
9555581Sbasabi if (strcmp(u->home, pw->pw_dir) != 0) {
9565581Sbasabi free(u->home);
9575581Sbasabi u->home = xmalloc(strlen(pw->pw_dir) + 1);
9585581Sbasabi (void) strcpy(u->home, pw->pw_dir);
9595581Sbasabi }
9605581Sbasabi } else {
9615558Sbasabi u->home = xmalloc(strlen(pw->pw_dir) + 1);
9620Sstevel@tonic-gate (void) strcpy(u->home, pw->pw_dir);
9630Sstevel@tonic-gate }
9640Sstevel@tonic-gate u->ctexists = TRUE;
9650Sstevel@tonic-gate if (u->ctid == 0) {
9660Sstevel@tonic-gate #ifdef DEBUG
9670Sstevel@tonic-gate (void) fprintf(stderr, "%s now has a crontab\n",
9680Sstevel@tonic-gate u->name);
9690Sstevel@tonic-gate #endif
9700Sstevel@tonic-gate /* user didnt have a crontab last time */
9710Sstevel@tonic-gate u->ctid = ecid++;
9720Sstevel@tonic-gate u->ctevents = NULL;
9730Sstevel@tonic-gate readcron(u, reftime);
9740Sstevel@tonic-gate return;
9750Sstevel@tonic-gate }
9760Sstevel@tonic-gate #ifdef DEBUG
9770Sstevel@tonic-gate (void) fprintf(stderr, "%s has revised his crontab\n", u->name);
9780Sstevel@tonic-gate #endif
9790Sstevel@tonic-gate rm_ctevents(u);
9800Sstevel@tonic-gate el_remove(u->ctid, 0);
9810Sstevel@tonic-gate readcron(u, reftime);
9820Sstevel@tonic-gate }
9830Sstevel@tonic-gate }
9840Sstevel@tonic-gate
9850Sstevel@tonic-gate /* ARGSUSED */
9860Sstevel@tonic-gate static void
mod_atjob(char * name,time_t reftime)9870Sstevel@tonic-gate mod_atjob(char *name, time_t reftime)
9880Sstevel@tonic-gate {
9890Sstevel@tonic-gate char *ptr;
9900Sstevel@tonic-gate time_t tim;
9910Sstevel@tonic-gate struct passwd *pw;
9920Sstevel@tonic-gate struct stat buf;
9930Sstevel@tonic-gate struct usr *u;
9940Sstevel@tonic-gate char namebuf[PATH_MAX];
9950Sstevel@tonic-gate char *pname;
9960Sstevel@tonic-gate int jobtype;
9970Sstevel@tonic-gate
9980Sstevel@tonic-gate ptr = name;
9990Sstevel@tonic-gate if (((tim = num(&ptr)) == 0) || (*ptr != '.'))
10000Sstevel@tonic-gate return;
10010Sstevel@tonic-gate ptr++;
10020Sstevel@tonic-gate if (!isalpha(*ptr))
10030Sstevel@tonic-gate return;
10040Sstevel@tonic-gate jobtype = *ptr - 'a';
10050Sstevel@tonic-gate
10060Sstevel@tonic-gate /* check for audit ancillary file */
10070Sstevel@tonic-gate if (audit_cron_is_anc_name(name))
10080Sstevel@tonic-gate return;
10090Sstevel@tonic-gate
10100Sstevel@tonic-gate if (cwd != AT) {
10110Sstevel@tonic-gate if (snprintf(namebuf, sizeof (namebuf), "%s/%s", ATDIR, name)
10120Sstevel@tonic-gate >= sizeof (namebuf)) {
10130Sstevel@tonic-gate return;
10140Sstevel@tonic-gate }
10150Sstevel@tonic-gate pname = namebuf;
10160Sstevel@tonic-gate } else {
10170Sstevel@tonic-gate pname = name;
10180Sstevel@tonic-gate }
10190Sstevel@tonic-gate if (stat(pname, &buf) || jobtype >= NQUEUE) {
10200Sstevel@tonic-gate cron_unlink(pname);
10210Sstevel@tonic-gate return;
10220Sstevel@tonic-gate }
10230Sstevel@tonic-gate if (!(buf.st_mode & ISUID) || !S_ISREG(buf.st_mode)) {
10240Sstevel@tonic-gate cron_unlink(pname);
10250Sstevel@tonic-gate return;
10260Sstevel@tonic-gate }
10270Sstevel@tonic-gate if ((pw = getpwuid(buf.st_uid)) == NULL) {
10280Sstevel@tonic-gate cron_unlink(pname);
10290Sstevel@tonic-gate return;
10300Sstevel@tonic-gate }
10310Sstevel@tonic-gate /*
10320Sstevel@tonic-gate * a warning message is given by the at command so there is no
10330Sstevel@tonic-gate * need to give one here......use this code if you only want
10340Sstevel@tonic-gate * users with a login shell of /usr/bin/sh to use cron
10350Sstevel@tonic-gate */
10360Sstevel@tonic-gate #ifdef BOURNESHELLONLY
10370Sstevel@tonic-gate if ((strcmp(pw->pw_shell, "") != 0) &&
10380Sstevel@tonic-gate (strcmp(pw->pw_shell, SHELL) != 0)) {
10390Sstevel@tonic-gate mail(pw->pw_name, BADSHELL, ERR_CANTEXECAT);
10400Sstevel@tonic-gate cron_unlink(pname);
10410Sstevel@tonic-gate return;
10420Sstevel@tonic-gate }
10430Sstevel@tonic-gate #endif
10440Sstevel@tonic-gate if ((u = find_usr(pw->pw_name)) == NULL) {
10450Sstevel@tonic-gate #ifdef DEBUG
10460Sstevel@tonic-gate (void) fprintf(stderr, "new user (%s) with an at job = %s\n",
10475558Sbasabi pw->pw_name, name);
10480Sstevel@tonic-gate #endif
10495558Sbasabi u = create_ulist(pw->pw_name, ATEVENT);
1050*11115SNobutomo.Nakano@Sun.COM u->home = xstrdup(pw->pw_dir);
10510Sstevel@tonic-gate u->uid = pw->pw_uid;
10520Sstevel@tonic-gate u->gid = pw->pw_gid;
10530Sstevel@tonic-gate add_atevent(u, name, tim, jobtype);
10540Sstevel@tonic-gate } else {
10550Sstevel@tonic-gate u->uid = pw->pw_uid;
10560Sstevel@tonic-gate u->gid = pw->pw_gid;
10575558Sbasabi free(u->home);
1058*11115SNobutomo.Nakano@Sun.COM u->home = xstrdup(pw->pw_dir);
10595558Sbasabi update_atevent(u, name, tim, jobtype);
10600Sstevel@tonic-gate }
10610Sstevel@tonic-gate }
10620Sstevel@tonic-gate
10630Sstevel@tonic-gate static void
add_atevent(struct usr * u,char * job,time_t tim,int jobtype)10640Sstevel@tonic-gate add_atevent(struct usr *u, char *job, time_t tim, int jobtype)
10650Sstevel@tonic-gate {
10660Sstevel@tonic-gate struct event *e;
10670Sstevel@tonic-gate
10680Sstevel@tonic-gate e = xmalloc(sizeof (struct event));
10690Sstevel@tonic-gate e->etype = jobtype;
10705558Sbasabi e->cmd = xmalloc(strlen(job) + 1);
10710Sstevel@tonic-gate (void) strcpy(e->cmd, job);
10720Sstevel@tonic-gate e->u = u;
10730Sstevel@tonic-gate e->link = u->atevents;
10740Sstevel@tonic-gate u->atevents = e;
10750Sstevel@tonic-gate e->of.at.exists = TRUE;
10760Sstevel@tonic-gate e->of.at.eventid = ecid++;
10770Sstevel@tonic-gate if (tim < init_time) /* old job */
10780Sstevel@tonic-gate e->time = init_time;
10790Sstevel@tonic-gate else
10800Sstevel@tonic-gate e->time = tim;
10810Sstevel@tonic-gate #ifdef DEBUG
10820Sstevel@tonic-gate (void) fprintf(stderr, "add_atevent: user=%s, job=%s, time=%ld\n",
10835558Sbasabi u->name, e->cmd, e->time);
10840Sstevel@tonic-gate #endif
10857879SSerge.Dussud@Sun.COM if (el_add(e, e->time, e->of.at.eventid) < 0) {
10867879SSerge.Dussud@Sun.COM ignore_msg("add_atevent", "at", e);
10877879SSerge.Dussud@Sun.COM }
10880Sstevel@tonic-gate }
10890Sstevel@tonic-gate
10905558Sbasabi void
update_atevent(struct usr * u,char * name,time_t tim,int jobtype)10915558Sbasabi update_atevent(struct usr *u, char *name, time_t tim, int jobtype)
10925558Sbasabi {
10935558Sbasabi struct event *e;
10945558Sbasabi
10955558Sbasabi e = u->atevents;
10965558Sbasabi while (e != NULL) {
10975558Sbasabi if (strcmp(e->cmd, name) == 0) {
10985558Sbasabi e->of.at.exists = TRUE;
10995558Sbasabi break;
11005558Sbasabi } else {
11015558Sbasabi e = e->link;
11025558Sbasabi }
11035558Sbasabi }
11045558Sbasabi if (e == NULL) {
11055558Sbasabi #ifdef DEBUG
11065558Sbasabi (void) fprintf(stderr, "%s has a new at job = %s\n",
11075558Sbasabi u->name, name);
11085558Sbasabi #endif
11095558Sbasabi add_atevent(u, name, tim, jobtype);
11105558Sbasabi }
11115558Sbasabi }
11120Sstevel@tonic-gate
11130Sstevel@tonic-gate static char line[CTLINESIZE]; /* holds a line from a crontab file */
11140Sstevel@tonic-gate static int cursor; /* cursor for the above line */
11150Sstevel@tonic-gate
11160Sstevel@tonic-gate static void
readcron(struct usr * u,time_t reftime)11170Sstevel@tonic-gate readcron(struct usr *u, time_t reftime)
11180Sstevel@tonic-gate {
11190Sstevel@tonic-gate /*
11200Sstevel@tonic-gate * readcron reads in a crontab file for a user (u). The list of
11210Sstevel@tonic-gate * events for user u is built, and u->events is made to point to
11220Sstevel@tonic-gate * this list. Each event is also entered into the main event
11230Sstevel@tonic-gate * list.
11240Sstevel@tonic-gate */
11250Sstevel@tonic-gate FILE *cf; /* cf will be a user's crontab file */
11260Sstevel@tonic-gate struct event *e;
11270Sstevel@tonic-gate int start;
11280Sstevel@tonic-gate unsigned int i;
11290Sstevel@tonic-gate char namebuf[PATH_MAX];
11300Sstevel@tonic-gate char *pname;
11318439SChris.Gerhard@sun.com struct shared *tz = NULL;
11328439SChris.Gerhard@sun.com struct shared *home = NULL;
11338439SChris.Gerhard@sun.com struct shared *shell = NULL;
11340Sstevel@tonic-gate int lineno = 0;
11350Sstevel@tonic-gate
11360Sstevel@tonic-gate /* read the crontab file */
11370Sstevel@tonic-gate cte_init(); /* Init error handling */
11380Sstevel@tonic-gate if (cwd != CRON) {
11390Sstevel@tonic-gate if (snprintf(namebuf, sizeof (namebuf), "%s/%s",
11400Sstevel@tonic-gate CRONDIR, u->name) >= sizeof (namebuf)) {
11410Sstevel@tonic-gate return;
11420Sstevel@tonic-gate }
11430Sstevel@tonic-gate pname = namebuf;
11440Sstevel@tonic-gate } else {
11450Sstevel@tonic-gate pname = u->name;
11460Sstevel@tonic-gate }
11470Sstevel@tonic-gate if ((cf = fopen(pname, "r")) == NULL) {
11480Sstevel@tonic-gate mail(u->name, NOREAD, ERR_UNIXERR);
11490Sstevel@tonic-gate return;
11500Sstevel@tonic-gate }
11510Sstevel@tonic-gate while (fgets(line, CTLINESIZE, cf) != NULL) {
11528439SChris.Gerhard@sun.com char *tmp;
11530Sstevel@tonic-gate /* process a line of a crontab file */
11540Sstevel@tonic-gate lineno++;
11550Sstevel@tonic-gate if (cte_istoomany())
11560Sstevel@tonic-gate break;
11570Sstevel@tonic-gate cursor = 0;
11580Sstevel@tonic-gate while (line[cursor] == ' ' || line[cursor] == '\t')
11590Sstevel@tonic-gate cursor++;
11600Sstevel@tonic-gate if (line[cursor] == '#' || line[cursor] == '\n')
11610Sstevel@tonic-gate continue;
11628439SChris.Gerhard@sun.com
11638439SChris.Gerhard@sun.com if (strncmp(&line[cursor], ENV_TZ,
11648439SChris.Gerhard@sun.com strlen(ENV_TZ)) == 0) {
11658439SChris.Gerhard@sun.com if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
11668439SChris.Gerhard@sun.com *tmp = NULL;
11678439SChris.Gerhard@sun.com }
11688439SChris.Gerhard@sun.com
11698439SChris.Gerhard@sun.com if (!isvalid_tz(&line[cursor + strlen(ENV_TZ)], NULL,
11708439SChris.Gerhard@sun.com _VTZ_ALL)) {
11718439SChris.Gerhard@sun.com cte_add(lineno, line);
11728439SChris.Gerhard@sun.com break;
11738439SChris.Gerhard@sun.com }
11748439SChris.Gerhard@sun.com if (tz == NULL || strcmp(&line[cursor], get_obj(tz))) {
11758439SChris.Gerhard@sun.com rel_shared(tz);
11768439SChris.Gerhard@sun.com tz = create_shared_str(&line[cursor]);
11778439SChris.Gerhard@sun.com }
11788439SChris.Gerhard@sun.com continue;
11798439SChris.Gerhard@sun.com }
11808439SChris.Gerhard@sun.com
11818439SChris.Gerhard@sun.com if (strncmp(&line[cursor], ENV_HOME,
11828439SChris.Gerhard@sun.com strlen(ENV_HOME)) == 0) {
11838439SChris.Gerhard@sun.com if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
11848439SChris.Gerhard@sun.com *tmp = NULL;
11858439SChris.Gerhard@sun.com }
11868439SChris.Gerhard@sun.com if (home == NULL ||
11878439SChris.Gerhard@sun.com strcmp(&line[cursor], get_obj(home))) {
11888439SChris.Gerhard@sun.com rel_shared(home);
11898439SChris.Gerhard@sun.com home = create_shared_str(
11908439SChris.Gerhard@sun.com &line[cursor + strlen(ENV_HOME)]);
11918439SChris.Gerhard@sun.com }
11928439SChris.Gerhard@sun.com continue;
11938439SChris.Gerhard@sun.com }
11948439SChris.Gerhard@sun.com
11958439SChris.Gerhard@sun.com if (strncmp(&line[cursor], ENV_SHELL,
11968439SChris.Gerhard@sun.com strlen(ENV_SHELL)) == 0) {
11978439SChris.Gerhard@sun.com if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
11988439SChris.Gerhard@sun.com *tmp = NULL;
11998439SChris.Gerhard@sun.com }
12008439SChris.Gerhard@sun.com if (shell == NULL ||
12018439SChris.Gerhard@sun.com strcmp(&line[cursor], get_obj(shell))) {
12028439SChris.Gerhard@sun.com rel_shared(shell);
12038439SChris.Gerhard@sun.com shell = create_shared_str(&line[cursor]);
12048439SChris.Gerhard@sun.com }
12058439SChris.Gerhard@sun.com continue;
12068439SChris.Gerhard@sun.com }
12078439SChris.Gerhard@sun.com
12080Sstevel@tonic-gate e = xmalloc(sizeof (struct event));
12090Sstevel@tonic-gate e->etype = CRONEVENT;
12100Sstevel@tonic-gate if (!(((e->of.ct.minute = next_field(0, 59)) != NULL) &&
12110Sstevel@tonic-gate ((e->of.ct.hour = next_field(0, 23)) != NULL) &&
12120Sstevel@tonic-gate ((e->of.ct.daymon = next_field(1, 31)) != NULL) &&
12130Sstevel@tonic-gate ((e->of.ct.month = next_field(1, 12)) != NULL) &&
12140Sstevel@tonic-gate ((e->of.ct.dayweek = next_field(0, 6)) != NULL))) {
12150Sstevel@tonic-gate free(e);
12160Sstevel@tonic-gate cte_add(lineno, line);
12170Sstevel@tonic-gate continue;
12180Sstevel@tonic-gate }
12190Sstevel@tonic-gate while (line[cursor] == ' ' || line[cursor] == '\t')
12200Sstevel@tonic-gate cursor++;
12210Sstevel@tonic-gate if (line[cursor] == '\n' || line[cursor] == '\0')
12220Sstevel@tonic-gate continue;
12230Sstevel@tonic-gate /* get the command to execute */
12240Sstevel@tonic-gate start = cursor;
12250Sstevel@tonic-gate again:
12260Sstevel@tonic-gate while ((line[cursor] != '%') &&
12270Sstevel@tonic-gate (line[cursor] != '\n') &&
12280Sstevel@tonic-gate (line[cursor] != '\0') &&
12290Sstevel@tonic-gate (line[cursor] != '\\'))
12300Sstevel@tonic-gate cursor++;
12310Sstevel@tonic-gate if (line[cursor] == '\\') {
12320Sstevel@tonic-gate cursor += 2;
12330Sstevel@tonic-gate goto again;
12340Sstevel@tonic-gate }
12355558Sbasabi e->cmd = xmalloc(cursor-start + 1);
12365558Sbasabi (void) strncpy(e->cmd, line + start, cursor-start);
12370Sstevel@tonic-gate e->cmd[cursor-start] = '\0';
12380Sstevel@tonic-gate /* see if there is any standard input */
12390Sstevel@tonic-gate if (line[cursor] == '%') {
12405558Sbasabi e->of.ct.input = xmalloc(strlen(line)-cursor + 1);
12415558Sbasabi (void) strcpy(e->of.ct.input, line + cursor + 1);
12420Sstevel@tonic-gate for (i = 0; i < strlen(e->of.ct.input); i++) {
12430Sstevel@tonic-gate if (e->of.ct.input[i] == '%')
12440Sstevel@tonic-gate e->of.ct.input[i] = '\n';
12450Sstevel@tonic-gate }
12460Sstevel@tonic-gate } else {
12470Sstevel@tonic-gate e->of.ct.input = NULL;
12480Sstevel@tonic-gate }
12498439SChris.Gerhard@sun.com /* set the timezone of this entry */
12508439SChris.Gerhard@sun.com e->of.ct.tz = dup_shared(tz);
12518439SChris.Gerhard@sun.com /* set the shell of this entry */
12528439SChris.Gerhard@sun.com e->of.ct.shell = dup_shared(shell);
12538439SChris.Gerhard@sun.com /* set the home of this entry */
12548439SChris.Gerhard@sun.com e->of.ct.home = dup_shared(home);
12550Sstevel@tonic-gate /* have the event point to it's owner */
12560Sstevel@tonic-gate e->u = u;
12570Sstevel@tonic-gate /* insert this event at the front of this user's event list */
12580Sstevel@tonic-gate e->link = u->ctevents;
12590Sstevel@tonic-gate u->ctevents = e;
12600Sstevel@tonic-gate /* set the time for the first occurance of this event */
12610Sstevel@tonic-gate e->time = next_time(e, reftime);
12620Sstevel@tonic-gate /* finally, add this event to the main event list */
12637879SSerge.Dussud@Sun.COM switch (el_add(e, e->time, u->ctid)) {
12647879SSerge.Dussud@Sun.COM case -1:
12657879SSerge.Dussud@Sun.COM ignore_msg("readcron", "cron", e);
12667879SSerge.Dussud@Sun.COM break;
12677879SSerge.Dussud@Sun.COM case -2: /* event time lower than init time */
12687879SSerge.Dussud@Sun.COM reset_needed = 1;
12697879SSerge.Dussud@Sun.COM break;
12707879SSerge.Dussud@Sun.COM }
12710Sstevel@tonic-gate cte_valid();
12720Sstevel@tonic-gate #ifdef DEBUG
12730Sstevel@tonic-gate cftime(timebuf, "%C", &e->time);
12740Sstevel@tonic-gate (void) fprintf(stderr, "inserting cron event %s at %ld (%s)\n",
12755558Sbasabi e->cmd, e->time, timebuf);
12760Sstevel@tonic-gate #endif
12770Sstevel@tonic-gate }
12780Sstevel@tonic-gate cte_sendmail(u->name); /* mail errors if any to user */
12790Sstevel@tonic-gate (void) fclose(cf);
12808439SChris.Gerhard@sun.com rel_shared(tz);
12818439SChris.Gerhard@sun.com rel_shared(shell);
12828439SChris.Gerhard@sun.com rel_shared(home);
12830Sstevel@tonic-gate }
12840Sstevel@tonic-gate
12850Sstevel@tonic-gate /*
12860Sstevel@tonic-gate * Below are the functions for handling of errors in crontabs. Concept is to
12870Sstevel@tonic-gate * collect faulty lines and send one email at the end of the crontab
12880Sstevel@tonic-gate * evaluation. If there are erroneous lines only ((cte_nvalid == 0), evaluation
12890Sstevel@tonic-gate * of crontab is aborted. Otherwise reading of crontab is continued to the end
12900Sstevel@tonic-gate * of the file but no further error logging appears.
12910Sstevel@tonic-gate */
12920Sstevel@tonic-gate static void
cte_init()12930Sstevel@tonic-gate cte_init()
12940Sstevel@tonic-gate {
12950Sstevel@tonic-gate if (cte_text == NULL)
12960Sstevel@tonic-gate cte_text = xmalloc(MAILBUFLEN);
12970Sstevel@tonic-gate (void) strlcpy(cte_text, cte_intro, MAILBUFLEN);
12980Sstevel@tonic-gate cte_lp = cte_text + sizeof (cte_intro) - 1;
12990Sstevel@tonic-gate cte_free = MAILBINITFREE;
13000Sstevel@tonic-gate cte_nvalid = 0;
13010Sstevel@tonic-gate }
13020Sstevel@tonic-gate
13030Sstevel@tonic-gate static void
cte_add(int lineno,char * ctline)13040Sstevel@tonic-gate cte_add(int lineno, char *ctline)
13050Sstevel@tonic-gate {
13060Sstevel@tonic-gate int len;
13070Sstevel@tonic-gate char *p;
13080Sstevel@tonic-gate
13090Sstevel@tonic-gate if (cte_free >= LINELIMIT) {
13100Sstevel@tonic-gate (void) sprintf(cte_lp, "%4d: ", lineno);
13110Sstevel@tonic-gate (void) strlcat(cte_lp, ctline, LINELIMIT - 1);
13120Sstevel@tonic-gate len = strlen(cte_lp);
13130Sstevel@tonic-gate if (cte_lp[len - 1] != '\n') {
13140Sstevel@tonic-gate cte_lp[len++] = '\n';
13150Sstevel@tonic-gate cte_lp[len] = '\0';
13160Sstevel@tonic-gate }
13170Sstevel@tonic-gate for (p = cte_lp; *p; p++) {
13180Sstevel@tonic-gate if (isprint(*p) || *p == '\n' || *p == '\t')
13190Sstevel@tonic-gate continue;
13200Sstevel@tonic-gate *p = '.';
13210Sstevel@tonic-gate }
13220Sstevel@tonic-gate cte_lp += len;
13230Sstevel@tonic-gate cte_free -= len;
13240Sstevel@tonic-gate if (cte_free < LINELIMIT) {
13250Sstevel@tonic-gate size_t buflen = MAILBUFLEN - (cte_lp - cte_text);
13260Sstevel@tonic-gate (void) strlcpy(cte_lp, cte_trail1, buflen);
13270Sstevel@tonic-gate if (cte_nvalid == 0)
13280Sstevel@tonic-gate (void) strlcat(cte_lp, cte_trail2, buflen);
13290Sstevel@tonic-gate }
13300Sstevel@tonic-gate }
13310Sstevel@tonic-gate }
13320Sstevel@tonic-gate
13330Sstevel@tonic-gate static void
cte_valid()13340Sstevel@tonic-gate cte_valid()
13350Sstevel@tonic-gate {
13360Sstevel@tonic-gate cte_nvalid++;
13370Sstevel@tonic-gate }
13380Sstevel@tonic-gate
13390Sstevel@tonic-gate static int
cte_istoomany()13400Sstevel@tonic-gate cte_istoomany()
13410Sstevel@tonic-gate {
13420Sstevel@tonic-gate /*
13430Sstevel@tonic-gate * Return TRUE only if all lines are faulty. So evaluation of
13440Sstevel@tonic-gate * a crontab is not aborted if at least one valid line was found.
13450Sstevel@tonic-gate */
13460Sstevel@tonic-gate return (cte_nvalid == 0 && cte_free < LINELIMIT);
13470Sstevel@tonic-gate }
13480Sstevel@tonic-gate
13490Sstevel@tonic-gate static void
cte_sendmail(char * username)13500Sstevel@tonic-gate cte_sendmail(char *username)
13510Sstevel@tonic-gate {
13520Sstevel@tonic-gate if (cte_free < MAILBINITFREE)
13530Sstevel@tonic-gate mail(username, cte_text, ERR_CRONTABENT);
13540Sstevel@tonic-gate }
13550Sstevel@tonic-gate
13560Sstevel@tonic-gate /*
13570Sstevel@tonic-gate * Send mail with error message to a user
13580Sstevel@tonic-gate */
13590Sstevel@tonic-gate static void
mail(char * usrname,char * mesg,int format)13600Sstevel@tonic-gate mail(char *usrname, char *mesg, int format)
13610Sstevel@tonic-gate {
13620Sstevel@tonic-gate /* mail mails a user a message. */
13630Sstevel@tonic-gate FILE *pipe;
13640Sstevel@tonic-gate char *temp;
13650Sstevel@tonic-gate struct passwd *ruser_ids;
13660Sstevel@tonic-gate pid_t fork_val;
13670Sstevel@tonic-gate int saveerrno = errno;
13680Sstevel@tonic-gate struct utsname name;
13690Sstevel@tonic-gate
13700Sstevel@tonic-gate #ifdef TESTING
13710Sstevel@tonic-gate return;
13720Sstevel@tonic-gate #endif
13730Sstevel@tonic-gate (void) uname(&name);
13740Sstevel@tonic-gate if ((fork_val = fork()) == (pid_t)-1) {
13750Sstevel@tonic-gate msg("cron cannot fork\n");
13760Sstevel@tonic-gate return;
13770Sstevel@tonic-gate }
13780Sstevel@tonic-gate if (fork_val == 0) {
13790Sstevel@tonic-gate child_sigreset();
13800Sstevel@tonic-gate contract_clear_template();
13810Sstevel@tonic-gate if ((ruser_ids = getpwnam(usrname)) == NULL)
13820Sstevel@tonic-gate exit(0);
13830Sstevel@tonic-gate (void) setuid(ruser_ids->pw_uid);
13845558Sbasabi temp = xmalloc(strlen(MAIL) + strlen(usrname) + 2);
13850Sstevel@tonic-gate (void) sprintf(temp, "%s %s", MAIL, usrname);
13860Sstevel@tonic-gate pipe = popen(temp, "w");
13870Sstevel@tonic-gate if (pipe != NULL) {
13880Sstevel@tonic-gate (void) fprintf(pipe, "To: %s\n", usrname);
13890Sstevel@tonic-gate switch (format) {
13900Sstevel@tonic-gate case ERR_CRONTABENT:
13910Sstevel@tonic-gate (void) fprintf(pipe, CRONTABERR);
13920Sstevel@tonic-gate (void) fprintf(pipe, "Your \"crontab\" on %s\n",
13930Sstevel@tonic-gate name.nodename);
13940Sstevel@tonic-gate (void) fprintf(pipe, mesg);
13950Sstevel@tonic-gate (void) fprintf(pipe,
13960Sstevel@tonic-gate "\nEntries or crontab have been ignored\n");
13970Sstevel@tonic-gate break;
13980Sstevel@tonic-gate case ERR_UNIXERR:
13990Sstevel@tonic-gate (void) fprintf(pipe, "Subject: %s\n\n", mesg);
14000Sstevel@tonic-gate (void) fprintf(pipe,
14010Sstevel@tonic-gate "The error on %s was \"%s\"\n",
14020Sstevel@tonic-gate name.nodename, errmsg(saveerrno));
14030Sstevel@tonic-gate break;
14040Sstevel@tonic-gate
14050Sstevel@tonic-gate case ERR_CANTEXECCRON:
14060Sstevel@tonic-gate (void) fprintf(pipe,
14070Sstevel@tonic-gate "Subject: Couldn't run your \"cron\" job\n\n");
14080Sstevel@tonic-gate (void) fprintf(pipe,
14090Sstevel@tonic-gate "Your \"cron\" job on %s ", name.nodename);
14100Sstevel@tonic-gate (void) fprintf(pipe, "couldn't be run\n");
14110Sstevel@tonic-gate (void) fprintf(pipe, "%s\n", mesg);
14120Sstevel@tonic-gate (void) fprintf(pipe,
14130Sstevel@tonic-gate "The error was \"%s\"\n", errmsg(saveerrno));
14140Sstevel@tonic-gate break;
14150Sstevel@tonic-gate
14160Sstevel@tonic-gate case ERR_CANTEXECAT:
14170Sstevel@tonic-gate (void) fprintf(pipe,
14180Sstevel@tonic-gate "Subject: Couldn't run your \"at\" job\n\n");
14190Sstevel@tonic-gate (void) fprintf(pipe, "Your \"at\" job on %s ",
14200Sstevel@tonic-gate name.nodename);
14210Sstevel@tonic-gate (void) fprintf(pipe, "couldn't be run\n");
14220Sstevel@tonic-gate (void) fprintf(pipe, "%s\n", mesg);
14230Sstevel@tonic-gate (void) fprintf(pipe,
14240Sstevel@tonic-gate "The error was \"%s\"\n", errmsg(saveerrno));
14250Sstevel@tonic-gate break;
14260Sstevel@tonic-gate
14270Sstevel@tonic-gate default:
14280Sstevel@tonic-gate break;
14290Sstevel@tonic-gate }
14300Sstevel@tonic-gate (void) pclose(pipe);
14310Sstevel@tonic-gate }
14320Sstevel@tonic-gate free(temp);
14330Sstevel@tonic-gate exit(0);
14340Sstevel@tonic-gate }
14350Sstevel@tonic-gate
14360Sstevel@tonic-gate contract_abandon_latest(fork_val);
14370Sstevel@tonic-gate
14380Sstevel@tonic-gate if (cron_pid == getpid()) {
14390Sstevel@tonic-gate miscpid_insert(fork_val);
14400Sstevel@tonic-gate }
14410Sstevel@tonic-gate }
14420Sstevel@tonic-gate
14430Sstevel@tonic-gate static char *
next_field(int lower,int upper)14440Sstevel@tonic-gate next_field(int lower, int upper)
14450Sstevel@tonic-gate {
14460Sstevel@tonic-gate /*
14470Sstevel@tonic-gate * next_field returns a pointer to a string which holds the next
14480Sstevel@tonic-gate * field of a line of a crontab file.
14490Sstevel@tonic-gate * if (numbers in this field are out of range (lower..upper),
14500Sstevel@tonic-gate * or there is a syntax error) then
14510Sstevel@tonic-gate * NULL is returned, and a mail message is sent to the
14520Sstevel@tonic-gate * user telling him which line the error was in.
14530Sstevel@tonic-gate */
14540Sstevel@tonic-gate
14550Sstevel@tonic-gate char *s;
14560Sstevel@tonic-gate int num, num2, start;
14570Sstevel@tonic-gate
14580Sstevel@tonic-gate while ((line[cursor] == ' ') || (line[cursor] == '\t'))
14590Sstevel@tonic-gate cursor++;
14600Sstevel@tonic-gate start = cursor;
14610Sstevel@tonic-gate if (line[cursor] == '\0') {
14620Sstevel@tonic-gate return (NULL);
14630Sstevel@tonic-gate }
14640Sstevel@tonic-gate if (line[cursor] == '*') {
14650Sstevel@tonic-gate cursor++;
14660Sstevel@tonic-gate if ((line[cursor] != ' ') && (line[cursor] != '\t'))
14670Sstevel@tonic-gate return (NULL);
14680Sstevel@tonic-gate s = xmalloc(2);
14690Sstevel@tonic-gate (void) strcpy(s, "*");
14700Sstevel@tonic-gate return (s);
14710Sstevel@tonic-gate }
14720Sstevel@tonic-gate for (;;) {
14730Sstevel@tonic-gate if (!isdigit(line[cursor]))
14740Sstevel@tonic-gate return (NULL);
14750Sstevel@tonic-gate num = 0;
14760Sstevel@tonic-gate do {
14770Sstevel@tonic-gate num = num*10 + (line[cursor]-'0');
14780Sstevel@tonic-gate } while (isdigit(line[++cursor]));
14790Sstevel@tonic-gate if ((num < lower) || (num > upper))
14800Sstevel@tonic-gate return (NULL);
14810Sstevel@tonic-gate if (line[cursor] == '-') {
14820Sstevel@tonic-gate if (!isdigit(line[++cursor]))
14830Sstevel@tonic-gate return (NULL);
14840Sstevel@tonic-gate num2 = 0;
14850Sstevel@tonic-gate do {
14860Sstevel@tonic-gate num2 = num2*10 + (line[cursor]-'0');
14870Sstevel@tonic-gate } while (isdigit(line[++cursor]));
14880Sstevel@tonic-gate if ((num2 < lower) || (num2 > upper))
14890Sstevel@tonic-gate return (NULL);
14900Sstevel@tonic-gate }
14910Sstevel@tonic-gate if ((line[cursor] == ' ') || (line[cursor] == '\t'))
14920Sstevel@tonic-gate break;
14930Sstevel@tonic-gate if (line[cursor] == '\0')
14940Sstevel@tonic-gate return (NULL);
14950Sstevel@tonic-gate if (line[cursor++] != ',')
14960Sstevel@tonic-gate return (NULL);
14970Sstevel@tonic-gate }
14985558Sbasabi s = xmalloc(cursor-start + 1);
14995558Sbasabi (void) strncpy(s, line + start, cursor-start);
15000Sstevel@tonic-gate s[cursor-start] = '\0';
15010Sstevel@tonic-gate return (s);
15020Sstevel@tonic-gate }
15030Sstevel@tonic-gate
15040Sstevel@tonic-gate #define tm_cmp(t1, t2) (\
15050Sstevel@tonic-gate (t1)->tm_year == (t2)->tm_year && \
15060Sstevel@tonic-gate (t1)->tm_mon == (t2)->tm_mon && \
15070Sstevel@tonic-gate (t1)->tm_mday == (t2)->tm_mday && \
15080Sstevel@tonic-gate (t1)->tm_hour == (t2)->tm_hour && \
15090Sstevel@tonic-gate (t1)->tm_min == (t2)->tm_min)
15100Sstevel@tonic-gate
15110Sstevel@tonic-gate #define tm_setup(tp, yr, mon, dy, hr, min, dst) \
15120Sstevel@tonic-gate (tp)->tm_year = yr; \
15130Sstevel@tonic-gate (tp)->tm_mon = mon; \
15140Sstevel@tonic-gate (tp)->tm_mday = dy; \
15150Sstevel@tonic-gate (tp)->tm_hour = hr; \
15160Sstevel@tonic-gate (tp)->tm_min = min; \
15170Sstevel@tonic-gate (tp)->tm_isdst = dst; \
15180Sstevel@tonic-gate (tp)->tm_sec = 0; \
15190Sstevel@tonic-gate (tp)->tm_wday = 0; \
15200Sstevel@tonic-gate (tp)->tm_yday = 0;
15210Sstevel@tonic-gate
15220Sstevel@tonic-gate /*
15230Sstevel@tonic-gate * modification for bugid 1104537. the second argument to next_time is
15240Sstevel@tonic-gate * now the value of time(2) to be used. if this is 0, then use the
15250Sstevel@tonic-gate * current time. otherwise, the second argument is the time from which to
15260Sstevel@tonic-gate * calculate things. this is useful to correct situations where you've
15270Sstevel@tonic-gate * gone backwards in time (I.e. the system's internal clock is correcting
15280Sstevel@tonic-gate * itself backwards).
15290Sstevel@tonic-gate */
15300Sstevel@tonic-gate
15318439SChris.Gerhard@sun.com
15328439SChris.Gerhard@sun.com
15330Sstevel@tonic-gate static time_t
tz_next_time(struct event * e,time_t tflag)15348439SChris.Gerhard@sun.com tz_next_time(struct event *e, time_t tflag)
15350Sstevel@tonic-gate {
15360Sstevel@tonic-gate /*
15370Sstevel@tonic-gate * returns the integer time for the next occurance of event e.
15380Sstevel@tonic-gate * the following fields have ranges as indicated:
15390Sstevel@tonic-gate * PRGM | min hour day of month mon day of week
15400Sstevel@tonic-gate * ------|-------------------------------------------------------
15410Sstevel@tonic-gate * cron | 0-59 0-23 1-31 1-12 0-6 (0=sunday)
15420Sstevel@tonic-gate * time | 0-59 0-23 1-31 0-11 0-6 (0=sunday)
15430Sstevel@tonic-gate * NOTE: this routine is hard to understand.
15440Sstevel@tonic-gate */
15450Sstevel@tonic-gate
15460Sstevel@tonic-gate struct tm *tm, ref_tm, tmp, tmp1, tmp2;
15478439SChris.Gerhard@sun.com int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days;
15488439SChris.Gerhard@sun.com int d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd;
15498439SChris.Gerhard@sun.com int today;
15500Sstevel@tonic-gate time_t t, ref_t, t1, t2, zone_start;
15510Sstevel@tonic-gate int fallback;
15520Sstevel@tonic-gate extern int days_btwn(int, int, int, int, int, int);
15530Sstevel@tonic-gate
15540Sstevel@tonic-gate if (tflag == 0) {
15550Sstevel@tonic-gate t = time(NULL); /* original way of doing things */
15560Sstevel@tonic-gate } else {
15570Sstevel@tonic-gate t = tflag;
15580Sstevel@tonic-gate }
15590Sstevel@tonic-gate
15600Sstevel@tonic-gate tm = &ref_tm; /* use a local variable and call localtime_r() */
15610Sstevel@tonic-gate ref_t = t; /* keep a copy of the reference time */
15620Sstevel@tonic-gate
15630Sstevel@tonic-gate recalc:
15640Sstevel@tonic-gate fallback = 0;
15650Sstevel@tonic-gate
15660Sstevel@tonic-gate (void) localtime_r(&t, tm);
15670Sstevel@tonic-gate
15680Sstevel@tonic-gate if (daylight) {
15690Sstevel@tonic-gate tmp = *tm;
15700Sstevel@tonic-gate tmp.tm_isdst = (tm->tm_isdst > 0 ? 0 : 1);
15710Sstevel@tonic-gate t1 = xmktime(&tmp);
15720Sstevel@tonic-gate /*
15730Sstevel@tonic-gate * see if we will have timezone switch over, and clock will
15740Sstevel@tonic-gate * fall back. zone_start will hold the time when it happens
15750Sstevel@tonic-gate * (ie time of PST -> PDT switch over).
15760Sstevel@tonic-gate */
15770Sstevel@tonic-gate if (tm->tm_isdst != tmp.tm_isdst &&
15780Sstevel@tonic-gate (t1 - t) == (timezone - altzone) &&
15790Sstevel@tonic-gate tm_cmp(tm, &tmp)) {
15800Sstevel@tonic-gate zone_start = get_switching_time(tmp.tm_isdst, t);
15810Sstevel@tonic-gate fallback = 1;
15820Sstevel@tonic-gate }
15830Sstevel@tonic-gate }
15840Sstevel@tonic-gate
15855558Sbasabi tm_mon = next_ge(tm->tm_mon + 1, e->of.ct.month) - 1; /* 0-11 */
15860Sstevel@tonic-gate tm_mday = next_ge(tm->tm_mday, e->of.ct.daymon); /* 1-31 */
15870Sstevel@tonic-gate tm_wday = next_ge(tm->tm_wday, e->of.ct.dayweek); /* 0-6 */
15880Sstevel@tonic-gate today = TRUE;
15890Sstevel@tonic-gate if ((strcmp(e->of.ct.daymon, "*") == 0 && tm->tm_wday != tm_wday) ||
15900Sstevel@tonic-gate (strcmp(e->of.ct.dayweek, "*") == 0 && tm->tm_mday != tm_mday) ||
15910Sstevel@tonic-gate (tm->tm_mday != tm_mday && tm->tm_wday != tm_wday) ||
15920Sstevel@tonic-gate (tm->tm_mon != tm_mon)) {
15930Sstevel@tonic-gate today = FALSE;
15940Sstevel@tonic-gate }
15950Sstevel@tonic-gate m = tm->tm_min + (t == ref_t ? 1 : 0);
15960Sstevel@tonic-gate if ((tm->tm_hour + 1) <= next_ge(tm->tm_hour, e->of.ct.hour)) {
15970Sstevel@tonic-gate m = 0;
15980Sstevel@tonic-gate }
15990Sstevel@tonic-gate min = next_ge(m%60, e->of.ct.minute);
16000Sstevel@tonic-gate carry = (min < m) ? 1 : 0;
16010Sstevel@tonic-gate h = tm->tm_hour + carry;
16020Sstevel@tonic-gate hr = next_ge(h%24, e->of.ct.hour);
16030Sstevel@tonic-gate carry = (hr < h) ? 1 : 0;
16040Sstevel@tonic-gate
16050Sstevel@tonic-gate if (carry == 0 && today) {
16060Sstevel@tonic-gate /* this event must occur today */
16070Sstevel@tonic-gate tm_setup(&tmp, tm->tm_year, tm->tm_mon, tm->tm_mday,
16085558Sbasabi hr, min, tm->tm_isdst);
16090Sstevel@tonic-gate tmp1 = tmp;
16100Sstevel@tonic-gate if ((t1 = xmktime(&tmp1)) == (time_t)-1) {
16110Sstevel@tonic-gate return (0);
16120Sstevel@tonic-gate }
16130Sstevel@tonic-gate if (daylight && tmp.tm_isdst != tmp1.tm_isdst) {
16140Sstevel@tonic-gate /* In case we are falling back */
16150Sstevel@tonic-gate if (fallback) {
16160Sstevel@tonic-gate /* we may need to run the job once more. */
16170Sstevel@tonic-gate t = zone_start;
16180Sstevel@tonic-gate goto recalc;
16190Sstevel@tonic-gate }
16200Sstevel@tonic-gate
16210Sstevel@tonic-gate /*
16220Sstevel@tonic-gate * In case we are not in falling back period,
16230Sstevel@tonic-gate * calculate the time assuming the DST. If the
16240Sstevel@tonic-gate * date/time is not altered by mktime, it is the
16250Sstevel@tonic-gate * time to execute the job.
16260Sstevel@tonic-gate */
16270Sstevel@tonic-gate tmp2 = tmp;
16280Sstevel@tonic-gate tmp2.tm_isdst = tmp1.tm_isdst;
16290Sstevel@tonic-gate if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
16300Sstevel@tonic-gate return (0);
16310Sstevel@tonic-gate }
16320Sstevel@tonic-gate if (tmp1.tm_isdst == tmp2.tm_isdst &&
16330Sstevel@tonic-gate tm_cmp(&tmp, &tmp2)) {
16340Sstevel@tonic-gate /*
16350Sstevel@tonic-gate * We got a valid time.
16360Sstevel@tonic-gate */
16370Sstevel@tonic-gate return (t1);
16380Sstevel@tonic-gate } else {
16390Sstevel@tonic-gate /*
16400Sstevel@tonic-gate * If the date does not match even if
16410Sstevel@tonic-gate * we assume the alternate timezone, then
16420Sstevel@tonic-gate * it must be the invalid time. eg
16430Sstevel@tonic-gate * 2am while switching 1:59am to 3am.
16440Sstevel@tonic-gate * t1 should point the time before the
16450Sstevel@tonic-gate * switching over as we've calculate the
16460Sstevel@tonic-gate * time with assuming alternate zone.
16470Sstevel@tonic-gate */
16480Sstevel@tonic-gate if (tmp1.tm_isdst != tmp2.tm_isdst) {
16490Sstevel@tonic-gate t = get_switching_time(tmp1.tm_isdst,
16505558Sbasabi t1);
16510Sstevel@tonic-gate } else {
16520Sstevel@tonic-gate /* does this really happen? */
16530Sstevel@tonic-gate t = get_switching_time(tmp1.tm_isdst,
16545558Sbasabi t1 - abs(timezone - altzone));
16550Sstevel@tonic-gate }
16568439SChris.Gerhard@sun.com if (t == (time_t)-1) {
16570Sstevel@tonic-gate return (0);
16588439SChris.Gerhard@sun.com }
16590Sstevel@tonic-gate }
16600Sstevel@tonic-gate goto recalc;
16610Sstevel@tonic-gate }
16620Sstevel@tonic-gate if (tm_cmp(&tmp, &tmp1)) {
16630Sstevel@tonic-gate /* got valid time */
16640Sstevel@tonic-gate return (t1);
16650Sstevel@tonic-gate } else {
16660Sstevel@tonic-gate /*
16670Sstevel@tonic-gate * This should never happen, but just in
16680Sstevel@tonic-gate * case, we fall back to the old code.
16690Sstevel@tonic-gate */
16700Sstevel@tonic-gate if (tm->tm_min > min) {
16710Sstevel@tonic-gate t += (time_t)(hr-tm->tm_hour-1) * HOUR +
16725558Sbasabi (time_t)(60-tm->tm_min + min) * MINUTE;
16730Sstevel@tonic-gate } else {
16740Sstevel@tonic-gate t += (time_t)(hr-tm->tm_hour) * HOUR +
16755558Sbasabi (time_t)(min-tm->tm_min) * MINUTE;
16760Sstevel@tonic-gate }
16770Sstevel@tonic-gate t1 = t;
16780Sstevel@tonic-gate t -= (time_t)tm->tm_sec;
16790Sstevel@tonic-gate (void) localtime_r(&t, &tmp);
16800Sstevel@tonic-gate if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
16810Sstevel@tonic-gate t -= (timezone - altzone);
16820Sstevel@tonic-gate return ((t <= ref_t) ? t1 : t);
16830Sstevel@tonic-gate }
16840Sstevel@tonic-gate }
16850Sstevel@tonic-gate
16860Sstevel@tonic-gate /*
16870Sstevel@tonic-gate * Job won't run today, however if we have a switch over within
16880Sstevel@tonic-gate * one hour and we will have one hour time drifting back in this
16890Sstevel@tonic-gate * period, we may need to run the job one more time if the job was
16900Sstevel@tonic-gate * set to run on this hour of clock.
16910Sstevel@tonic-gate */
16920Sstevel@tonic-gate if (fallback) {
16930Sstevel@tonic-gate t = zone_start;
16940Sstevel@tonic-gate goto recalc;
16950Sstevel@tonic-gate }
16960Sstevel@tonic-gate
16970Sstevel@tonic-gate min = next_ge(0, e->of.ct.minute);
16980Sstevel@tonic-gate hr = next_ge(0, e->of.ct.hour);
16990Sstevel@tonic-gate
17000Sstevel@tonic-gate /*
17010Sstevel@tonic-gate * calculate the date of the next occurance of this event, which
17020Sstevel@tonic-gate * will be on a different day than the current
17030Sstevel@tonic-gate */
17040Sstevel@tonic-gate
17050Sstevel@tonic-gate /* check monthly day specification */
17065558Sbasabi d1 = tm->tm_mday + 1;
17075558Sbasabi day1 = next_ge((d1-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1,
17085558Sbasabi e->of.ct.daymon);
17090Sstevel@tonic-gate carry1 = (day1 < d1) ? 1 : 0;
17100Sstevel@tonic-gate
17110Sstevel@tonic-gate /* check weekly day specification */
17125558Sbasabi d2 = tm->tm_wday + 1;
17130Sstevel@tonic-gate wday = next_ge(d2%7, e->of.ct.dayweek);
17140Sstevel@tonic-gate if (wday < d2)
17150Sstevel@tonic-gate daysahead = 7 - d2 + wday;
17160Sstevel@tonic-gate else
17170Sstevel@tonic-gate daysahead = wday - d2;
17185558Sbasabi day2 = (d1 + daysahead-1)%days_in_mon(tm->tm_mon, tm->tm_year) + 1;
17190Sstevel@tonic-gate carry2 = (day2 < d1) ? 1 : 0;
17200Sstevel@tonic-gate
17210Sstevel@tonic-gate /*
17220Sstevel@tonic-gate * based on their respective specifications, day1, and day2 give
17230Sstevel@tonic-gate * the day of the month for the next occurance of this event.
17240Sstevel@tonic-gate */
17250Sstevel@tonic-gate if ((strcmp(e->of.ct.daymon, "*") == 0) &&
17260Sstevel@tonic-gate (strcmp(e->of.ct.dayweek, "*") != 0)) {
17270Sstevel@tonic-gate day1 = day2;
17280Sstevel@tonic-gate carry1 = carry2;
17290Sstevel@tonic-gate }
17300Sstevel@tonic-gate if ((strcmp(e->of.ct.daymon, "*") != 0) &&
17310Sstevel@tonic-gate (strcmp(e->of.ct.dayweek, "*") == 0)) {
17320Sstevel@tonic-gate day2 = day1;
17330Sstevel@tonic-gate carry2 = carry1;
17340Sstevel@tonic-gate }
17350Sstevel@tonic-gate
17360Sstevel@tonic-gate yr = tm->tm_year;
17370Sstevel@tonic-gate if ((carry1 && carry2) || (tm->tm_mon != tm_mon)) {
17380Sstevel@tonic-gate /* event does not occur in this month */
17395558Sbasabi m = tm->tm_mon + 1;
17405558Sbasabi mon = next_ge(m%12 + 1, e->of.ct.month) - 1; /* 0..11 */
17410Sstevel@tonic-gate carry = (mon < m) ? 1 : 0;
17420Sstevel@tonic-gate yr += carry;
17430Sstevel@tonic-gate /* recompute day1 and day2 */
17440Sstevel@tonic-gate day1 = next_ge(1, e->of.ct.daymon);
17450Sstevel@tonic-gate db = days_btwn(tm->tm_mon, tm->tm_mday, tm->tm_year, mon,
17465558Sbasabi 1, yr) + 1;
17475558Sbasabi wd = (tm->tm_wday + db)%7;
17480Sstevel@tonic-gate /* wd is the day of the week of the first of month mon */
17490Sstevel@tonic-gate wday = next_ge(wd, e->of.ct.dayweek);
17500Sstevel@tonic-gate if (wday < wd)
17510Sstevel@tonic-gate day2 = 1 + 7 - wd + wday;
17520Sstevel@tonic-gate else
17530Sstevel@tonic-gate day2 = 1 + wday - wd;
17540Sstevel@tonic-gate if ((strcmp(e->of.ct.daymon, "*") != 0) &&
17550Sstevel@tonic-gate (strcmp(e->of.ct.dayweek, "*") == 0))
17560Sstevel@tonic-gate day2 = day1;
17570Sstevel@tonic-gate if ((strcmp(e->of.ct.daymon, "*") == 0) &&
17580Sstevel@tonic-gate (strcmp(e->of.ct.dayweek, "*") != 0))
17590Sstevel@tonic-gate day1 = day2;
17600Sstevel@tonic-gate day = (day1 < day2) ? day1 : day2;
17610Sstevel@tonic-gate } else { /* event occurs in this month */
17620Sstevel@tonic-gate mon = tm->tm_mon;
17630Sstevel@tonic-gate if (!carry1 && !carry2)
17640Sstevel@tonic-gate day = (day1 < day2) ? day1 : day2;
17650Sstevel@tonic-gate else if (!carry1)
17660Sstevel@tonic-gate day = day1;
17670Sstevel@tonic-gate else
17680Sstevel@tonic-gate day = day2;
17690Sstevel@tonic-gate }
17700Sstevel@tonic-gate
17710Sstevel@tonic-gate /*
17720Sstevel@tonic-gate * now that we have the min, hr, day, mon, yr of the next event,
17730Sstevel@tonic-gate * figure out what time that turns out to be.
17740Sstevel@tonic-gate */
17750Sstevel@tonic-gate tm_setup(&tmp, yr, mon, day, hr, min, -1);
17760Sstevel@tonic-gate tmp2 = tmp;
17770Sstevel@tonic-gate if ((t1 = xmktime(&tmp2)) == (time_t)-1) {
17780Sstevel@tonic-gate return (0);
17790Sstevel@tonic-gate }
17800Sstevel@tonic-gate if (tm_cmp(&tmp, &tmp2)) {
17810Sstevel@tonic-gate /*
17820Sstevel@tonic-gate * mktime returns clock for the current time zone. If the
17830Sstevel@tonic-gate * target date was in fallback period, it needs to be adjusted
17840Sstevel@tonic-gate * to the time comes first.
17850Sstevel@tonic-gate * Suppose, we are at Jan and scheduling job at 1:30am10/26/03.
17860Sstevel@tonic-gate * mktime returns the time in PST, but 1:30am in PDT comes
17870Sstevel@tonic-gate * first. So reverse the tm_isdst, and see if we have such
17880Sstevel@tonic-gate * time/date.
17890Sstevel@tonic-gate */
17900Sstevel@tonic-gate if (daylight) {
17910Sstevel@tonic-gate int dst = tmp2.tm_isdst;
17920Sstevel@tonic-gate
17930Sstevel@tonic-gate tmp2 = tmp;
17940Sstevel@tonic-gate tmp2.tm_isdst = (dst > 0 ? 0 : 1);
17950Sstevel@tonic-gate if ((t2 = xmktime(&tmp2)) == (time_t)-1) {
17960Sstevel@tonic-gate return (0);
17970Sstevel@tonic-gate }
17980Sstevel@tonic-gate if (tm_cmp(&tmp, &tmp2)) {
17990Sstevel@tonic-gate /*
18000Sstevel@tonic-gate * same time/date found in the opposite zone.
18010Sstevel@tonic-gate * check the clock to see which comes early.
18020Sstevel@tonic-gate */
18030Sstevel@tonic-gate if (t2 > ref_t && t2 < t1) {
18040Sstevel@tonic-gate t1 = t2;
18050Sstevel@tonic-gate }
18060Sstevel@tonic-gate }
18070Sstevel@tonic-gate }
18080Sstevel@tonic-gate return (t1);
18090Sstevel@tonic-gate } else {
18100Sstevel@tonic-gate /*
18110Sstevel@tonic-gate * mktime has set different time/date for the given date.
18120Sstevel@tonic-gate * This means that the next job is scheduled to be run on the
18130Sstevel@tonic-gate * invalid time. There are three possible invalid date/time.
18140Sstevel@tonic-gate * 1. Non existing day of the month. such as April 31th.
18150Sstevel@tonic-gate * 2. Feb 29th in the non-leap year.
18160Sstevel@tonic-gate * 3. Time gap during the DST switch over.
18170Sstevel@tonic-gate */
18180Sstevel@tonic-gate d1 = days_in_mon(mon, yr);
18190Sstevel@tonic-gate if ((mon != 1 && day > d1) || (mon == 1 && day > 29)) {
18200Sstevel@tonic-gate /*
18210Sstevel@tonic-gate * see if we have got a specific date which
18220Sstevel@tonic-gate * is invalid.
18230Sstevel@tonic-gate */
18240Sstevel@tonic-gate if (strcmp(e->of.ct.dayweek, "*") == 0 &&
18255558Sbasabi mon == (next_ge((mon + 1)%12 + 1,
18265558Sbasabi e->of.ct.month) - 1) &&
18270Sstevel@tonic-gate day <= next_ge(1, e->of.ct.daymon)) {
18280Sstevel@tonic-gate /* job never run */
18290Sstevel@tonic-gate return (0);
18300Sstevel@tonic-gate }
18310Sstevel@tonic-gate /*
18320Sstevel@tonic-gate * Since the day has gone invalid, we need to go to
18330Sstevel@tonic-gate * next month, and recalcuate the first occurrence.
18340Sstevel@tonic-gate * eg the cron tab such as:
18350Sstevel@tonic-gate * 0 0 1,15,31 1,2,3,4,5 * /usr/bin....
18360Sstevel@tonic-gate * 2/31 is invalid, so the next job is 3/1.
18370Sstevel@tonic-gate */
18380Sstevel@tonic-gate tmp2 = tmp;
18390Sstevel@tonic-gate tmp2.tm_min = 0;
18400Sstevel@tonic-gate tmp2.tm_hour = 0;
18410Sstevel@tonic-gate tmp2.tm_mday = 1; /* 1st day of the month */
18420Sstevel@tonic-gate if (mon == 11) {
18430Sstevel@tonic-gate tmp2.tm_mon = 0;
18440Sstevel@tonic-gate tmp2.tm_year = yr + 1;
18450Sstevel@tonic-gate } else {
18460Sstevel@tonic-gate tmp2.tm_mon = mon + 1;
18470Sstevel@tonic-gate }
18480Sstevel@tonic-gate if ((t = xmktime(&tmp2)) == (time_t)-1) {
18490Sstevel@tonic-gate return (0);
18500Sstevel@tonic-gate }
18510Sstevel@tonic-gate } else if (mon == 1 && day > d1) {
18520Sstevel@tonic-gate /*
18530Sstevel@tonic-gate * ie 29th in the non-leap year. Forwarding the
18540Sstevel@tonic-gate * clock to Feb 29th 00:00 (March 1st), and recalculate
18550Sstevel@tonic-gate * the next time.
18560Sstevel@tonic-gate */
18570Sstevel@tonic-gate tmp2 = tmp;
18580Sstevel@tonic-gate tmp2.tm_min = 0;
18590Sstevel@tonic-gate tmp2.tm_hour = 0;
18600Sstevel@tonic-gate if ((t = xmktime(&tmp2)) == (time_t)-1) {
18610Sstevel@tonic-gate return (0);
18620Sstevel@tonic-gate }
18630Sstevel@tonic-gate } else if (daylight) {
18640Sstevel@tonic-gate /*
18650Sstevel@tonic-gate * Non existing time, eg 2am PST during summer time
18660Sstevel@tonic-gate * switch.
18670Sstevel@tonic-gate * We need to get the correct isdst which we are
18680Sstevel@tonic-gate * swithing to, by adding time difference to make sure
18690Sstevel@tonic-gate * that t2 is in the zone being switched.
18700Sstevel@tonic-gate */
18710Sstevel@tonic-gate t2 = t1;
18720Sstevel@tonic-gate t2 += abs(timezone - altzone);
18730Sstevel@tonic-gate (void) localtime_r(&t2, &tmp2);
18740Sstevel@tonic-gate zone_start = get_switching_time(tmp2.tm_isdst,
18755558Sbasabi t1 - abs(timezone - altzone));
18760Sstevel@tonic-gate if (zone_start == (time_t)-1) {
18770Sstevel@tonic-gate return (0);
18780Sstevel@tonic-gate }
18790Sstevel@tonic-gate t = zone_start;
18800Sstevel@tonic-gate } else {
18810Sstevel@tonic-gate /*
18820Sstevel@tonic-gate * This should never happen, but fall back to the
18830Sstevel@tonic-gate * old code.
18840Sstevel@tonic-gate */
18850Sstevel@tonic-gate days = days_btwn(tm->tm_mon,
18865558Sbasabi tm->tm_mday, tm->tm_year, mon, day, yr);
18870Sstevel@tonic-gate t += (time_t)(23-tm->tm_hour)*HOUR
18885558Sbasabi + (time_t)(60-tm->tm_min)*MINUTE
18895558Sbasabi + (time_t)hr*HOUR + (time_t)min*MINUTE
18905558Sbasabi + (time_t)days*DAY;
18910Sstevel@tonic-gate t1 = t;
18920Sstevel@tonic-gate t -= (time_t)tm->tm_sec;
18930Sstevel@tonic-gate (void) localtime_r(&t, &tmp);
18940Sstevel@tonic-gate if ((tm->tm_isdst == 0) && (tmp.tm_isdst > 0))
18950Sstevel@tonic-gate t -= (timezone - altzone);
18960Sstevel@tonic-gate return (t <= ref_t ? t1 : t);
18970Sstevel@tonic-gate }
18980Sstevel@tonic-gate goto recalc;
18990Sstevel@tonic-gate }
19000Sstevel@tonic-gate /*NOTREACHED*/
19010Sstevel@tonic-gate }
19020Sstevel@tonic-gate
19038439SChris.Gerhard@sun.com static time_t
next_time(struct event * e,time_t tflag)19048439SChris.Gerhard@sun.com next_time(struct event *e, time_t tflag)
19058439SChris.Gerhard@sun.com {
19068439SChris.Gerhard@sun.com if (e->of.ct.tz != NULL) {
19078439SChris.Gerhard@sun.com time_t ret;
19088439SChris.Gerhard@sun.com
19098439SChris.Gerhard@sun.com (void) putenv((char *)get_obj(e->of.ct.tz));
19108439SChris.Gerhard@sun.com tzset();
19118439SChris.Gerhard@sun.com ret = tz_next_time(e, tflag);
19128439SChris.Gerhard@sun.com (void) putenv(tzone);
19138439SChris.Gerhard@sun.com tzset();
19148439SChris.Gerhard@sun.com return (ret);
19158439SChris.Gerhard@sun.com } else {
19168439SChris.Gerhard@sun.com return (tz_next_time(e, tflag));
19178439SChris.Gerhard@sun.com }
19188439SChris.Gerhard@sun.com }
19198439SChris.Gerhard@sun.com
19200Sstevel@tonic-gate /*
19210Sstevel@tonic-gate * This returns TOD in time_t that zone switch will happen, and this
19220Sstevel@tonic-gate * will be called when clock fallback is about to happen.
19230Sstevel@tonic-gate * (ie 30minutes before the time of PST -> PDT switch. 2:00 AM PST
19240Sstevel@tonic-gate * will fall back to 1:00 PDT. So this function will be called only
19250Sstevel@tonic-gate * for the time between 1:00 AM PST and 2:00 PST(1:00 PST)).
19260Sstevel@tonic-gate * First goes through the common time differences to see if zone
19270Sstevel@tonic-gate * switch happens at those minutes later. If not, check every minutes
19280Sstevel@tonic-gate * until 6 hours ahead see if it happens(We might have 45minutes
19290Sstevel@tonic-gate * fallback).
19300Sstevel@tonic-gate */
19310Sstevel@tonic-gate static time_t
get_switching_time(int to_dst,time_t t_ref)19320Sstevel@tonic-gate get_switching_time(int to_dst, time_t t_ref)
19330Sstevel@tonic-gate {
19340Sstevel@tonic-gate time_t t, t1;
19350Sstevel@tonic-gate struct tm tmp, tmp1;
19360Sstevel@tonic-gate int hints[] = { 60, 120, 30, 90, 0}; /* minutes */
19370Sstevel@tonic-gate int i;
19380Sstevel@tonic-gate
19390Sstevel@tonic-gate (void) localtime_r(&t_ref, &tmp);
19400Sstevel@tonic-gate tmp1 = tmp;
19410Sstevel@tonic-gate tmp1.tm_sec = 0;
19420Sstevel@tonic-gate tmp1.tm_min = 0;
19430Sstevel@tonic-gate if ((t = xmktime(&tmp1)) == (time_t)-1)
19440Sstevel@tonic-gate return ((time_t)-1);
19450Sstevel@tonic-gate
19460Sstevel@tonic-gate /* fast path */
19470Sstevel@tonic-gate for (i = 0; hints[i] != 0; i++) {
19480Sstevel@tonic-gate t1 = t + hints[i] * 60;
19490Sstevel@tonic-gate (void) localtime_r(&t1, &tmp1);
19500Sstevel@tonic-gate if (tmp1.tm_isdst == to_dst) {
19510Sstevel@tonic-gate t1--;
19520Sstevel@tonic-gate (void) localtime_r(&t1, &tmp1);
19530Sstevel@tonic-gate if (tmp1.tm_isdst != to_dst) {
19540Sstevel@tonic-gate return (t1 + 1);
19550Sstevel@tonic-gate }
19560Sstevel@tonic-gate }
19570Sstevel@tonic-gate }
19580Sstevel@tonic-gate
19590Sstevel@tonic-gate /* ugly, but don't know other than this. */
19600Sstevel@tonic-gate tmp1 = tmp;
19610Sstevel@tonic-gate tmp1.tm_sec = 0;
19620Sstevel@tonic-gate if ((t = xmktime(&tmp1)) == (time_t)-1)
19630Sstevel@tonic-gate return ((time_t)-1);
19640Sstevel@tonic-gate while (t < (t_ref + 6*60*60)) { /* 6 hours should be enough */
19650Sstevel@tonic-gate t += 60; /* at least one minute, I assume */
19660Sstevel@tonic-gate (void) localtime_r(&t, &tmp);
19670Sstevel@tonic-gate if (tmp.tm_isdst == to_dst)
19680Sstevel@tonic-gate return (t);
19690Sstevel@tonic-gate }
19700Sstevel@tonic-gate return ((time_t)-1);
19710Sstevel@tonic-gate }
19720Sstevel@tonic-gate
19730Sstevel@tonic-gate static time_t
xmktime(struct tm * tmp)19740Sstevel@tonic-gate xmktime(struct tm *tmp)
19750Sstevel@tonic-gate {
19760Sstevel@tonic-gate time_t ret;
19770Sstevel@tonic-gate
19780Sstevel@tonic-gate if ((ret = mktime(tmp)) == (time_t)-1) {
19790Sstevel@tonic-gate if (errno == EOVERFLOW) {
19800Sstevel@tonic-gate return ((time_t)-1);
19810Sstevel@tonic-gate }
19820Sstevel@tonic-gate crabort("internal error: mktime failed",
19835558Sbasabi REMOVE_FIFO|CONSOLE_MSG);
19840Sstevel@tonic-gate }
19850Sstevel@tonic-gate return (ret);
19860Sstevel@tonic-gate }
19870Sstevel@tonic-gate
19880Sstevel@tonic-gate #define DUMMY 100
19890Sstevel@tonic-gate
19900Sstevel@tonic-gate static int
next_ge(int current,char * list)19910Sstevel@tonic-gate next_ge(int current, char *list)
19920Sstevel@tonic-gate {
19930Sstevel@tonic-gate /*
19940Sstevel@tonic-gate * list is a character field as in a crontab file;
19950Sstevel@tonic-gate * for example: "40, 20, 50-10"
19960Sstevel@tonic-gate * next_ge returns the next number in the list that is
19970Sstevel@tonic-gate * greater than or equal to current. if no numbers of list
19980Sstevel@tonic-gate * are >= current, the smallest element of list is returned.
19990Sstevel@tonic-gate * NOTE: current must be in the appropriate range.
20000Sstevel@tonic-gate */
20010Sstevel@tonic-gate
20020Sstevel@tonic-gate char *ptr;
20030Sstevel@tonic-gate int n, n2, min, min_gt;
20040Sstevel@tonic-gate
20050Sstevel@tonic-gate if (strcmp(list, "*") == 0)
20060Sstevel@tonic-gate return (current);
20070Sstevel@tonic-gate ptr = list;
20080Sstevel@tonic-gate min = DUMMY;
20090Sstevel@tonic-gate min_gt = DUMMY;
20100Sstevel@tonic-gate for (;;) {
20110Sstevel@tonic-gate if ((n = (int)num(&ptr)) == current)
20120Sstevel@tonic-gate return (current);
20130Sstevel@tonic-gate if (n < min)
20140Sstevel@tonic-gate min = n;
20150Sstevel@tonic-gate if ((n > current) && (n < min_gt))
20160Sstevel@tonic-gate min_gt = n;
20170Sstevel@tonic-gate if (*ptr == '-') {
20180Sstevel@tonic-gate ptr++;
20190Sstevel@tonic-gate if ((n2 = (int)num(&ptr)) > n) {
20200Sstevel@tonic-gate if ((current > n) && (current <= n2))
20210Sstevel@tonic-gate return (current);
20220Sstevel@tonic-gate } else { /* range that wraps around */
20230Sstevel@tonic-gate if (current > n)
20240Sstevel@tonic-gate return (current);
20250Sstevel@tonic-gate if (current <= n2)
20260Sstevel@tonic-gate return (current);
20270Sstevel@tonic-gate }
20280Sstevel@tonic-gate }
20290Sstevel@tonic-gate if (*ptr == '\0')
20300Sstevel@tonic-gate break;
20310Sstevel@tonic-gate ptr += 1;
20320Sstevel@tonic-gate }
20330Sstevel@tonic-gate if (min_gt != DUMMY)
20340Sstevel@tonic-gate return (min_gt);
20350Sstevel@tonic-gate else
20360Sstevel@tonic-gate return (min);
20370Sstevel@tonic-gate }
20380Sstevel@tonic-gate
20390Sstevel@tonic-gate static void
free_if_unused(struct usr * u)20400Sstevel@tonic-gate free_if_unused(struct usr *u)
20410Sstevel@tonic-gate {
20420Sstevel@tonic-gate struct usr *cur, *prev;
20430Sstevel@tonic-gate /*
20440Sstevel@tonic-gate * To make sure a usr structure is idle we must check that
20450Sstevel@tonic-gate * there are no at jobs queued for the user; the user does
20460Sstevel@tonic-gate * not have a crontab, and also that there are no running at
20470Sstevel@tonic-gate * or cron jobs (since the runinfo structure also has a
20480Sstevel@tonic-gate * pointer to the usr structure).
20490Sstevel@tonic-gate */
20500Sstevel@tonic-gate if (!u->ctexists && u->atevents == NULL &&
20510Sstevel@tonic-gate u->cruncnt == 0 && u->aruncnt == 0) {
20520Sstevel@tonic-gate #ifdef DEBUG
20530Sstevel@tonic-gate (void) fprintf(stderr, "%s removed from usr list\n", u->name);
20540Sstevel@tonic-gate #endif
20550Sstevel@tonic-gate for (cur = uhead, prev = NULL;
20565558Sbasabi cur != u;
20575558Sbasabi prev = cur, cur = cur->nextusr) {
20580Sstevel@tonic-gate if (cur == NULL) {
20590Sstevel@tonic-gate return;
20600Sstevel@tonic-gate }
20610Sstevel@tonic-gate }
20620Sstevel@tonic-gate
20630Sstevel@tonic-gate if (prev == NULL)
20640Sstevel@tonic-gate uhead = u->nextusr;
20650Sstevel@tonic-gate else
20660Sstevel@tonic-gate prev->nextusr = u->nextusr;
20670Sstevel@tonic-gate free(u->name);
20680Sstevel@tonic-gate free(u->home);
20690Sstevel@tonic-gate free(u);
20700Sstevel@tonic-gate }
20710Sstevel@tonic-gate }
20720Sstevel@tonic-gate
20730Sstevel@tonic-gate static void
del_atjob(char * name,char * usrname)20740Sstevel@tonic-gate del_atjob(char *name, char *usrname)
20750Sstevel@tonic-gate {
20760Sstevel@tonic-gate
20770Sstevel@tonic-gate struct event *e, *eprev;
20780Sstevel@tonic-gate struct usr *u;
20790Sstevel@tonic-gate
20800Sstevel@tonic-gate if ((u = find_usr(usrname)) == NULL)
20810Sstevel@tonic-gate return;
20820Sstevel@tonic-gate e = u->atevents;
20830Sstevel@tonic-gate eprev = NULL;
20840Sstevel@tonic-gate while (e != NULL) {
20850Sstevel@tonic-gate if (strcmp(name, e->cmd) == 0) {
20860Sstevel@tonic-gate if (next_event == e)
20870Sstevel@tonic-gate next_event = NULL;
20880Sstevel@tonic-gate if (eprev == NULL)
20890Sstevel@tonic-gate u->atevents = e->link;
20900Sstevel@tonic-gate else
20910Sstevel@tonic-gate eprev->link = e->link;
20920Sstevel@tonic-gate el_remove(e->of.at.eventid, 1);
20930Sstevel@tonic-gate free(e->cmd);
20940Sstevel@tonic-gate free(e);
20950Sstevel@tonic-gate break;
20960Sstevel@tonic-gate } else {
20970Sstevel@tonic-gate eprev = e;
20980Sstevel@tonic-gate e = e->link;
20990Sstevel@tonic-gate }
21000Sstevel@tonic-gate }
21010Sstevel@tonic-gate
21020Sstevel@tonic-gate free_if_unused(u);
21030Sstevel@tonic-gate }
21040Sstevel@tonic-gate
21050Sstevel@tonic-gate static void
del_ctab(char * name)21060Sstevel@tonic-gate del_ctab(char *name)
21070Sstevel@tonic-gate {
21080Sstevel@tonic-gate
21090Sstevel@tonic-gate struct usr *u;
21100Sstevel@tonic-gate
21110Sstevel@tonic-gate if ((u = find_usr(name)) == NULL)
21120Sstevel@tonic-gate return;
21130Sstevel@tonic-gate rm_ctevents(u);
21140Sstevel@tonic-gate el_remove(u->ctid, 0);
21150Sstevel@tonic-gate u->ctid = 0;
21160Sstevel@tonic-gate u->ctexists = 0;
21170Sstevel@tonic-gate
21180Sstevel@tonic-gate free_if_unused(u);
21190Sstevel@tonic-gate }
21200Sstevel@tonic-gate
21210Sstevel@tonic-gate static void
rm_ctevents(struct usr * u)21220Sstevel@tonic-gate rm_ctevents(struct usr *u)
21230Sstevel@tonic-gate {
21240Sstevel@tonic-gate struct event *e2, *e3;
21250Sstevel@tonic-gate
21260Sstevel@tonic-gate /*
21270Sstevel@tonic-gate * see if the next event (to be run by cron) is a cronevent
21280Sstevel@tonic-gate * owned by this user.
21290Sstevel@tonic-gate */
21300Sstevel@tonic-gate
21310Sstevel@tonic-gate if ((next_event != NULL) &&
21320Sstevel@tonic-gate (next_event->etype == CRONEVENT) &&
21330Sstevel@tonic-gate (next_event->u == u)) {
21340Sstevel@tonic-gate next_event = NULL;
21350Sstevel@tonic-gate }
21360Sstevel@tonic-gate e2 = u->ctevents;
21370Sstevel@tonic-gate while (e2 != NULL) {
21380Sstevel@tonic-gate free(e2->cmd);
21398439SChris.Gerhard@sun.com rel_shared(e2->of.ct.tz);
21408439SChris.Gerhard@sun.com rel_shared(e2->of.ct.shell);
21418439SChris.Gerhard@sun.com rel_shared(e2->of.ct.home);
21420Sstevel@tonic-gate free(e2->of.ct.minute);
21430Sstevel@tonic-gate free(e2->of.ct.hour);
21440Sstevel@tonic-gate free(e2->of.ct.daymon);
21450Sstevel@tonic-gate free(e2->of.ct.month);
21460Sstevel@tonic-gate free(e2->of.ct.dayweek);
21470Sstevel@tonic-gate if (e2->of.ct.input != NULL)
21480Sstevel@tonic-gate free(e2->of.ct.input);
21490Sstevel@tonic-gate e3 = e2->link;
21500Sstevel@tonic-gate free(e2);
21510Sstevel@tonic-gate e2 = e3;
21520Sstevel@tonic-gate }
21530Sstevel@tonic-gate u->ctevents = NULL;
21540Sstevel@tonic-gate }
21550Sstevel@tonic-gate
21560Sstevel@tonic-gate
21570Sstevel@tonic-gate static struct usr *
find_usr(char * uname)21580Sstevel@tonic-gate find_usr(char *uname)
21590Sstevel@tonic-gate {
21600Sstevel@tonic-gate struct usr *u;
21610Sstevel@tonic-gate
21620Sstevel@tonic-gate u = uhead;
21630Sstevel@tonic-gate while (u != NULL) {
21640Sstevel@tonic-gate if (strcmp(u->name, uname) == 0)
21650Sstevel@tonic-gate return (u);
21660Sstevel@tonic-gate u = u->nextusr;
21670Sstevel@tonic-gate }
21680Sstevel@tonic-gate return (NULL);
21690Sstevel@tonic-gate }
21700Sstevel@tonic-gate
21710Sstevel@tonic-gate /*
21720Sstevel@tonic-gate * Execute cron command or at/batch job.
21730Sstevel@tonic-gate * If ever a premature return is added to this function pay attention to
21740Sstevel@tonic-gate * free at_cmdfile and outfile plus jobname buffers of the runinfo structure.
21750Sstevel@tonic-gate */
21760Sstevel@tonic-gate static int
ex(struct event * e)21770Sstevel@tonic-gate ex(struct event *e)
21780Sstevel@tonic-gate {
21790Sstevel@tonic-gate int r;
21800Sstevel@tonic-gate int fd;
21810Sstevel@tonic-gate pid_t rfork;
21820Sstevel@tonic-gate FILE *atcmdfp;
21830Sstevel@tonic-gate char mailvar[4];
21840Sstevel@tonic-gate char *at_cmdfile = NULL;
21850Sstevel@tonic-gate struct stat buf;
21860Sstevel@tonic-gate struct queue *qp;
21870Sstevel@tonic-gate struct runinfo *rp;
21880Sstevel@tonic-gate struct project proj, *pproj = NULL;
21898439SChris.Gerhard@sun.com union {
21908439SChris.Gerhard@sun.com struct {
21918439SChris.Gerhard@sun.com char buf[PROJECT_BUFSZ];
21928439SChris.Gerhard@sun.com char buf2[PROJECT_BUFSZ];
21938439SChris.Gerhard@sun.com } p;
21948439SChris.Gerhard@sun.com char error[CANT_STR_LEN + PATH_MAX];
21958439SChris.Gerhard@sun.com } bufs;
21960Sstevel@tonic-gate char *tmpfile;
21970Sstevel@tonic-gate FILE *fptr;
21980Sstevel@tonic-gate time_t dhltime;
21990Sstevel@tonic-gate projid_t projid;
22000Sstevel@tonic-gate int projflag = 0;
22018439SChris.Gerhard@sun.com char *home;
22028439SChris.Gerhard@sun.com char *sh;
22030Sstevel@tonic-gate
22040Sstevel@tonic-gate qp = &qt[e->etype]; /* set pointer to queue defs */
22050Sstevel@tonic-gate if (qp->nrun >= qp->njob) {
22065558Sbasabi msg("%c queue max run limit reached", e->etype + 'a');
22070Sstevel@tonic-gate resched(qp->nwait);
22080Sstevel@tonic-gate return (0);
22090Sstevel@tonic-gate }
2210*11115SNobutomo.Nakano@Sun.COM
2211801Sbasabi rp = rinfo_get(0); /* allocating a new runinfo struct */
22120Sstevel@tonic-gate
22130Sstevel@tonic-gate /*
22140Sstevel@tonic-gate * the tempnam() function uses malloc(3C) to allocate space for the
22150Sstevel@tonic-gate * constructed file name, and returns a pointer to this area, which
22160Sstevel@tonic-gate * is assigned to rp->outfile. Here rp->outfile is not overwritten.
22170Sstevel@tonic-gate */
22180Sstevel@tonic-gate
22190Sstevel@tonic-gate rp->outfile = tempnam(TMPDIR, PFX);
22200Sstevel@tonic-gate rp->jobtype = e->etype;
22210Sstevel@tonic-gate if (e->etype == CRONEVENT) {
22225558Sbasabi rp->jobname = xmalloc(strlen(e->cmd) + 1);
22230Sstevel@tonic-gate (void) strcpy(rp->jobname, e->cmd);
22240Sstevel@tonic-gate /* "cron" jobs only produce mail if there's output */
22250Sstevel@tonic-gate rp->mailwhendone = 0;
22260Sstevel@tonic-gate } else {
22275558Sbasabi at_cmdfile = xmalloc(strlen(ATDIR) + strlen(e->cmd) + 2);
22280Sstevel@tonic-gate (void) sprintf(at_cmdfile, "%s/%s", ATDIR, e->cmd);
22290Sstevel@tonic-gate if ((atcmdfp = fopen(at_cmdfile, "r")) == NULL) {
22300Sstevel@tonic-gate if (errno == ENAMETOOLONG) {
22310Sstevel@tonic-gate if (chdir(ATDIR) == 0)
22320Sstevel@tonic-gate cron_unlink(e->cmd);
22330Sstevel@tonic-gate } else {
22340Sstevel@tonic-gate cron_unlink(at_cmdfile);
22350Sstevel@tonic-gate }
22360Sstevel@tonic-gate mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECAT);
22370Sstevel@tonic-gate free(at_cmdfile);
22380Sstevel@tonic-gate rinfo_free(rp);
22390Sstevel@tonic-gate return (0);
22400Sstevel@tonic-gate }
22415558Sbasabi rp->jobname = xmalloc(strlen(at_cmdfile) + 1);
22420Sstevel@tonic-gate (void) strcpy(rp->jobname, at_cmdfile);
22430Sstevel@tonic-gate
22440Sstevel@tonic-gate /*
22450Sstevel@tonic-gate * Skip over the first two lines.
22460Sstevel@tonic-gate */
22470Sstevel@tonic-gate (void) fscanf(atcmdfp, "%*[^\n]\n");
22480Sstevel@tonic-gate (void) fscanf(atcmdfp, "%*[^\n]\n");
22490Sstevel@tonic-gate if (fscanf(atcmdfp, ": notify by mail: %3s%*[^\n]\n",
22505558Sbasabi mailvar) == 1) {
22510Sstevel@tonic-gate /*
22520Sstevel@tonic-gate * Check to see if we should always send mail
22530Sstevel@tonic-gate * to the owner.
22540Sstevel@tonic-gate */
22550Sstevel@tonic-gate rp->mailwhendone = (strcmp(mailvar, "yes") == 0);
22560Sstevel@tonic-gate } else {
22570Sstevel@tonic-gate rp->mailwhendone = 0;
22580Sstevel@tonic-gate }
22590Sstevel@tonic-gate
22600Sstevel@tonic-gate if (fscanf(atcmdfp, "\n: project: %d\n", &projid) == 1) {
22610Sstevel@tonic-gate projflag = 1;
22620Sstevel@tonic-gate }
22630Sstevel@tonic-gate (void) fclose(atcmdfp);
22640Sstevel@tonic-gate }
22650Sstevel@tonic-gate
22660Sstevel@tonic-gate /*
22670Sstevel@tonic-gate * we make sure that the system time
22680Sstevel@tonic-gate * hasn't drifted backwards. if it has, el_add() is now
22690Sstevel@tonic-gate * called, to make sure that the event queue is back in order,
22700Sstevel@tonic-gate * and we set the delayed flag. cron will pick up the request
22710Sstevel@tonic-gate * later on at the proper time.
22720Sstevel@tonic-gate */
22730Sstevel@tonic-gate dhltime = time(NULL);
22740Sstevel@tonic-gate if ((dhltime - e->time) < 0) {
22750Sstevel@tonic-gate msg("clock time drifted backwards!\n");
22760Sstevel@tonic-gate if (next_event->etype == CRONEVENT) {
22770Sstevel@tonic-gate msg("correcting cron event\n");
22780Sstevel@tonic-gate next_event->time = next_time(next_event, dhltime);
22797879SSerge.Dussud@Sun.COM switch (el_add(next_event, next_event->time,
22807879SSerge.Dussud@Sun.COM (next_event->u)->ctid)) {
22817879SSerge.Dussud@Sun.COM case -1:
22827879SSerge.Dussud@Sun.COM ignore_msg("ex", "cron", next_event);
22837879SSerge.Dussud@Sun.COM break;
22847879SSerge.Dussud@Sun.COM case -2: /* event time lower than init time */
22857879SSerge.Dussud@Sun.COM reset_needed = 1;
22867879SSerge.Dussud@Sun.COM break;
22877879SSerge.Dussud@Sun.COM }
22880Sstevel@tonic-gate } else { /* etype == ATEVENT */
22890Sstevel@tonic-gate msg("correcting batch event\n");
22907879SSerge.Dussud@Sun.COM if (el_add(next_event, next_event->time,
22917879SSerge.Dussud@Sun.COM next_event->of.at.eventid) < 0) {
22927879SSerge.Dussud@Sun.COM ignore_msg("ex", "at", next_event);
22937879SSerge.Dussud@Sun.COM }
22940Sstevel@tonic-gate }
22950Sstevel@tonic-gate delayed++;
22960Sstevel@tonic-gate t_old = time(NULL);
22970Sstevel@tonic-gate free(at_cmdfile);
22980Sstevel@tonic-gate rinfo_free(rp);
22990Sstevel@tonic-gate return (0);
23000Sstevel@tonic-gate }
23010Sstevel@tonic-gate
23020Sstevel@tonic-gate if ((rfork = fork()) == (pid_t)-1) {
23030Sstevel@tonic-gate reap_child();
23040Sstevel@tonic-gate if ((rfork = fork()) == (pid_t)-1) {
23050Sstevel@tonic-gate msg("cannot fork");
23060Sstevel@tonic-gate free(at_cmdfile);
23070Sstevel@tonic-gate rinfo_free(rp);
23080Sstevel@tonic-gate resched(60);
23090Sstevel@tonic-gate (void) sleep(30);
23100Sstevel@tonic-gate return (0);
23110Sstevel@tonic-gate }
23120Sstevel@tonic-gate }
23130Sstevel@tonic-gate if (rfork) { /* parent process */
23140Sstevel@tonic-gate contract_abandon_latest(rfork);
23150Sstevel@tonic-gate
23160Sstevel@tonic-gate ++qp->nrun;
23170Sstevel@tonic-gate rp->pid = rfork;
23180Sstevel@tonic-gate rp->que = e->etype;
23190Sstevel@tonic-gate if (e->etype != CRONEVENT)
23200Sstevel@tonic-gate (e->u)->aruncnt++;
23210Sstevel@tonic-gate else
23220Sstevel@tonic-gate (e->u)->cruncnt++;
23230Sstevel@tonic-gate rp->rusr = (e->u);
23240Sstevel@tonic-gate logit(BCHAR, rp, 0);
23250Sstevel@tonic-gate free(at_cmdfile);
23260Sstevel@tonic-gate
23270Sstevel@tonic-gate return (0);
23280Sstevel@tonic-gate }
23290Sstevel@tonic-gate
23300Sstevel@tonic-gate child_sigreset();
23310Sstevel@tonic-gate contract_clear_template();
23320Sstevel@tonic-gate
23330Sstevel@tonic-gate if (e->etype != CRONEVENT) {
23340Sstevel@tonic-gate /* open jobfile as stdin to shell */
23350Sstevel@tonic-gate if (stat(at_cmdfile, &buf)) {
23360Sstevel@tonic-gate if (errno == ENAMETOOLONG) {
23370Sstevel@tonic-gate if (chdir(ATDIR) == 0)
23380Sstevel@tonic-gate cron_unlink(e->cmd);
23390Sstevel@tonic-gate } else
23400Sstevel@tonic-gate cron_unlink(at_cmdfile);
23410Sstevel@tonic-gate mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
23420Sstevel@tonic-gate exit(1);
23430Sstevel@tonic-gate }
23440Sstevel@tonic-gate if (!(buf.st_mode&ISUID)) {
23450Sstevel@tonic-gate /*
23460Sstevel@tonic-gate * if setuid bit off, original owner has
23470Sstevel@tonic-gate * given this file to someone else
23480Sstevel@tonic-gate */
23490Sstevel@tonic-gate cron_unlink(at_cmdfile);
23500Sstevel@tonic-gate exit(1);
23510Sstevel@tonic-gate }
23520Sstevel@tonic-gate if ((fd = open(at_cmdfile, O_RDONLY)) == -1) {
23530Sstevel@tonic-gate mail((e->u)->name, BADJOBOPEN, ERR_CANTEXECCRON);
23540Sstevel@tonic-gate cron_unlink(at_cmdfile);
23550Sstevel@tonic-gate exit(1);
23560Sstevel@tonic-gate }
23570Sstevel@tonic-gate if (fd != 0) {
23580Sstevel@tonic-gate (void) dup2(fd, 0);
23590Sstevel@tonic-gate (void) close(fd);
23600Sstevel@tonic-gate }
23610Sstevel@tonic-gate /*
23620Sstevel@tonic-gate * retrieve the project id of the at job and convert it
23630Sstevel@tonic-gate * to a project name. fail if it's not a valid project
23640Sstevel@tonic-gate * or if the user isn't a member of the project.
23650Sstevel@tonic-gate */
23660Sstevel@tonic-gate if (projflag == 1) {
23670Sstevel@tonic-gate if ((pproj = getprojbyid(projid, &proj,
23688439SChris.Gerhard@sun.com (void *)&bufs.p.buf,
23698439SChris.Gerhard@sun.com sizeof (bufs.p.buf))) == NULL ||
23700Sstevel@tonic-gate !inproj(e->u->name, pproj->pj_name,
23718439SChris.Gerhard@sun.com bufs.p.buf2, sizeof (bufs.p.buf2))) {
23720Sstevel@tonic-gate cron_unlink(at_cmdfile);
23730Sstevel@tonic-gate mail((e->u)->name, BADPROJID, ERR_CANTEXECAT);
23740Sstevel@tonic-gate exit(1);
23750Sstevel@tonic-gate }
23760Sstevel@tonic-gate }
23770Sstevel@tonic-gate }
23780Sstevel@tonic-gate
23790Sstevel@tonic-gate /*
23800Sstevel@tonic-gate * Put process in a new session, and create a new task.
23810Sstevel@tonic-gate */
23820Sstevel@tonic-gate if (setsid() < 0) {
23830Sstevel@tonic-gate msg("setsid failed with errno = %d. job failed (%s)"
23840Sstevel@tonic-gate " for user %s", errno, e->cmd, e->u->name);
23850Sstevel@tonic-gate if (e->etype != CRONEVENT)
23860Sstevel@tonic-gate cron_unlink(at_cmdfile);
23870Sstevel@tonic-gate exit(1);
23880Sstevel@tonic-gate }
23890Sstevel@tonic-gate
23900Sstevel@tonic-gate /*
23910Sstevel@tonic-gate * set correct user identification and check his account
23920Sstevel@tonic-gate */
23930Sstevel@tonic-gate r = set_user_cred(e->u, pproj);
23940Sstevel@tonic-gate if (r == VUC_EXPIRED) {
23950Sstevel@tonic-gate msg("user (%s) account is expired", e->u->name);
23960Sstevel@tonic-gate audit_cron_user_acct_expired(e->u->name);
23970Sstevel@tonic-gate clean_out_user(e->u);
23980Sstevel@tonic-gate exit(1);
23990Sstevel@tonic-gate }
24000Sstevel@tonic-gate if (r == VUC_NEW_AUTH) {
24010Sstevel@tonic-gate msg("user (%s) password has expired", e->u->name);
24020Sstevel@tonic-gate audit_cron_user_acct_expired(e->u->name);
24030Sstevel@tonic-gate clean_out_user(e->u);
24040Sstevel@tonic-gate exit(1);
24050Sstevel@tonic-gate }
24060Sstevel@tonic-gate if (r != VUC_OK) {
24070Sstevel@tonic-gate msg("bad user (%s)", e->u->name);
24080Sstevel@tonic-gate audit_cron_bad_user(e->u->name);
24090Sstevel@tonic-gate clean_out_user(e->u);
24100Sstevel@tonic-gate exit(1);
24110Sstevel@tonic-gate }
24120Sstevel@tonic-gate /*
24130Sstevel@tonic-gate * check user and initialize the supplementary group access list.
24140Sstevel@tonic-gate * bugid 1230784: deleted from parent to avoid cron hang. Now
24150Sstevel@tonic-gate * only child handles the call.
24160Sstevel@tonic-gate */
24170Sstevel@tonic-gate
24180Sstevel@tonic-gate if (verify_user_cred(e->u) != VUC_OK ||
24190Sstevel@tonic-gate setgid(e->u->gid) == -1 ||
24200Sstevel@tonic-gate initgroups(e->u->name, e->u->gid) == -1) {
24210Sstevel@tonic-gate msg("bad user (%s) or setgid failed (%s)",
24225558Sbasabi e->u->name, e->u->name);
24230Sstevel@tonic-gate audit_cron_bad_user(e->u->name);
24240Sstevel@tonic-gate clean_out_user(e->u);
24250Sstevel@tonic-gate exit(1);
24260Sstevel@tonic-gate }
24270Sstevel@tonic-gate
24286858Sjc144527 if ((e->u)->uid == 0) { /* set default path */
24296858Sjc144527 /* path settable in defaults file */
24306858Sjc144527 envinit[2] = supath;
24316858Sjc144527 } else {
24326858Sjc144527 envinit[2] = path;
24336858Sjc144527 }
24346858Sjc144527
24350Sstevel@tonic-gate if (e->etype != CRONEVENT) {
24360Sstevel@tonic-gate r = audit_cron_session(e->u->name, NULL,
24375558Sbasabi e->u->uid, e->u->gid, at_cmdfile);
24380Sstevel@tonic-gate cron_unlink(at_cmdfile);
24390Sstevel@tonic-gate } else {
24400Sstevel@tonic-gate r = audit_cron_session(e->u->name, CRONDIR,
24415558Sbasabi e->u->uid, e->u->gid, NULL);
24420Sstevel@tonic-gate }
24430Sstevel@tonic-gate if (r != 0) {
24440Sstevel@tonic-gate msg("cron audit problem. job failed (%s) for user %s",
24455558Sbasabi e->cmd, e->u->name);
24460Sstevel@tonic-gate exit(1);
24470Sstevel@tonic-gate }
24480Sstevel@tonic-gate
24490Sstevel@tonic-gate audit_cron_new_job(e->cmd, e->etype, (void *)e);
24500Sstevel@tonic-gate
24510Sstevel@tonic-gate if (setuid(e->u->uid) == -1) {
24520Sstevel@tonic-gate msg("setuid failed (%s)", e->u->name);
24530Sstevel@tonic-gate clean_out_user(e->u);
24540Sstevel@tonic-gate exit(1);
24550Sstevel@tonic-gate }
24560Sstevel@tonic-gate
24570Sstevel@tonic-gate if (e->etype == CRONEVENT) {
24580Sstevel@tonic-gate /* check for standard input to command */
24590Sstevel@tonic-gate if (e->of.ct.input != NULL) {
24600Sstevel@tonic-gate if ((tmpfile = strdup(TMPINFILE)) == NULL) {
24610Sstevel@tonic-gate mail((e->u)->name, MALLOCERR,
24625558Sbasabi ERR_CANTEXECCRON);
24630Sstevel@tonic-gate exit(1);
24640Sstevel@tonic-gate }
24650Sstevel@tonic-gate if ((fd = mkstemp(tmpfile)) == -1 ||
24665558Sbasabi (fptr = fdopen(fd, "w")) == NULL) {
24670Sstevel@tonic-gate mail((e->u)->name, NOSTDIN,
24685558Sbasabi ERR_CANTEXECCRON);
24690Sstevel@tonic-gate cron_unlink(tmpfile);
24700Sstevel@tonic-gate free(tmpfile);
24710Sstevel@tonic-gate exit(1);
24720Sstevel@tonic-gate }
24730Sstevel@tonic-gate if ((fwrite(e->of.ct.input, sizeof (char),
24745558Sbasabi strlen(e->of.ct.input), fptr)) !=
24755558Sbasabi strlen(e->of.ct.input)) {
24760Sstevel@tonic-gate mail((e->u)->name, NOSTDIN, ERR_CANTEXECCRON);
24770Sstevel@tonic-gate cron_unlink(tmpfile);
24780Sstevel@tonic-gate free(tmpfile);
24790Sstevel@tonic-gate (void) close(fd);
24800Sstevel@tonic-gate (void) fclose(fptr);
24810Sstevel@tonic-gate exit(1);
24820Sstevel@tonic-gate }
24830Sstevel@tonic-gate if (fseek(fptr, (off_t)0, SEEK_SET) != -1) {
24840Sstevel@tonic-gate if (fd != 0) {
24850Sstevel@tonic-gate (void) dup2(fd, 0);
24860Sstevel@tonic-gate (void) close(fd);
24870Sstevel@tonic-gate }
24880Sstevel@tonic-gate }
24890Sstevel@tonic-gate cron_unlink(tmpfile);
24900Sstevel@tonic-gate free(tmpfile);
24910Sstevel@tonic-gate (void) fclose(fptr);
24920Sstevel@tonic-gate } else if ((fd = open("/dev/null", O_RDONLY)) > 0) {
24930Sstevel@tonic-gate (void) dup2(fd, 0);
24940Sstevel@tonic-gate (void) close(fd);
24950Sstevel@tonic-gate }
24960Sstevel@tonic-gate }
24970Sstevel@tonic-gate
24980Sstevel@tonic-gate /* redirect stdout and stderr for the shell */
24990Sstevel@tonic-gate if ((fd = open(rp->outfile, O_WRONLY|O_CREAT|O_EXCL, OUTMODE)) == 1)
25000Sstevel@tonic-gate fd = open("/dev/null", O_WRONLY);
25010Sstevel@tonic-gate
25020Sstevel@tonic-gate if (fd >= 0 && fd != 1)
25030Sstevel@tonic-gate (void) dup2(fd, 1);
25040Sstevel@tonic-gate
25050Sstevel@tonic-gate if (fd >= 0 && fd != 2) {
25060Sstevel@tonic-gate (void) dup2(fd, 2);
25070Sstevel@tonic-gate if (fd != 1)
25080Sstevel@tonic-gate (void) close(fd);
25090Sstevel@tonic-gate }
25100Sstevel@tonic-gate
25118439SChris.Gerhard@sun.com if (e->etype == CRONEVENT && e->of.ct.home != NULL) {
25128439SChris.Gerhard@sun.com home = (char *)get_obj(e->of.ct.home);
25138439SChris.Gerhard@sun.com } else {
25148439SChris.Gerhard@sun.com home = (e->u)->home;
25158439SChris.Gerhard@sun.com }
25168439SChris.Gerhard@sun.com (void) strlcat(homedir, home, sizeof (homedir));
25170Sstevel@tonic-gate (void) strlcat(logname, (e->u)->name, sizeof (logname));
25180Sstevel@tonic-gate environ = envinit;
25198439SChris.Gerhard@sun.com if (chdir(home) == -1) {
25208439SChris.Gerhard@sun.com snprintf(bufs.error, sizeof (bufs.error), CANTCDHOME, home);
25218439SChris.Gerhard@sun.com mail((e->u)->name, bufs.error,
25225558Sbasabi e->etype == CRONEVENT ? ERR_CANTEXECCRON :
25235558Sbasabi ERR_CANTEXECAT);
25240Sstevel@tonic-gate exit(1);
25250Sstevel@tonic-gate }
25260Sstevel@tonic-gate #ifdef TESTING
25270Sstevel@tonic-gate exit(1);
25280Sstevel@tonic-gate #endif
25290Sstevel@tonic-gate /*
25300Sstevel@tonic-gate * make sure that all file descriptors EXCEPT 0, 1 and 2
25310Sstevel@tonic-gate * will be closed.
25320Sstevel@tonic-gate */
25330Sstevel@tonic-gate closefrom(3);
25340Sstevel@tonic-gate
25350Sstevel@tonic-gate if ((e->u)->uid != 0)
25360Sstevel@tonic-gate (void) nice(qp->nice);
25378439SChris.Gerhard@sun.com if (e->etype == CRONEVENT) {
25388439SChris.Gerhard@sun.com if (e->of.ct.tz) {
25398439SChris.Gerhard@sun.com (void) putenv((char *)get_obj(e->of.ct.tz));
25408439SChris.Gerhard@sun.com }
25418439SChris.Gerhard@sun.com if (e->of.ct.shell) {
25428439SChris.Gerhard@sun.com char *name;
25438439SChris.Gerhard@sun.com
25448439SChris.Gerhard@sun.com sh = (char *)get_obj(e->of.ct.shell);
25458439SChris.Gerhard@sun.com name = strrchr(sh, '/');
25468439SChris.Gerhard@sun.com if (name == NULL)
25478439SChris.Gerhard@sun.com name = sh;
25488439SChris.Gerhard@sun.com else
25498439SChris.Gerhard@sun.com name++;
25508439SChris.Gerhard@sun.com
25518439SChris.Gerhard@sun.com (void) putenv(sh);
25528439SChris.Gerhard@sun.com sh += strlen(ENV_SHELL);
25538439SChris.Gerhard@sun.com (void) execl(sh, name, "-c", e->cmd, 0);
25548439SChris.Gerhard@sun.com } else {
25558439SChris.Gerhard@sun.com (void) execl(SHELL, "sh", "-c", e->cmd, 0);
25568439SChris.Gerhard@sun.com sh = SHELL;
25578439SChris.Gerhard@sun.com }
25588439SChris.Gerhard@sun.com } else { /* type == ATEVENT */
25590Sstevel@tonic-gate (void) execl(SHELL, "sh", 0);
25608439SChris.Gerhard@sun.com sh = SHELL;
25618439SChris.Gerhard@sun.com }
25628439SChris.Gerhard@sun.com snprintf(bufs.error, sizeof (bufs.error), CANTEXECSH, sh);
25638439SChris.Gerhard@sun.com mail((e->u)->name, bufs.error,
25645558Sbasabi e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT);
25650Sstevel@tonic-gate exit(1);
25660Sstevel@tonic-gate /*NOTREACHED*/
25670Sstevel@tonic-gate }
25680Sstevel@tonic-gate
2569*11115SNobutomo.Nakano@Sun.COM /*
2570*11115SNobutomo.Nakano@Sun.COM * Main idle loop.
2571*11115SNobutomo.Nakano@Sun.COM * When timed out to run the job, return 0.
2572*11115SNobutomo.Nakano@Sun.COM * If for some reasons we need to reschedule jobs, return 1.
2573*11115SNobutomo.Nakano@Sun.COM */
25740Sstevel@tonic-gate static int
idle(long t)25750Sstevel@tonic-gate idle(long t)
25760Sstevel@tonic-gate {
25770Sstevel@tonic-gate time_t now;
25780Sstevel@tonic-gate
2579*11115SNobutomo.Nakano@Sun.COM refresh = 0;
2580*11115SNobutomo.Nakano@Sun.COM
25810Sstevel@tonic-gate while (t > 0L) {
25820Sstevel@tonic-gate if (msg_wait(t) != 0) {
25830Sstevel@tonic-gate /* we need to run next job immediately */
25840Sstevel@tonic-gate return (0);
25850Sstevel@tonic-gate }
25860Sstevel@tonic-gate
25870Sstevel@tonic-gate reap_child();
25880Sstevel@tonic-gate
2589*11115SNobutomo.Nakano@Sun.COM if (refresh) {
2590*11115SNobutomo.Nakano@Sun.COM /* We got THAW or REFRESH message */
2591*11115SNobutomo.Nakano@Sun.COM return (1);
2592*11115SNobutomo.Nakano@Sun.COM }
2593*11115SNobutomo.Nakano@Sun.COM
25940Sstevel@tonic-gate now = time(NULL);
25950Sstevel@tonic-gate if (last_time > now) {
2596*11115SNobutomo.Nakano@Sun.COM /* clock has been reset to backward */
25970Sstevel@tonic-gate return (1);
25980Sstevel@tonic-gate }
25990Sstevel@tonic-gate
26000Sstevel@tonic-gate if (next_event == NULL && !el_empty()) {
26010Sstevel@tonic-gate next_event = (struct event *)el_first();
26020Sstevel@tonic-gate }
2603*11115SNobutomo.Nakano@Sun.COM
26040Sstevel@tonic-gate if (next_event == NULL)
26050Sstevel@tonic-gate t = INFINITY;
26060Sstevel@tonic-gate else
26070Sstevel@tonic-gate t = (long)next_event->time - now;
26080Sstevel@tonic-gate }
26090Sstevel@tonic-gate return (0);
26100Sstevel@tonic-gate }
26110Sstevel@tonic-gate
26120Sstevel@tonic-gate /*
26130Sstevel@tonic-gate * This used to be in the idle(), but moved to the separate function.
26140Sstevel@tonic-gate * This called from various place when cron needs to reap the
26150Sstevel@tonic-gate * child. It includes the situation that cron hit maxrun, and needs
26160Sstevel@tonic-gate * to reschedule the job.
26170Sstevel@tonic-gate */
26180Sstevel@tonic-gate static void
reap_child()26190Sstevel@tonic-gate reap_child()
26200Sstevel@tonic-gate {
26210Sstevel@tonic-gate pid_t pid;
26220Sstevel@tonic-gate int prc;
26230Sstevel@tonic-gate struct runinfo *rp;
26240Sstevel@tonic-gate
26250Sstevel@tonic-gate for (;;) {
26260Sstevel@tonic-gate pid = waitpid((pid_t)-1, &prc, WNOHANG);
26270Sstevel@tonic-gate if (pid <= 0)
26280Sstevel@tonic-gate break;
26290Sstevel@tonic-gate #ifdef DEBUG
26300Sstevel@tonic-gate fprintf(stderr,
26315558Sbasabi "wait returned %x for process %d\n", prc, pid);
26320Sstevel@tonic-gate #endif
26330Sstevel@tonic-gate if ((rp = rinfo_get(pid)) == NULL) {
26340Sstevel@tonic-gate if (miscpid_delete(pid) == 0) {
26350Sstevel@tonic-gate /* not found in anywhere */
26360Sstevel@tonic-gate msg(PIDERR, pid);
26370Sstevel@tonic-gate }
26380Sstevel@tonic-gate } else if (rp->que == ZOMB) {
26390Sstevel@tonic-gate (void) unlink(rp->outfile);
26400Sstevel@tonic-gate rinfo_free(rp);
26410Sstevel@tonic-gate } else {
26420Sstevel@tonic-gate cleanup(rp, prc);
26430Sstevel@tonic-gate }
26440Sstevel@tonic-gate }
26450Sstevel@tonic-gate }
26460Sstevel@tonic-gate
26470Sstevel@tonic-gate static void
cleanup(struct runinfo * pr,int rc)26480Sstevel@tonic-gate cleanup(struct runinfo *pr, int rc)
26490Sstevel@tonic-gate {
26500Sstevel@tonic-gate int nextfork = 1;
26510Sstevel@tonic-gate struct usr *p;
26520Sstevel@tonic-gate struct stat buf;
26530Sstevel@tonic-gate
26540Sstevel@tonic-gate logit(ECHAR, pr, rc);
26550Sstevel@tonic-gate --qt[pr->que].nrun;
26560Sstevel@tonic-gate p = pr->rusr;
26570Sstevel@tonic-gate if (pr->que != CRONEVENT)
26580Sstevel@tonic-gate --p->aruncnt;
26590Sstevel@tonic-gate else
26600Sstevel@tonic-gate --p->cruncnt;
26610Sstevel@tonic-gate
2662871Scasper if (lstat(pr->outfile, &buf) == 0) {
2663871Scasper if (!S_ISLNK(buf.st_mode) &&
26640Sstevel@tonic-gate (buf.st_size > 0 || pr->mailwhendone)) {
26650Sstevel@tonic-gate /* mail user stdout and stderr */
26660Sstevel@tonic-gate for (;;) {
26670Sstevel@tonic-gate if ((pr->pid = fork()) < 0) {
26680Sstevel@tonic-gate /*
26690Sstevel@tonic-gate * if fork fails try forever in doubling
26700Sstevel@tonic-gate * retry times, up to 16 seconds
26710Sstevel@tonic-gate */
26720Sstevel@tonic-gate (void) sleep(nextfork);
26730Sstevel@tonic-gate if (nextfork < 16)
26740Sstevel@tonic-gate nextfork += nextfork;
26750Sstevel@tonic-gate continue;
26760Sstevel@tonic-gate } else if (pr->pid == 0) {
26770Sstevel@tonic-gate child_sigreset();
26780Sstevel@tonic-gate contract_clear_template();
26790Sstevel@tonic-gate
26800Sstevel@tonic-gate mail_result(p, pr, buf.st_size);
26810Sstevel@tonic-gate /* NOTREACHED */
26820Sstevel@tonic-gate } else {
26830Sstevel@tonic-gate contract_abandon_latest(pr->pid);
26840Sstevel@tonic-gate pr->que = ZOMB;
26850Sstevel@tonic-gate break;
26860Sstevel@tonic-gate }
26870Sstevel@tonic-gate }
26880Sstevel@tonic-gate } else {
26890Sstevel@tonic-gate (void) unlink(pr->outfile);
26900Sstevel@tonic-gate rinfo_free(pr);
26910Sstevel@tonic-gate }
26920Sstevel@tonic-gate } else {
26930Sstevel@tonic-gate rinfo_free(pr);
26940Sstevel@tonic-gate }
26950Sstevel@tonic-gate
26960Sstevel@tonic-gate free_if_unused(p);
26970Sstevel@tonic-gate }
26980Sstevel@tonic-gate
26990Sstevel@tonic-gate /*
27000Sstevel@tonic-gate * Mail stdout and stderr of a job to user. Get uid for real user and become
27010Sstevel@tonic-gate * that person. We do this so that mail won't come from root since this
27020Sstevel@tonic-gate * could be a security hole. If failure, quit - don't send mail as root.
27030Sstevel@tonic-gate */
27040Sstevel@tonic-gate static void
mail_result(struct usr * p,struct runinfo * pr,size_t filesize)27050Sstevel@tonic-gate mail_result(struct usr *p, struct runinfo *pr, size_t filesize)
27060Sstevel@tonic-gate {
27070Sstevel@tonic-gate struct passwd *ruser_ids;
27080Sstevel@tonic-gate FILE *mailpipe;
27090Sstevel@tonic-gate FILE *st;
27100Sstevel@tonic-gate struct utsname name;
27110Sstevel@tonic-gate int nbytes;
27120Sstevel@tonic-gate char iobuf[BUFSIZ];
27130Sstevel@tonic-gate char *cmd;
27140Sstevel@tonic-gate
27150Sstevel@tonic-gate (void) uname(&name);
27160Sstevel@tonic-gate if ((ruser_ids = getpwnam(p->name)) == NULL)
27170Sstevel@tonic-gate exit(0);
27180Sstevel@tonic-gate (void) setuid(ruser_ids->pw_uid);
27190Sstevel@tonic-gate
27205558Sbasabi cmd = xmalloc(strlen(MAIL) + strlen(p->name)+2);
27210Sstevel@tonic-gate (void) sprintf(cmd, "%s %s", MAIL, p->name);
27220Sstevel@tonic-gate mailpipe = popen(cmd, "w");
27230Sstevel@tonic-gate free(cmd);
27240Sstevel@tonic-gate if (mailpipe == NULL)
27250Sstevel@tonic-gate exit(127);
27260Sstevel@tonic-gate (void) fprintf(mailpipe, "To: %s\n", p->name);
27270Sstevel@tonic-gate if (pr->jobtype == CRONEVENT) {
27280Sstevel@tonic-gate (void) fprintf(mailpipe, CRONOUT);
27290Sstevel@tonic-gate (void) fprintf(mailpipe, "Your \"cron\" job on %s\n",
27305558Sbasabi name.nodename);
27310Sstevel@tonic-gate if (pr->jobname != NULL) {
27320Sstevel@tonic-gate (void) fprintf(mailpipe, "%s\n\n", pr->jobname);
27330Sstevel@tonic-gate }
27340Sstevel@tonic-gate } else {
27350Sstevel@tonic-gate (void) fprintf(mailpipe, "Subject: Output from \"at\" job\n\n");
27360Sstevel@tonic-gate (void) fprintf(mailpipe, "Your \"at\" job on %s\n",
27375558Sbasabi name.nodename);
27380Sstevel@tonic-gate if (pr->jobname != NULL) {
27390Sstevel@tonic-gate (void) fprintf(mailpipe, "\"%s\"\n\n", pr->jobname);
27400Sstevel@tonic-gate }
27410Sstevel@tonic-gate }
27420Sstevel@tonic-gate /* Tmp. file is fopen'ed w/ "r", secure open */
27430Sstevel@tonic-gate if (filesize > 0 &&
27440Sstevel@tonic-gate (st = fopen(pr->outfile, "r")) != NULL) {
27450Sstevel@tonic-gate (void) fprintf(mailpipe,
27465558Sbasabi "produced the following output:\n\n");
27470Sstevel@tonic-gate while ((nbytes = fread(iobuf, sizeof (char), BUFSIZ, st)) != 0)
27480Sstevel@tonic-gate (void) fwrite(iobuf, sizeof (char), nbytes, mailpipe);
27490Sstevel@tonic-gate (void) fclose(st);
27500Sstevel@tonic-gate } else {
27510Sstevel@tonic-gate (void) fprintf(mailpipe, "completed.\n");
27520Sstevel@tonic-gate }
27530Sstevel@tonic-gate (void) pclose(mailpipe);
27540Sstevel@tonic-gate exit(0);
27550Sstevel@tonic-gate }
27560Sstevel@tonic-gate
27570Sstevel@tonic-gate static int
msg_wait(long tim)27580Sstevel@tonic-gate msg_wait(long tim)
27590Sstevel@tonic-gate {
27600Sstevel@tonic-gate struct message msg;
27610Sstevel@tonic-gate int cnt;
27620Sstevel@tonic-gate time_t reftime;
2763*11115SNobutomo.Nakano@Sun.COM fd_set fds;
2764*11115SNobutomo.Nakano@Sun.COM struct timespec tout, *toutp;
27650Sstevel@tonic-gate static int pending_msg;
27660Sstevel@tonic-gate static time_t pending_reftime;
27670Sstevel@tonic-gate
27680Sstevel@tonic-gate if (pending_msg) {
27690Sstevel@tonic-gate process_msg(&msgbuf, pending_reftime);
27700Sstevel@tonic-gate pending_msg = 0;
27710Sstevel@tonic-gate return (0);
27720Sstevel@tonic-gate }
27730Sstevel@tonic-gate
2774*11115SNobutomo.Nakano@Sun.COM FD_ZERO(&fds);
2775*11115SNobutomo.Nakano@Sun.COM FD_SET(msgfd, &fds);
2776*11115SNobutomo.Nakano@Sun.COM
2777*11115SNobutomo.Nakano@Sun.COM toutp = NULL;
2778*11115SNobutomo.Nakano@Sun.COM if (tim != INFINITY) {
27790Sstevel@tonic-gate #ifdef CRON_MAXSLEEP
2780*11115SNobutomo.Nakano@Sun.COM /*
2781*11115SNobutomo.Nakano@Sun.COM * CRON_MAXSLEEP can be defined to have cron periodically wake
2782*11115SNobutomo.Nakano@Sun.COM * up, so that cron can detect a change of TOD and adjust the
2783*11115SNobutomo.Nakano@Sun.COM * sleep time more frequently.
2784*11115SNobutomo.Nakano@Sun.COM */
2785*11115SNobutomo.Nakano@Sun.COM tim = (tim > CRON_MAXSLEEP) ? CRON_MAXSLEEP : tim;
27860Sstevel@tonic-gate #endif
2787*11115SNobutomo.Nakano@Sun.COM tout.tv_nsec = 0;
2788*11115SNobutomo.Nakano@Sun.COM tout.tv_sec = tim;
2789*11115SNobutomo.Nakano@Sun.COM toutp = &tout;
2790*11115SNobutomo.Nakano@Sun.COM }
2791*11115SNobutomo.Nakano@Sun.COM
2792*11115SNobutomo.Nakano@Sun.COM cnt = pselect(msgfd + 1, &fds, NULL, NULL, toutp, &defmask);
2793*11115SNobutomo.Nakano@Sun.COM if (cnt == -1 && errno != EINTR)
2794*11115SNobutomo.Nakano@Sun.COM perror("! pselect");
2795*11115SNobutomo.Nakano@Sun.COM
2796*11115SNobutomo.Nakano@Sun.COM /* pselect timeout or interrupted */
27970Sstevel@tonic-gate if (cnt <= 0)
27980Sstevel@tonic-gate return (0);
27990Sstevel@tonic-gate
28000Sstevel@tonic-gate errno = 0;
28010Sstevel@tonic-gate if ((cnt = read(msgfd, &msg, sizeof (msg))) != sizeof (msg)) {
28020Sstevel@tonic-gate if (cnt != -1 || errno != EAGAIN)
28030Sstevel@tonic-gate perror("! read");
28040Sstevel@tonic-gate return (0);
28050Sstevel@tonic-gate }
28060Sstevel@tonic-gate reftime = time(NULL);
28070Sstevel@tonic-gate if (next_event != NULL && reftime >= next_event->time) {
28080Sstevel@tonic-gate /*
28090Sstevel@tonic-gate * we need to run the job before reloading crontab.
28100Sstevel@tonic-gate */
28110Sstevel@tonic-gate (void) memcpy(&msgbuf, &msg, sizeof (msg));
28120Sstevel@tonic-gate pending_msg = 1;
28130Sstevel@tonic-gate pending_reftime = reftime;
28140Sstevel@tonic-gate return (1);
28150Sstevel@tonic-gate }
28160Sstevel@tonic-gate process_msg(&msg, reftime);
28170Sstevel@tonic-gate return (0);
28180Sstevel@tonic-gate }
28190Sstevel@tonic-gate
28200Sstevel@tonic-gate /*
28210Sstevel@tonic-gate * process the message supplied via pipe. This will be called either
28220Sstevel@tonic-gate * immediately after cron read the message from pipe, or idle time
28230Sstevel@tonic-gate * if the message was pending due to the job execution.
28240Sstevel@tonic-gate */
28250Sstevel@tonic-gate static void
process_msg(struct message * pmsg,time_t reftime)28260Sstevel@tonic-gate process_msg(struct message *pmsg, time_t reftime)
28270Sstevel@tonic-gate {
28280Sstevel@tonic-gate if (pmsg->etype == NULL)
28290Sstevel@tonic-gate return;
28300Sstevel@tonic-gate
28310Sstevel@tonic-gate switch (pmsg->etype) {
28320Sstevel@tonic-gate case AT:
28330Sstevel@tonic-gate if (pmsg->action == DELETE)
28340Sstevel@tonic-gate del_atjob(pmsg->fname, pmsg->logname);
28350Sstevel@tonic-gate else
28360Sstevel@tonic-gate mod_atjob(pmsg->fname, (time_t)0);
28370Sstevel@tonic-gate break;
28380Sstevel@tonic-gate case CRON:
28390Sstevel@tonic-gate if (pmsg->action == DELETE)
28400Sstevel@tonic-gate del_ctab(pmsg->fname);
28410Sstevel@tonic-gate else
28420Sstevel@tonic-gate mod_ctab(pmsg->fname, reftime);
28430Sstevel@tonic-gate break;
2844*11115SNobutomo.Nakano@Sun.COM case REFRESH:
2845*11115SNobutomo.Nakano@Sun.COM refresh = 1;
2846*11115SNobutomo.Nakano@Sun.COM pmsg->etype = 0;
2847*11115SNobutomo.Nakano@Sun.COM return;
28480Sstevel@tonic-gate default:
28490Sstevel@tonic-gate msg("message received - bad format");
28500Sstevel@tonic-gate break;
28510Sstevel@tonic-gate }
28520Sstevel@tonic-gate if (next_event != NULL) {
28537879SSerge.Dussud@Sun.COM if (next_event->etype == CRONEVENT) {
28547879SSerge.Dussud@Sun.COM switch (el_add(next_event, next_event->time,
28557879SSerge.Dussud@Sun.COM (next_event->u)->ctid)) {
28567879SSerge.Dussud@Sun.COM case -1:
28577879SSerge.Dussud@Sun.COM ignore_msg("process_msg", "cron", next_event);
28587879SSerge.Dussud@Sun.COM break;
28597879SSerge.Dussud@Sun.COM case -2: /* event time lower than init time */
28607879SSerge.Dussud@Sun.COM reset_needed = 1;
28617879SSerge.Dussud@Sun.COM break;
28627879SSerge.Dussud@Sun.COM }
28637879SSerge.Dussud@Sun.COM } else { /* etype == ATEVENT */
28647879SSerge.Dussud@Sun.COM if (el_add(next_event, next_event->time,
28657879SSerge.Dussud@Sun.COM next_event->of.at.eventid) < 0) {
28667879SSerge.Dussud@Sun.COM ignore_msg("process_msg", "at", next_event);
28677879SSerge.Dussud@Sun.COM }
28687879SSerge.Dussud@Sun.COM }
28690Sstevel@tonic-gate next_event = NULL;
28700Sstevel@tonic-gate }
28710Sstevel@tonic-gate (void) fflush(stdout);
2872*11115SNobutomo.Nakano@Sun.COM pmsg->etype = 0;
28730Sstevel@tonic-gate }
28740Sstevel@tonic-gate
2875801Sbasabi /*
2876801Sbasabi * Allocate a new or find an existing runinfo structure
2877801Sbasabi */
28780Sstevel@tonic-gate static struct runinfo *
rinfo_get(pid_t pid)28790Sstevel@tonic-gate rinfo_get(pid_t pid)
28800Sstevel@tonic-gate {
28810Sstevel@tonic-gate struct runinfo *rp;
28820Sstevel@tonic-gate
2883801Sbasabi if (pid == 0) { /* allocate a new entry */
2884801Sbasabi rp = xcalloc(1, sizeof (struct runinfo));
2885801Sbasabi rp->next = rthead; /* link the entry into the list */
2886801Sbasabi rthead = rp;
2887801Sbasabi return (rp);
2888801Sbasabi }
2889801Sbasabi /* search the list for an existing entry */
2890801Sbasabi for (rp = rthead; rp != NULL; rp = rp->next) {
28910Sstevel@tonic-gate if (rp->pid == pid)
28920Sstevel@tonic-gate break;
28930Sstevel@tonic-gate }
2894801Sbasabi return (rp);
28950Sstevel@tonic-gate }
28960Sstevel@tonic-gate
28970Sstevel@tonic-gate /*
2898801Sbasabi * Free a runinfo structure and its associated memory
28990Sstevel@tonic-gate */
29000Sstevel@tonic-gate static void
rinfo_free(struct runinfo * entry)2901801Sbasabi rinfo_free(struct runinfo *entry)
29020Sstevel@tonic-gate {
2903801Sbasabi struct runinfo **rpp;
2904801Sbasabi struct runinfo *rp;
2905801Sbasabi
2906801Sbasabi #ifdef DEBUG
2907801Sbasabi (void) fprintf(stderr, "freeing job %s\n", entry->jobname);
2908801Sbasabi #endif
2909801Sbasabi for (rpp = &rthead; (rp = *rpp) != NULL; rpp = &rp->next) {
2910801Sbasabi if (rp == entry) {
2911801Sbasabi *rpp = rp->next; /* unlink the entry */
2912801Sbasabi free(rp->outfile);
2913801Sbasabi free(rp->jobname);
2914801Sbasabi free(rp);
2915801Sbasabi break;
2916801Sbasabi }
2917801Sbasabi }
29180Sstevel@tonic-gate }
29190Sstevel@tonic-gate
29200Sstevel@tonic-gate /* ARGSUSED */
29210Sstevel@tonic-gate static void
thaw_handler(int sig)29220Sstevel@tonic-gate thaw_handler(int sig)
29230Sstevel@tonic-gate {
2924*11115SNobutomo.Nakano@Sun.COM refresh = 1;
29250Sstevel@tonic-gate }
29260Sstevel@tonic-gate
29270Sstevel@tonic-gate
29280Sstevel@tonic-gate /* ARGSUSED */
29290Sstevel@tonic-gate static void
cronend(int sig)29300Sstevel@tonic-gate cronend(int sig)
29310Sstevel@tonic-gate {
29320Sstevel@tonic-gate crabort("SIGTERM", REMOVE_FIFO);
29330Sstevel@tonic-gate }
29340Sstevel@tonic-gate
29350Sstevel@tonic-gate /*ARGSUSED*/
29360Sstevel@tonic-gate static void
child_handler(int sig)29370Sstevel@tonic-gate child_handler(int sig)
29380Sstevel@tonic-gate {
2939*11115SNobutomo.Nakano@Sun.COM ;
29400Sstevel@tonic-gate }
29410Sstevel@tonic-gate
29420Sstevel@tonic-gate static void
child_sigreset(void)29430Sstevel@tonic-gate child_sigreset(void)
29440Sstevel@tonic-gate {
29450Sstevel@tonic-gate (void) signal(SIGCLD, SIG_DFL);
29460Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, &defmask, NULL);
29470Sstevel@tonic-gate }
29480Sstevel@tonic-gate
29490Sstevel@tonic-gate /*
29500Sstevel@tonic-gate * crabort() - handle exits out of cron
29510Sstevel@tonic-gate */
29520Sstevel@tonic-gate static void
crabort(char * mssg,int action)29530Sstevel@tonic-gate crabort(char *mssg, int action)
29540Sstevel@tonic-gate {
29550Sstevel@tonic-gate int c;
29560Sstevel@tonic-gate
29570Sstevel@tonic-gate if (action & REMOVE_FIFO) {
29580Sstevel@tonic-gate /* FIFO vanishes when cron finishes */
29590Sstevel@tonic-gate if (unlink(FIFO) < 0)
29600Sstevel@tonic-gate perror("cron could not unlink FIFO");
29610Sstevel@tonic-gate }
29620Sstevel@tonic-gate
29630Sstevel@tonic-gate if (action & CONSOLE_MSG) {
29640Sstevel@tonic-gate /* write error msg to console */
29650Sstevel@tonic-gate if ((c = open(CONSOLE, O_WRONLY)) >= 0) {
29660Sstevel@tonic-gate (void) write(c, "cron aborted: ", 14);
29670Sstevel@tonic-gate (void) write(c, mssg, strlen(mssg));
29680Sstevel@tonic-gate (void) write(c, "\n", 1);
29690Sstevel@tonic-gate (void) close(c);
29700Sstevel@tonic-gate }
29710Sstevel@tonic-gate }
29720Sstevel@tonic-gate
29730Sstevel@tonic-gate /* always log the message */
29740Sstevel@tonic-gate msg(mssg);
29750Sstevel@tonic-gate msg("******* CRON ABORTED ********");
29760Sstevel@tonic-gate exit(1);
29770Sstevel@tonic-gate }
29780Sstevel@tonic-gate
29790Sstevel@tonic-gate /*
29800Sstevel@tonic-gate * msg() - time-stamped error reporting function
29810Sstevel@tonic-gate */
29820Sstevel@tonic-gate /*PRINTFLIKE1*/
29830Sstevel@tonic-gate static void
msg(char * fmt,...)29840Sstevel@tonic-gate msg(char *fmt, ...)
29850Sstevel@tonic-gate {
29860Sstevel@tonic-gate va_list args;
29870Sstevel@tonic-gate time_t t;
29880Sstevel@tonic-gate
29890Sstevel@tonic-gate t = time(NULL);
29900Sstevel@tonic-gate
29910Sstevel@tonic-gate (void) fflush(stdout);
29920Sstevel@tonic-gate
29930Sstevel@tonic-gate (void) fprintf(stderr, "! ");
29940Sstevel@tonic-gate
29950Sstevel@tonic-gate va_start(args, fmt);
29960Sstevel@tonic-gate (void) vfprintf(stderr, fmt, args);
29970Sstevel@tonic-gate va_end(args);
29980Sstevel@tonic-gate
29990Sstevel@tonic-gate (void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
30000Sstevel@tonic-gate (void) fprintf(stderr, " %s\n", timebuf);
30010Sstevel@tonic-gate
30020Sstevel@tonic-gate (void) fflush(stderr);
30030Sstevel@tonic-gate }
30040Sstevel@tonic-gate
30050Sstevel@tonic-gate static void
ignore_msg(char * func_name,char * job_type,struct event * event)30067879SSerge.Dussud@Sun.COM ignore_msg(char *func_name, char *job_type, struct event *event)
30077879SSerge.Dussud@Sun.COM {
30087879SSerge.Dussud@Sun.COM msg("%s: ignoring %s job (user: %s, cmd: %s, time: %ld)",
30097879SSerge.Dussud@Sun.COM func_name, job_type,
30107879SSerge.Dussud@Sun.COM event->u->name ? event->u->name : "unknown",
30117879SSerge.Dussud@Sun.COM event->cmd ? event->cmd : "unknown",
30127879SSerge.Dussud@Sun.COM event->time);
30137879SSerge.Dussud@Sun.COM }
30147879SSerge.Dussud@Sun.COM
30157879SSerge.Dussud@Sun.COM static void
logit(int cc,struct runinfo * rp,int rc)30160Sstevel@tonic-gate logit(int cc, struct runinfo *rp, int rc)
30170Sstevel@tonic-gate {
30180Sstevel@tonic-gate time_t t;
30190Sstevel@tonic-gate int ret;
30200Sstevel@tonic-gate
30210Sstevel@tonic-gate if (!log)
30220Sstevel@tonic-gate return;
30230Sstevel@tonic-gate
30240Sstevel@tonic-gate t = time(NULL);
30250Sstevel@tonic-gate if (cc == BCHAR)
30260Sstevel@tonic-gate (void) printf("%c CMD: %s\n", cc, next_event->cmd);
30270Sstevel@tonic-gate (void) strftime(timebuf, sizeof (timebuf), FORMAT, localtime(&t));
30280Sstevel@tonic-gate (void) printf("%c %.8s %u %c %s",
30295558Sbasabi cc, (rp->rusr)->name, rp->pid, QUE(rp->que), timebuf);
30300Sstevel@tonic-gate if ((ret = TSTAT(rc)) != 0)
30310Sstevel@tonic-gate (void) printf(" ts=%d", ret);
30320Sstevel@tonic-gate if ((ret = RCODE(rc)) != 0)
30330Sstevel@tonic-gate (void) printf(" rc=%d", ret);
30340Sstevel@tonic-gate (void) putchar('\n');
30350Sstevel@tonic-gate (void) fflush(stdout);
30360Sstevel@tonic-gate }
30370Sstevel@tonic-gate
30380Sstevel@tonic-gate static void
resched(int delay)30390Sstevel@tonic-gate resched(int delay)
30400Sstevel@tonic-gate {
30410Sstevel@tonic-gate time_t nt;
30420Sstevel@tonic-gate
30430Sstevel@tonic-gate /* run job at a later time */
30440Sstevel@tonic-gate nt = next_event->time + delay;
30450Sstevel@tonic-gate if (next_event->etype == CRONEVENT) {
30460Sstevel@tonic-gate next_event->time = next_time(next_event, (time_t)0);
30470Sstevel@tonic-gate if (nt < next_event->time)
30480Sstevel@tonic-gate next_event->time = nt;
30497879SSerge.Dussud@Sun.COM switch (el_add(next_event, next_event->time,
30507879SSerge.Dussud@Sun.COM (next_event->u)->ctid)) {
30517879SSerge.Dussud@Sun.COM case -1:
30527879SSerge.Dussud@Sun.COM ignore_msg("resched", "cron", next_event);
30537879SSerge.Dussud@Sun.COM break;
30547879SSerge.Dussud@Sun.COM case -2: /* event time lower than init time */
30557879SSerge.Dussud@Sun.COM reset_needed = 1;
30567879SSerge.Dussud@Sun.COM break;
30577879SSerge.Dussud@Sun.COM }
30580Sstevel@tonic-gate delayed = 1;
30590Sstevel@tonic-gate msg("rescheduling a cron job");
30600Sstevel@tonic-gate return;
30610Sstevel@tonic-gate }
30620Sstevel@tonic-gate add_atevent(next_event->u, next_event->cmd, nt, next_event->etype);
30630Sstevel@tonic-gate msg("rescheduling at job");
30640Sstevel@tonic-gate }
30650Sstevel@tonic-gate
30660Sstevel@tonic-gate static void
quedefs(int action)30670Sstevel@tonic-gate quedefs(int action)
30680Sstevel@tonic-gate {
30690Sstevel@tonic-gate int i;
30700Sstevel@tonic-gate int j;
30710Sstevel@tonic-gate char qbuf[QBUFSIZ];
30720Sstevel@tonic-gate FILE *fd;
30730Sstevel@tonic-gate
30740Sstevel@tonic-gate /* set up default queue definitions */
30750Sstevel@tonic-gate for (i = 0; i < NQUEUE; i++) {
30760Sstevel@tonic-gate qt[i].njob = qd.njob;
30770Sstevel@tonic-gate qt[i].nice = qd.nice;
30780Sstevel@tonic-gate qt[i].nwait = qd.nwait;
30790Sstevel@tonic-gate }
30800Sstevel@tonic-gate if (action == DEFAULT)
30810Sstevel@tonic-gate return;
30820Sstevel@tonic-gate if ((fd = fopen(QUEDEFS, "r")) == NULL) {
30830Sstevel@tonic-gate msg("cannot open quedefs file");
30840Sstevel@tonic-gate msg("using default queue definitions");
30850Sstevel@tonic-gate return;
30860Sstevel@tonic-gate }
30870Sstevel@tonic-gate while (fgets(qbuf, QBUFSIZ, fd) != NULL) {
30880Sstevel@tonic-gate if ((j = qbuf[0]-'a') < 0 || j >= NQUEUE || qbuf[1] != '.')
30890Sstevel@tonic-gate continue;
30900Sstevel@tonic-gate parsqdef(&qbuf[2]);
30910Sstevel@tonic-gate qt[j].njob = qq.njob;
30920Sstevel@tonic-gate qt[j].nice = qq.nice;
30930Sstevel@tonic-gate qt[j].nwait = qq.nwait;
30940Sstevel@tonic-gate }
30950Sstevel@tonic-gate (void) fclose(fd);
30960Sstevel@tonic-gate }
30970Sstevel@tonic-gate
30980Sstevel@tonic-gate static void
parsqdef(char * name)30990Sstevel@tonic-gate parsqdef(char *name)
31000Sstevel@tonic-gate {
31010Sstevel@tonic-gate int i;
31020Sstevel@tonic-gate
31030Sstevel@tonic-gate qq = qd;
31040Sstevel@tonic-gate while (*name) {
31050Sstevel@tonic-gate i = 0;
31060Sstevel@tonic-gate while (isdigit(*name)) {
31070Sstevel@tonic-gate i *= 10;
31080Sstevel@tonic-gate i += *name++ - '0';
31090Sstevel@tonic-gate }
31100Sstevel@tonic-gate switch (*name++) {
31110Sstevel@tonic-gate case JOBF:
31120Sstevel@tonic-gate qq.njob = i;
31130Sstevel@tonic-gate break;
31140Sstevel@tonic-gate case NICEF:
31150Sstevel@tonic-gate qq.nice = i;
31160Sstevel@tonic-gate break;
31170Sstevel@tonic-gate case WAITF:
31180Sstevel@tonic-gate qq.nwait = i;
31190Sstevel@tonic-gate break;
31200Sstevel@tonic-gate }
31210Sstevel@tonic-gate }
31220Sstevel@tonic-gate }
31230Sstevel@tonic-gate
31240Sstevel@tonic-gate /*
31250Sstevel@tonic-gate * defaults - read defaults from /etc/default/cron
31260Sstevel@tonic-gate */
31270Sstevel@tonic-gate static void
defaults()31280Sstevel@tonic-gate defaults()
31290Sstevel@tonic-gate {
31300Sstevel@tonic-gate int flags;
31310Sstevel@tonic-gate char *deflog;
31320Sstevel@tonic-gate char *hz, *tz;
31330Sstevel@tonic-gate
31340Sstevel@tonic-gate /*
31350Sstevel@tonic-gate * get HZ value for environment
31360Sstevel@tonic-gate */
31370Sstevel@tonic-gate if ((hz = getenv("HZ")) == (char *)NULL)
31380Sstevel@tonic-gate (void) sprintf(hzname, "HZ=%d", HZ);
31390Sstevel@tonic-gate else
31400Sstevel@tonic-gate (void) snprintf(hzname, sizeof (hzname), "HZ=%s", hz);
31410Sstevel@tonic-gate /*
31420Sstevel@tonic-gate * get TZ value for environment
31430Sstevel@tonic-gate */
31440Sstevel@tonic-gate (void) snprintf(tzone, sizeof (tzone), "TZ=%s",
31455558Sbasabi ((tz = getenv("TZ")) != NULL) ? tz : DEFTZ);
31460Sstevel@tonic-gate
31470Sstevel@tonic-gate if (defopen(DEFFILE) == 0) {
31480Sstevel@tonic-gate /* ignore case */
31490Sstevel@tonic-gate flags = defcntl(DC_GETFLAGS, 0);
31500Sstevel@tonic-gate TURNOFF(flags, DC_CASE);
31510Sstevel@tonic-gate (void) defcntl(DC_SETFLAGS, flags);
31520Sstevel@tonic-gate
31530Sstevel@tonic-gate if (((deflog = defread("CRONLOG=")) == NULL) ||
31540Sstevel@tonic-gate (*deflog == 'N') || (*deflog == 'n'))
31550Sstevel@tonic-gate log = 0;
31560Sstevel@tonic-gate else
31570Sstevel@tonic-gate log = 1;
31580Sstevel@tonic-gate /* fix for 1087611 - allow paths to be set in defaults file */
31590Sstevel@tonic-gate if ((Def_path = defread("PATH=")) != NULL) {
31600Sstevel@tonic-gate (void) strlcat(path, Def_path, LINE_MAX);
31610Sstevel@tonic-gate } else {
31620Sstevel@tonic-gate (void) strlcpy(path, NONROOTPATH, LINE_MAX);
31630Sstevel@tonic-gate }
31640Sstevel@tonic-gate if ((Def_supath = defread("SUPATH=")) != NULL) {
31650Sstevel@tonic-gate (void) strlcat(supath, Def_supath, LINE_MAX);
31660Sstevel@tonic-gate } else {
31670Sstevel@tonic-gate (void) strlcpy(supath, ROOTPATH, LINE_MAX);
31680Sstevel@tonic-gate }
31690Sstevel@tonic-gate (void) defopen(NULL);
31700Sstevel@tonic-gate }
31710Sstevel@tonic-gate }
31720Sstevel@tonic-gate
31730Sstevel@tonic-gate /*
31740Sstevel@tonic-gate * Determine if a user entry for a job is still ok. The method used here
31750Sstevel@tonic-gate * is a lot (about 75x) faster than using setgrent() / getgrent()
31760Sstevel@tonic-gate * endgrent(). It should be safe because we use the sysconf to determine
31770Sstevel@tonic-gate * the max, and it tolerates the max being 0.
31780Sstevel@tonic-gate */
31790Sstevel@tonic-gate
31800Sstevel@tonic-gate static int
verify_user_cred(struct usr * u)31815558Sbasabi verify_user_cred(struct usr *u)
31820Sstevel@tonic-gate {
31830Sstevel@tonic-gate struct passwd *pw;
31840Sstevel@tonic-gate size_t numUsrGrps = 0;
31850Sstevel@tonic-gate size_t numOrigGrps = 0;
31860Sstevel@tonic-gate size_t i;
31870Sstevel@tonic-gate int retval;
31880Sstevel@tonic-gate
31890Sstevel@tonic-gate /*
31900Sstevel@tonic-gate * Maximum number of groups a user may be in concurrently. This
31910Sstevel@tonic-gate * is a value which we obtain at runtime through a sysconf()
31920Sstevel@tonic-gate * call.
31930Sstevel@tonic-gate */
31940Sstevel@tonic-gate
31950Sstevel@tonic-gate static size_t nGroupsMax = (size_t)-1;
31960Sstevel@tonic-gate
31970Sstevel@tonic-gate /*
31980Sstevel@tonic-gate * Arrays for cron user's group list, constructed at startup to
31990Sstevel@tonic-gate * be nGroupsMax elements long, used for verifying user
32000Sstevel@tonic-gate * credentials prior to execution.
32010Sstevel@tonic-gate */
32020Sstevel@tonic-gate
32030Sstevel@tonic-gate static gid_t *UsrGrps;
32040Sstevel@tonic-gate static gid_t *OrigGrps;
32050Sstevel@tonic-gate
32065558Sbasabi if ((pw = getpwnam(u->name)) == NULL)
32070Sstevel@tonic-gate return (VUC_BADUSER);
32085558Sbasabi if (u->home != NULL) {
32095558Sbasabi if (strcmp(u->home, pw->pw_dir) != 0) {
32105558Sbasabi free(u->home);
32115558Sbasabi u->home = xmalloc(strlen(pw->pw_dir) + 1);
32125558Sbasabi (void) strcpy(u->home, pw->pw_dir);
32135558Sbasabi }
32145558Sbasabi } else {
32155558Sbasabi u->home = xmalloc(strlen(pw->pw_dir) + 1);
32165558Sbasabi (void) strcpy(u->home, pw->pw_dir);
32170Sstevel@tonic-gate }
32185558Sbasabi if (u->uid != pw->pw_uid)
32195558Sbasabi u->uid = pw->pw_uid;
32205558Sbasabi if (u->gid != pw->pw_gid)
32215558Sbasabi u->gid = pw->pw_gid;
32220Sstevel@tonic-gate
32230Sstevel@tonic-gate /*
32240Sstevel@tonic-gate * Create the group id lists needed for job credential
32250Sstevel@tonic-gate * verification.
32260Sstevel@tonic-gate */
32270Sstevel@tonic-gate
32280Sstevel@tonic-gate if (nGroupsMax == (size_t)-1) {
32290Sstevel@tonic-gate if ((nGroupsMax = sysconf(_SC_NGROUPS_MAX)) > 0) {
32300Sstevel@tonic-gate UsrGrps = xcalloc(nGroupsMax, sizeof (gid_t));
32310Sstevel@tonic-gate OrigGrps = xcalloc(nGroupsMax, sizeof (gid_t));
32320Sstevel@tonic-gate }
32330Sstevel@tonic-gate
32340Sstevel@tonic-gate #ifdef DEBUG
32350Sstevel@tonic-gate (void) fprintf(stderr, "nGroupsMax = %ld\n", nGroupsMax);
32360Sstevel@tonic-gate #endif
32370Sstevel@tonic-gate }
32380Sstevel@tonic-gate
32390Sstevel@tonic-gate #ifdef DEBUG
32400Sstevel@tonic-gate (void) fprintf(stderr, "verify_user_cred (%s-%d)\n", pw->pw_name,
32410Sstevel@tonic-gate pw->pw_uid);
32420Sstevel@tonic-gate (void) fprintf(stderr, "verify_user_cred: pw->pw_gid = %d, "
32430Sstevel@tonic-gate "u->gid = %d\n", pw->pw_gid, u->gid);
32440Sstevel@tonic-gate #endif
32450Sstevel@tonic-gate
32460Sstevel@tonic-gate retval = (u->gid == pw->pw_gid) ? VUC_OK : VUC_NOTINGROUP;
32470Sstevel@tonic-gate
32480Sstevel@tonic-gate if (nGroupsMax > 0) {
32490Sstevel@tonic-gate numOrigGrps = getgroups(nGroupsMax, OrigGrps);
32500Sstevel@tonic-gate
32510Sstevel@tonic-gate (void) initgroups(pw->pw_name, pw->pw_gid);
32520Sstevel@tonic-gate numUsrGrps = getgroups(nGroupsMax, UsrGrps);
32530Sstevel@tonic-gate
32540Sstevel@tonic-gate for (i = 0; i < numUsrGrps; i++) {
32550Sstevel@tonic-gate if (UsrGrps[i] == u->gid) {
32560Sstevel@tonic-gate retval = VUC_OK;
32570Sstevel@tonic-gate break;
32580Sstevel@tonic-gate }
32590Sstevel@tonic-gate }
32600Sstevel@tonic-gate
32610Sstevel@tonic-gate if (OrigGrps) {
32620Sstevel@tonic-gate (void) setgroups(numOrigGrps, OrigGrps);
32630Sstevel@tonic-gate }
32640Sstevel@tonic-gate }
32650Sstevel@tonic-gate
32660Sstevel@tonic-gate #ifdef DEBUG
32670Sstevel@tonic-gate (void) fprintf(stderr, "verify_user_cred: VUC = %d\n", retval);
32680Sstevel@tonic-gate #endif
32690Sstevel@tonic-gate
32700Sstevel@tonic-gate return (retval);
32710Sstevel@tonic-gate }
32720Sstevel@tonic-gate
32730Sstevel@tonic-gate static int
set_user_cred(const struct usr * u,struct project * pproj)32740Sstevel@tonic-gate set_user_cred(const struct usr *u, struct project *pproj)
32750Sstevel@tonic-gate {
32760Sstevel@tonic-gate static char *progname = "cron";
32770Sstevel@tonic-gate int r = 0, rval = 0;
32780Sstevel@tonic-gate
32790Sstevel@tonic-gate if ((r = pam_start(progname, u->name, &pam_conv, &pamh))
32805558Sbasabi != PAM_SUCCESS) {
32810Sstevel@tonic-gate #ifdef DEBUG
32820Sstevel@tonic-gate msg("pam_start returns %d\n", r);
32830Sstevel@tonic-gate #endif
32840Sstevel@tonic-gate rval = VUC_BADUSER;
32850Sstevel@tonic-gate goto set_eser_cred_exit;
32860Sstevel@tonic-gate }
32870Sstevel@tonic-gate
32880Sstevel@tonic-gate r = pam_acct_mgmt(pamh, 0);
32890Sstevel@tonic-gate #ifdef DEBUG
32900Sstevel@tonic-gate msg("pam_acc_mgmt returns %d\n", r);
32910Sstevel@tonic-gate #endif
32920Sstevel@tonic-gate if (r == PAM_ACCT_EXPIRED) {
32930Sstevel@tonic-gate rval = VUC_EXPIRED;
32940Sstevel@tonic-gate goto set_eser_cred_exit;
32950Sstevel@tonic-gate }
32960Sstevel@tonic-gate if (r == PAM_NEW_AUTHTOK_REQD) {
32970Sstevel@tonic-gate rval = VUC_NEW_AUTH;
32980Sstevel@tonic-gate goto set_eser_cred_exit;
32990Sstevel@tonic-gate }
33000Sstevel@tonic-gate if (r != PAM_SUCCESS) {
33010Sstevel@tonic-gate rval = VUC_BADUSER;
33020Sstevel@tonic-gate goto set_eser_cred_exit;
33030Sstevel@tonic-gate }
33040Sstevel@tonic-gate
33050Sstevel@tonic-gate if (pproj != NULL) {
33060Sstevel@tonic-gate size_t sz = sizeof (PROJECT) + strlen(pproj->pj_name);
33070Sstevel@tonic-gate char *buf = alloca(sz);
33080Sstevel@tonic-gate
33090Sstevel@tonic-gate (void) snprintf(buf, sz, PROJECT "%s", pproj->pj_name);
33100Sstevel@tonic-gate (void) pam_set_item(pamh, PAM_RESOURCE, buf);
33110Sstevel@tonic-gate }
33120Sstevel@tonic-gate
33130Sstevel@tonic-gate r = pam_setcred(pamh, PAM_ESTABLISH_CRED);
33140Sstevel@tonic-gate if (r != PAM_SUCCESS)
33150Sstevel@tonic-gate rval = VUC_BADUSER;
33160Sstevel@tonic-gate
33170Sstevel@tonic-gate set_eser_cred_exit:
33180Sstevel@tonic-gate (void) pam_end(pamh, r);
33190Sstevel@tonic-gate return (rval);
33200Sstevel@tonic-gate }
33210Sstevel@tonic-gate
33220Sstevel@tonic-gate static void
clean_out_user(struct usr * u)33230Sstevel@tonic-gate clean_out_user(struct usr *u)
33240Sstevel@tonic-gate {
33250Sstevel@tonic-gate if (next_event->u == u) {
33260Sstevel@tonic-gate next_event = NULL;
33270Sstevel@tonic-gate }
33280Sstevel@tonic-gate
33290Sstevel@tonic-gate clean_out_ctab(u);
33300Sstevel@tonic-gate clean_out_atjobs(u);
33310Sstevel@tonic-gate free_if_unused(u);
33320Sstevel@tonic-gate }
33330Sstevel@tonic-gate
33340Sstevel@tonic-gate static void
clean_out_atjobs(struct usr * u)33350Sstevel@tonic-gate clean_out_atjobs(struct usr *u)
33360Sstevel@tonic-gate {
33370Sstevel@tonic-gate struct event *ev, *pv;
33380Sstevel@tonic-gate
33390Sstevel@tonic-gate for (pv = NULL, ev = u->atevents;
33405558Sbasabi ev != NULL;
33415558Sbasabi pv = ev, ev = ev->link, free(pv)) {
33420Sstevel@tonic-gate el_remove(ev->of.at.eventid, 1);
33430Sstevel@tonic-gate if (cwd == AT)
33440Sstevel@tonic-gate cron_unlink(ev->cmd);
33450Sstevel@tonic-gate else {
33460Sstevel@tonic-gate char buf[PATH_MAX];
33470Sstevel@tonic-gate if (strlen(ATDIR) + strlen(ev->cmd) + 2
33485558Sbasabi < PATH_MAX) {
33490Sstevel@tonic-gate (void) sprintf(buf, "%s/%s", ATDIR, ev->cmd);
33500Sstevel@tonic-gate cron_unlink(buf);
33510Sstevel@tonic-gate }
33520Sstevel@tonic-gate }
33530Sstevel@tonic-gate free(ev->cmd);
33540Sstevel@tonic-gate }
33550Sstevel@tonic-gate
33560Sstevel@tonic-gate u->atevents = NULL;
33570Sstevel@tonic-gate }
33580Sstevel@tonic-gate
33590Sstevel@tonic-gate static void
clean_out_ctab(struct usr * u)33600Sstevel@tonic-gate clean_out_ctab(struct usr *u)
33610Sstevel@tonic-gate {
33620Sstevel@tonic-gate rm_ctevents(u);
33630Sstevel@tonic-gate el_remove(u->ctid, 0);
33640Sstevel@tonic-gate u->ctid = 0;
33650Sstevel@tonic-gate u->ctexists = 0;
33660Sstevel@tonic-gate }
33670Sstevel@tonic-gate
33680Sstevel@tonic-gate static void
cron_unlink(char * name)33690Sstevel@tonic-gate cron_unlink(char *name)
33700Sstevel@tonic-gate {
33710Sstevel@tonic-gate int r;
33720Sstevel@tonic-gate
33730Sstevel@tonic-gate r = unlink(name);
33740Sstevel@tonic-gate if (r == 0 || (r == -1 && errno == ENOENT)) {
33750Sstevel@tonic-gate (void) audit_cron_delete_anc_file(name, NULL);
33760Sstevel@tonic-gate }
33770Sstevel@tonic-gate }
33780Sstevel@tonic-gate
33790Sstevel@tonic-gate static void
create_anc_ctab(struct event * e)33800Sstevel@tonic-gate create_anc_ctab(struct event *e)
33810Sstevel@tonic-gate {
33820Sstevel@tonic-gate if (audit_cron_create_anc_file(e->u->name,
33835558Sbasabi (cwd == CRON) ? NULL:CRONDIR,
33845558Sbasabi e->u->name, e->u->uid) == -1) {
33850Sstevel@tonic-gate process_anc_files(CRON_ANC_DELETE);
33860Sstevel@tonic-gate crabort("cannot create ancillary files for crontabs",
33875558Sbasabi REMOVE_FIFO|CONSOLE_MSG);
33880Sstevel@tonic-gate }
33890Sstevel@tonic-gate }
33900Sstevel@tonic-gate
33910Sstevel@tonic-gate static void
delete_anc_ctab(struct event * e)33920Sstevel@tonic-gate delete_anc_ctab(struct event *e)
33930Sstevel@tonic-gate {
33940Sstevel@tonic-gate (void) audit_cron_delete_anc_file(e->u->name,
33955558Sbasabi (cwd == CRON) ? NULL:CRONDIR);
33960Sstevel@tonic-gate }
33970Sstevel@tonic-gate
33980Sstevel@tonic-gate static void
create_anc_atjob(struct event * e)33990Sstevel@tonic-gate create_anc_atjob(struct event *e)
34000Sstevel@tonic-gate {
34010Sstevel@tonic-gate if (!e->of.at.exists)
34020Sstevel@tonic-gate return;
34030Sstevel@tonic-gate
34040Sstevel@tonic-gate if (audit_cron_create_anc_file(e->cmd,
34055558Sbasabi (cwd == AT) ? NULL:ATDIR,
34065558Sbasabi e->u->name, e->u->uid) == -1) {
34070Sstevel@tonic-gate process_anc_files(CRON_ANC_DELETE);
34080Sstevel@tonic-gate crabort("cannot create ancillary files for atjobs",
34095558Sbasabi REMOVE_FIFO|CONSOLE_MSG);
34100Sstevel@tonic-gate }
34110Sstevel@tonic-gate }
34120Sstevel@tonic-gate
34130Sstevel@tonic-gate static void
delete_anc_atjob(struct event * e)34140Sstevel@tonic-gate delete_anc_atjob(struct event *e)
34150Sstevel@tonic-gate {
34160Sstevel@tonic-gate if (!e->of.at.exists)
34170Sstevel@tonic-gate return;
34180Sstevel@tonic-gate
34190Sstevel@tonic-gate (void) audit_cron_delete_anc_file(e->cmd,
34205558Sbasabi (cwd == AT) ? NULL:ATDIR);
34210Sstevel@tonic-gate }
34220Sstevel@tonic-gate
34230Sstevel@tonic-gate
34240Sstevel@tonic-gate static void
process_anc_files(int del)34250Sstevel@tonic-gate process_anc_files(int del)
34260Sstevel@tonic-gate {
34270Sstevel@tonic-gate struct usr *u = uhead;
34280Sstevel@tonic-gate struct event *e;
34290Sstevel@tonic-gate
34300Sstevel@tonic-gate if (!audit_cron_mode())
34310Sstevel@tonic-gate return;
34320Sstevel@tonic-gate
34330Sstevel@tonic-gate for (;;) {
34340Sstevel@tonic-gate if (u->ctexists && u->ctevents != NULL) {
34350Sstevel@tonic-gate e = u->ctevents;
34360Sstevel@tonic-gate for (;;) {
34370Sstevel@tonic-gate if (del)
34380Sstevel@tonic-gate delete_anc_ctab(e);
34390Sstevel@tonic-gate else
34400Sstevel@tonic-gate create_anc_ctab(e);
34410Sstevel@tonic-gate if ((e = e->link) == NULL)
34420Sstevel@tonic-gate break;
34430Sstevel@tonic-gate }
34440Sstevel@tonic-gate }
34450Sstevel@tonic-gate
34460Sstevel@tonic-gate if (u->atevents != NULL) {
34470Sstevel@tonic-gate e = u->atevents;
34480Sstevel@tonic-gate for (;;) {
34490Sstevel@tonic-gate if (del)
34500Sstevel@tonic-gate delete_anc_atjob(e);
34510Sstevel@tonic-gate else
34520Sstevel@tonic-gate create_anc_atjob(e);
34530Sstevel@tonic-gate if ((e = e->link) == NULL)
34540Sstevel@tonic-gate break;
34550Sstevel@tonic-gate }
34560Sstevel@tonic-gate }
34570Sstevel@tonic-gate
34580Sstevel@tonic-gate if ((u = u->nextusr) == NULL)
34590Sstevel@tonic-gate break;
34600Sstevel@tonic-gate }
34610Sstevel@tonic-gate }
34620Sstevel@tonic-gate
34630Sstevel@tonic-gate /*ARGSUSED*/
34640Sstevel@tonic-gate static int
cron_conv(int num_msg,struct pam_message ** msgs,struct pam_response ** response,void * appdata_ptr)34650Sstevel@tonic-gate cron_conv(int num_msg, struct pam_message **msgs,
34660Sstevel@tonic-gate struct pam_response **response, void *appdata_ptr)
34670Sstevel@tonic-gate {
34680Sstevel@tonic-gate struct pam_message **m = msgs;
34690Sstevel@tonic-gate int i;
34700Sstevel@tonic-gate
34710Sstevel@tonic-gate for (i = 0; i < num_msg; i++) {
34720Sstevel@tonic-gate switch (m[i]->msg_style) {
34730Sstevel@tonic-gate case PAM_ERROR_MSG:
34740Sstevel@tonic-gate case PAM_TEXT_INFO:
34750Sstevel@tonic-gate if (m[i]->msg != NULL) {
34760Sstevel@tonic-gate (void) msg("%s\n", m[i]->msg);
34770Sstevel@tonic-gate }
34780Sstevel@tonic-gate break;
34790Sstevel@tonic-gate
34800Sstevel@tonic-gate default:
34810Sstevel@tonic-gate break;
34820Sstevel@tonic-gate }
34830Sstevel@tonic-gate }
34840Sstevel@tonic-gate return (0);
34850Sstevel@tonic-gate }
34860Sstevel@tonic-gate
34870Sstevel@tonic-gate /*
34880Sstevel@tonic-gate * Cron creates process for other than job. Mail process is the
34890Sstevel@tonic-gate * one which rinfo does not cover. Therefore, miscpid will keep
34900Sstevel@tonic-gate * track of the pids executed from cron. Otherwise, we will see
34910Sstevel@tonic-gate * "unexpected pid returned.." messages appear in the log file.
34920Sstevel@tonic-gate */
34930Sstevel@tonic-gate static void
miscpid_insert(pid_t pid)34940Sstevel@tonic-gate miscpid_insert(pid_t pid)
34950Sstevel@tonic-gate {
34960Sstevel@tonic-gate struct miscpid *mp;
34970Sstevel@tonic-gate
34980Sstevel@tonic-gate mp = xmalloc(sizeof (*mp));
34990Sstevel@tonic-gate mp->pid = pid;
35000Sstevel@tonic-gate mp->next = miscpid_head;
35010Sstevel@tonic-gate miscpid_head = mp;
35020Sstevel@tonic-gate }
35030Sstevel@tonic-gate
35040Sstevel@tonic-gate static int
miscpid_delete(pid_t pid)35050Sstevel@tonic-gate miscpid_delete(pid_t pid)
35060Sstevel@tonic-gate {
35070Sstevel@tonic-gate struct miscpid *mp, *omp;
35080Sstevel@tonic-gate int found = 0;
35090Sstevel@tonic-gate
35100Sstevel@tonic-gate omp = NULL;
35110Sstevel@tonic-gate for (mp = miscpid_head; mp != NULL; mp = mp->next) {
35120Sstevel@tonic-gate if (mp->pid == pid) {
35130Sstevel@tonic-gate found = 1;
35140Sstevel@tonic-gate break;
35150Sstevel@tonic-gate }
35160Sstevel@tonic-gate omp = mp;
35170Sstevel@tonic-gate }
35180Sstevel@tonic-gate if (found) {
35190Sstevel@tonic-gate if (omp != NULL)
35200Sstevel@tonic-gate omp->next = mp->next;
35210Sstevel@tonic-gate else
35220Sstevel@tonic-gate miscpid_head = NULL;
35230Sstevel@tonic-gate free(mp);
35240Sstevel@tonic-gate }
35250Sstevel@tonic-gate return (found);
35260Sstevel@tonic-gate }
35270Sstevel@tonic-gate
35280Sstevel@tonic-gate /*
35290Sstevel@tonic-gate * Establish contract terms such that all children are in abandoned
35300Sstevel@tonic-gate * process contracts.
35310Sstevel@tonic-gate */
3532523Sbasabi static void
contract_set_template(void)3533523Sbasabi contract_set_template(void)
35340Sstevel@tonic-gate {
35350Sstevel@tonic-gate int fd;
35360Sstevel@tonic-gate
35370Sstevel@tonic-gate if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
35380Sstevel@tonic-gate crabort("cannot open process contract template",
35390Sstevel@tonic-gate REMOVE_FIFO | CONSOLE_MSG);
35400Sstevel@tonic-gate
35410Sstevel@tonic-gate if (ct_pr_tmpl_set_param(fd, 0) ||
35420Sstevel@tonic-gate ct_tmpl_set_informative(fd, 0) ||
35430Sstevel@tonic-gate ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR))
35440Sstevel@tonic-gate crabort("cannot establish contract template terms",
35450Sstevel@tonic-gate REMOVE_FIFO | CONSOLE_MSG);
35460Sstevel@tonic-gate
35470Sstevel@tonic-gate if (ct_tmpl_activate(fd))
35480Sstevel@tonic-gate crabort("cannot activate contract template",
35490Sstevel@tonic-gate REMOVE_FIFO | CONSOLE_MSG);
35500Sstevel@tonic-gate
35510Sstevel@tonic-gate (void) close(fd);
35520Sstevel@tonic-gate }
35530Sstevel@tonic-gate
35540Sstevel@tonic-gate /*
35550Sstevel@tonic-gate * Clear active process contract template.
35560Sstevel@tonic-gate */
3557523Sbasabi static void
contract_clear_template(void)3558523Sbasabi contract_clear_template(void)
35590Sstevel@tonic-gate {
35600Sstevel@tonic-gate int fd;
35610Sstevel@tonic-gate
35620Sstevel@tonic-gate if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) < 0)
35630Sstevel@tonic-gate crabort("cannot open process contract template",
35640Sstevel@tonic-gate REMOVE_FIFO | CONSOLE_MSG);
35650Sstevel@tonic-gate
35660Sstevel@tonic-gate if (ct_tmpl_clear(fd))
35670Sstevel@tonic-gate crabort("cannot clear contract template",
35680Sstevel@tonic-gate REMOVE_FIFO | CONSOLE_MSG);
35690Sstevel@tonic-gate
35700Sstevel@tonic-gate (void) close(fd);
35710Sstevel@tonic-gate }
35720Sstevel@tonic-gate
35730Sstevel@tonic-gate /*
35740Sstevel@tonic-gate * Abandon latest process contract unconditionally. If we have leaked [some
35750Sstevel@tonic-gate * critical amount], exit such that the kernel reaps our contracts.
35760Sstevel@tonic-gate */
35770Sstevel@tonic-gate static void
contract_abandon_latest(pid_t pid)35780Sstevel@tonic-gate contract_abandon_latest(pid_t pid)
35790Sstevel@tonic-gate {
35800Sstevel@tonic-gate int r;
35810Sstevel@tonic-gate ctid_t id;
35820Sstevel@tonic-gate static uint_t cts_lost;
35830Sstevel@tonic-gate
35840Sstevel@tonic-gate if (cts_lost > MAX_LOST_CONTRACTS)
35850Sstevel@tonic-gate crabort("repeated failure to abandon contracts",
35860Sstevel@tonic-gate REMOVE_FIFO | CONSOLE_MSG);
35870Sstevel@tonic-gate
35880Sstevel@tonic-gate if (r = contract_latest(&id)) {
35891315Sbasabi msg("could not obtain latest contract for "
35901315Sbasabi "PID %ld: %s", pid, strerror(r));
35910Sstevel@tonic-gate cts_lost++;
35920Sstevel@tonic-gate return;
35930Sstevel@tonic-gate }
35940Sstevel@tonic-gate
35950Sstevel@tonic-gate if (r = contract_abandon_id(id)) {
35960Sstevel@tonic-gate msg("could not abandon latest contract %ld: %s", id,
35970Sstevel@tonic-gate strerror(r));
35980Sstevel@tonic-gate cts_lost++;
35990Sstevel@tonic-gate return;
36000Sstevel@tonic-gate }
36010Sstevel@tonic-gate }
36028439SChris.Gerhard@sun.com
36038439SChris.Gerhard@sun.com static struct shared *
create_shared(void * obj,void * (* obj_alloc)(void * obj),void (* obj_free)(void *))36048439SChris.Gerhard@sun.com create_shared(void *obj, void * (*obj_alloc)(void *obj),
36058439SChris.Gerhard@sun.com void (*obj_free)(void *))
36068439SChris.Gerhard@sun.com {
36078439SChris.Gerhard@sun.com struct shared *out;
36088439SChris.Gerhard@sun.com
36098439SChris.Gerhard@sun.com if ((out = xmalloc(sizeof (struct shared))) == NULL) {
36108439SChris.Gerhard@sun.com return (NULL);
36118439SChris.Gerhard@sun.com }
36128439SChris.Gerhard@sun.com if ((out->obj = obj_alloc(obj)) == NULL) {
36138439SChris.Gerhard@sun.com free(out);
36148439SChris.Gerhard@sun.com return (NULL);
36158439SChris.Gerhard@sun.com }
36168439SChris.Gerhard@sun.com out->count = 1;
36178439SChris.Gerhard@sun.com out->free = obj_free;
36188439SChris.Gerhard@sun.com
36198439SChris.Gerhard@sun.com return (out);
36208439SChris.Gerhard@sun.com }
36218439SChris.Gerhard@sun.com
36228439SChris.Gerhard@sun.com static struct shared *
create_shared_str(char * str)36238439SChris.Gerhard@sun.com create_shared_str(char *str)
36248439SChris.Gerhard@sun.com {
36258439SChris.Gerhard@sun.com return (create_shared(str, (void *(*)(void *))strdup, free));
36268439SChris.Gerhard@sun.com }
36278439SChris.Gerhard@sun.com
36288439SChris.Gerhard@sun.com static struct shared *
dup_shared(struct shared * obj)36298439SChris.Gerhard@sun.com dup_shared(struct shared *obj)
36308439SChris.Gerhard@sun.com {
36318439SChris.Gerhard@sun.com if (obj != NULL) {
36328439SChris.Gerhard@sun.com obj->count++;
36338439SChris.Gerhard@sun.com }
36348439SChris.Gerhard@sun.com return (obj);
36358439SChris.Gerhard@sun.com }
36368439SChris.Gerhard@sun.com
36378439SChris.Gerhard@sun.com static void
rel_shared(struct shared * obj)36388439SChris.Gerhard@sun.com rel_shared(struct shared *obj)
36398439SChris.Gerhard@sun.com {
36408439SChris.Gerhard@sun.com if (obj && (--obj->count) == 0) {
36418439SChris.Gerhard@sun.com obj->free(obj->obj);
36428439SChris.Gerhard@sun.com free(obj);
36438439SChris.Gerhard@sun.com }
36448439SChris.Gerhard@sun.com }
36458439SChris.Gerhard@sun.com
36468439SChris.Gerhard@sun.com static void *
get_obj(struct shared * obj)36478439SChris.Gerhard@sun.com get_obj(struct shared *obj)
36488439SChris.Gerhard@sun.com {
36498439SChris.Gerhard@sun.com return (obj->obj);
36508439SChris.Gerhard@sun.com }
3651