summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cron
diff options
context:
space:
mode:
authorChris Gerhard <Chris.Gerhard@sun.com>2008-12-23 15:44:14 +0000
committerChris Gerhard <Chris.Gerhard@sun.com>2008-12-23 15:44:14 +0000
commit5b08e637db3c0e4201b09b542c766da0f66129e8 (patch)
treec1cfc272be27e1403ecdfdd35023261f8157919c /usr/src/cmd/cron
parenta51287096c163d49f314be2853367563999e083c (diff)
downloadillumos-gate-5b08e637db3c0e4201b09b542c766da0f66129e8.tar.gz
PSARC/2007/503 crontab entry environment variables
6518038 cron & crontab should support multiple timezones
Diffstat (limited to 'usr/src/cmd/cron')
-rw-r--r--usr/src/cmd/cron/Makefile12
-rw-r--r--usr/src/cmd/cron/cron.c232
-rw-r--r--usr/src/cmd/cron/cron.h8
-rw-r--r--usr/src/cmd/cron/crontab.c58
-rw-r--r--usr/src/cmd/cron/funcs.c32
5 files changed, 305 insertions, 37 deletions
diff --git a/usr/src/cmd/cron/Makefile b/usr/src/cmd/cron/Makefile
index 8fcc9f240d..d0ee885f58 100644
--- a/usr/src/cmd/cron/Makefile
+++ b/usr/src/cmd/cron/Makefile
@@ -19,11 +19,9 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
DEFAULTFILES = cron.dfl
@@ -144,10 +142,10 @@ at := LDLIBS += -lproject -lsecdb
at.xpg4 := LDLIBS += -lproject -lsecdb
atq := LDLIBS += -lsecdb
atrm := LDLIBS += -lsecdb
-cron := LDLIBS += -lpam -lproject -lcontract
-crontab := LDLIBS += -lsecdb -lpam
-crontab.xpg6 := LDLIBS += -lsecdb -lpam
-crontab.xpg4 := LDLIBS += -lsecdb -lpam
+cron := LDLIBS += -lpam -lproject -lcontract -lzoneinfo
+crontab := LDLIBS += -lsecdb -lpam -lzoneinfo
+crontab.xpg6 := LDLIBS += -lsecdb -lpam -lzoneinfo
+crontab.xpg4 := LDLIBS += -lsecdb -lpam -lzoneinfo
lint := LDLIBS += -lproject -lsecdb -lcontract -lpam
diff --git a/usr/src/cmd/cron/cron.c b/usr/src/cmd/cron/cron.c
index 2abcdc3d36..4143333be2 100644
--- a/usr/src/cmd/cron/cron.c
+++ b/usr/src/cmd/cron/cron.c
@@ -29,7 +29,6 @@
/* Copyright (c) 1987, 1988 Microsoft Corporation */
/* All Rights Reserved */
-
#ifdef lint
/* make lint happy */
#define __EXTENSIONS__
@@ -70,6 +69,7 @@
#include <stropts.h>
#include <time.h>
#include <unistd.h>
+#include <libzoneinfo.h>
#include "cron.h"
@@ -119,9 +119,12 @@ isn't /usr/bin/sh, you can't use cron."
#define BADSTAT "can't access your crontab or at-job file. Resubmit it."
#define BADPROJID "can't set project id for your job."
-#define CANTCDHOME "can't change directory to your home directory.\
+#define CANTCDHOME "can't change directory to %s.\
\nYour commands will not be executed."
-#define CANTEXECSH "unable to exec the shell for one of your commands."
+#define CANTEXECSH "unable to exec the shell, %s, for one of your \
+commands."
+#define CANT_STR_LEN (sizeof (CANTEXECSH) > sizeof (CANTCDHOME) ? \
+ sizeof (CANTEXECSH) : sizeof (CANTCDHOME))
#define NOREAD "can't read your crontab file. Resubmit it."
#define BADTYPE "crontab or at-job file is not a regular file.\n"
#define NOSTDIN "unable to create a standard input file for \
@@ -166,6 +169,12 @@ error for each of your commands."
#define FORMAT "%a %b %e %H:%M:%S %Y"
static char timebuf[80];
+struct shared {
+ int count; /* usage count */
+ void (*free)(void *obj); /* routine that will free obj */
+ void *obj; /* object */
+};
+
struct event {
time_t time; /* time of the event */
short etype; /* what type of event; 0=cron, 1=at */
@@ -180,6 +189,9 @@ struct event {
char *month; /* from */
char *dayweek; /* crontab) */
char *input; /* ptr to stdin */
+ struct shared *tz; /* timezone of this event */
+ struct shared *home; /* directory for this event */
+ struct shared *shell; /* shell for this event */
} ct;
struct { /* for at events */
short exists; /* for revising at events */
@@ -256,9 +268,9 @@ static char *Def_supath = NULL;
static char *Def_path = NULL;
static char path[LINE_MAX] = "PATH=";
static char supath[LINE_MAX] = "PATH=";
-static char homedir[LINE_MAX] = "HOME=";
+static char homedir[LINE_MAX] = ENV_HOME;
static char logname[LINE_MAX] = "LOGNAME=";
-static char tzone[LINE_MAX] = "TZ=";
+static char tzone[LINE_MAX] = ENV_TZ;
static char *envinit[] = {
homedir,
logname,
@@ -327,6 +339,10 @@ static void cte_sendmail(char *);
static int set_user_cred(const struct usr *, struct project *);
+static struct shared *create_shared_str(char *str);
+static struct shared *dup_shared(struct shared *obj);
+static void rel_shared(struct shared *obj);
+static void *get_obj(struct shared *obj);
/*
* last_time is set immediately prior to exection of an event (via ex())
* to indicate the last time an event was executed. This was (surely)
@@ -693,6 +709,9 @@ initialize(int firstpass)
init_time = time(NULL);
el_init(8, init_time, (time_t)(60*60*24), 10);
+ init_time = time(NULL);
+ el_init(8, init_time, (time_t)(60*60*24), 10);
+
/*
* read directories, create users list, and add events to the
* main event list. Only zero user list on firstpass.
@@ -715,7 +734,6 @@ initialize(int firstpass)
/* stderr also goes to ACCTFILE */
(void) close(fileno(stderr));
(void) dup(1);
-
/* null for stdin */
(void) freopen("/dev/null", "r", stdin);
@@ -874,7 +892,7 @@ mod_ctab(char *name, time_t reftime)
struct passwd *pw;
struct stat buf;
struct usr *u;
- char namebuf[PATH_MAX];
+ char namebuf[LINE_MAX];
char *pname;
/* skip over ancillary file names */
@@ -1110,6 +1128,9 @@ readcron(struct usr *u, time_t reftime)
unsigned int i;
char namebuf[PATH_MAX];
char *pname;
+ struct shared *tz = NULL;
+ struct shared *home = NULL;
+ struct shared *shell = NULL;
int lineno = 0;
/* read the crontab file */
@@ -1128,6 +1149,7 @@ readcron(struct usr *u, time_t reftime)
return;
}
while (fgets(line, CTLINESIZE, cf) != NULL) {
+ char *tmp;
/* process a line of a crontab file */
lineno++;
if (cte_istoomany())
@@ -1137,6 +1159,52 @@ readcron(struct usr *u, time_t reftime)
cursor++;
if (line[cursor] == '#' || line[cursor] == '\n')
continue;
+
+ if (strncmp(&line[cursor], ENV_TZ,
+ strlen(ENV_TZ)) == 0) {
+ if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
+ *tmp = NULL;
+ }
+
+ if (!isvalid_tz(&line[cursor + strlen(ENV_TZ)], NULL,
+ _VTZ_ALL)) {
+ cte_add(lineno, line);
+ break;
+ }
+ if (tz == NULL || strcmp(&line[cursor], get_obj(tz))) {
+ rel_shared(tz);
+ tz = create_shared_str(&line[cursor]);
+ }
+ continue;
+ }
+
+ if (strncmp(&line[cursor], ENV_HOME,
+ strlen(ENV_HOME)) == 0) {
+ if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
+ *tmp = NULL;
+ }
+ if (home == NULL ||
+ strcmp(&line[cursor], get_obj(home))) {
+ rel_shared(home);
+ home = create_shared_str(
+ &line[cursor + strlen(ENV_HOME)]);
+ }
+ continue;
+ }
+
+ if (strncmp(&line[cursor], ENV_SHELL,
+ strlen(ENV_SHELL)) == 0) {
+ if ((tmp = strchr(&line[cursor], '\n')) != NULL) {
+ *tmp = NULL;
+ }
+ if (shell == NULL ||
+ strcmp(&line[cursor], get_obj(shell))) {
+ rel_shared(shell);
+ shell = create_shared_str(&line[cursor]);
+ }
+ continue;
+ }
+
e = xmalloc(sizeof (struct event));
e->etype = CRONEVENT;
if (!(((e->of.ct.minute = next_field(0, 59)) != NULL) &&
@@ -1178,6 +1246,12 @@ again:
} else {
e->of.ct.input = NULL;
}
+ /* set the timezone of this entry */
+ e->of.ct.tz = dup_shared(tz);
+ /* set the shell of this entry */
+ e->of.ct.shell = dup_shared(shell);
+ /* set the home of this entry */
+ e->of.ct.home = dup_shared(home);
/* have the event point to it's owner */
e->u = u;
/* insert this event at the front of this user's event list */
@@ -1203,6 +1277,9 @@ again:
}
cte_sendmail(u->name); /* mail errors if any to user */
(void) fclose(cf);
+ rel_shared(tz);
+ rel_shared(shell);
+ rel_shared(home);
}
/*
@@ -1451,8 +1528,10 @@ next_field(int lower, int upper)
* itself backwards).
*/
+
+
static time_t
-next_time(struct event *e, time_t tflag)
+tz_next_time(struct event *e, time_t tflag)
{
/*
* returns the integer time for the next occurance of event e.
@@ -1465,10 +1544,9 @@ next_time(struct event *e, time_t tflag)
*/
struct tm *tm, ref_tm, tmp, tmp1, tmp2;
- int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days,
- d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd,
- today;
-
+ int tm_mon, tm_mday, tm_wday, wday, m, min, h, hr, carry, day, days;
+ int d1, day1, carry1, d2, day2, carry2, daysahead, mon, yr, db, wd;
+ int today;
time_t t, ref_t, t1, t2, zone_start;
int fallback;
extern int days_btwn(int, int, int, int, int, int);
@@ -1575,8 +1653,9 @@ recalc:
t = get_switching_time(tmp1.tm_isdst,
t1 - abs(timezone - altzone));
}
- if (t == (time_t)-1)
+ if (t == (time_t)-1) {
return (0);
+ }
}
goto recalc;
}
@@ -1821,6 +1900,23 @@ recalc:
/*NOTREACHED*/
}
+static time_t
+next_time(struct event *e, time_t tflag)
+{
+ if (e->of.ct.tz != NULL) {
+ time_t ret;
+
+ (void) putenv((char *)get_obj(e->of.ct.tz));
+ tzset();
+ ret = tz_next_time(e, tflag);
+ (void) putenv(tzone);
+ tzset();
+ return (ret);
+ } else {
+ return (tz_next_time(e, tflag));
+ }
+}
+
/*
* This returns TOD in time_t that zone switch will happen, and this
* will be called when clock fallback is about to happen.
@@ -2040,6 +2136,9 @@ rm_ctevents(struct usr *u)
e2 = u->ctevents;
while (e2 != NULL) {
free(e2->cmd);
+ rel_shared(e2->of.ct.tz);
+ rel_shared(e2->of.ct.shell);
+ rel_shared(e2->of.ct.home);
free(e2->of.ct.minute);
free(e2->of.ct.hour);
free(e2->of.ct.daymon);
@@ -2087,13 +2186,20 @@ ex(struct event *e)
struct queue *qp;
struct runinfo *rp;
struct project proj, *pproj = NULL;
- char mybuf[PROJECT_BUFSZ];
- char mybuf2[PROJECT_BUFSZ];
+ union {
+ struct {
+ char buf[PROJECT_BUFSZ];
+ char buf2[PROJECT_BUFSZ];
+ } p;
+ char error[CANT_STR_LEN + PATH_MAX];
+ } bufs;
char *tmpfile;
FILE *fptr;
time_t dhltime;
projid_t projid;
int projflag = 0;
+ char *home;
+ char *sh;
qp = &qt[e->etype]; /* set pointer to queue defs */
if (qp->nrun >= qp->njob) {
@@ -2259,9 +2365,10 @@ ex(struct event *e)
*/
if (projflag == 1) {
if ((pproj = getprojbyid(projid, &proj,
- (void *)&mybuf, sizeof (mybuf))) == NULL ||
+ (void *)&bufs.p.buf,
+ sizeof (bufs.p.buf))) == NULL ||
!inproj(e->u->name, pproj->pj_name,
- mybuf2, sizeof (mybuf2))) {
+ bufs.p.buf2, sizeof (bufs.p.buf2))) {
cron_unlink(at_cmdfile);
mail((e->u)->name, BADPROJID, ERR_CANTEXECAT);
exit(1);
@@ -2401,11 +2508,17 @@ ex(struct event *e)
(void) close(fd);
}
- (void) strlcat(homedir, (e->u)->home, sizeof (homedir));
+ if (e->etype == CRONEVENT && e->of.ct.home != NULL) {
+ home = (char *)get_obj(e->of.ct.home);
+ } else {
+ home = (e->u)->home;
+ }
+ (void) strlcat(homedir, home, sizeof (homedir));
(void) strlcat(logname, (e->u)->name, sizeof (logname));
environ = envinit;
- if (chdir((e->u)->home) == -1) {
- mail((e->u)->name, CANTCDHOME,
+ if (chdir(home) == -1) {
+ snprintf(bufs.error, sizeof (bufs.error), CANTCDHOME, home);
+ mail((e->u)->name, bufs.error,
e->etype == CRONEVENT ? ERR_CANTEXECCRON :
ERR_CANTEXECAT);
exit(1);
@@ -2421,11 +2534,33 @@ ex(struct event *e)
if ((e->u)->uid != 0)
(void) nice(qp->nice);
- if (e->etype == CRONEVENT)
- (void) execl(SHELL, "sh", "-c", e->cmd, 0);
- else /* type == ATEVENT */
+ if (e->etype == CRONEVENT) {
+ if (e->of.ct.tz) {
+ (void) putenv((char *)get_obj(e->of.ct.tz));
+ }
+ if (e->of.ct.shell) {
+ char *name;
+
+ sh = (char *)get_obj(e->of.ct.shell);
+ name = strrchr(sh, '/');
+ if (name == NULL)
+ name = sh;
+ else
+ name++;
+
+ (void) putenv(sh);
+ sh += strlen(ENV_SHELL);
+ (void) execl(sh, name, "-c", e->cmd, 0);
+ } else {
+ (void) execl(SHELL, "sh", "-c", e->cmd, 0);
+ sh = SHELL;
+ }
+ } else { /* type == ATEVENT */
(void) execl(SHELL, "sh", 0);
- mail((e->u)->name, CANTEXECSH,
+ sh = SHELL;
+ }
+ snprintf(bufs.error, sizeof (bufs.error), CANTEXECSH, sh);
+ mail((e->u)->name, bufs.error,
e->etype == CRONEVENT ? ERR_CANTEXECCRON : ERR_CANTEXECAT);
exit(1);
/*NOTREACHED*/
@@ -3493,3 +3628,52 @@ contract_abandon_latest(pid_t pid)
return;
}
}
+
+static struct shared *
+create_shared(void *obj, void * (*obj_alloc)(void *obj),
+ void (*obj_free)(void *))
+{
+ struct shared *out;
+
+ if ((out = xmalloc(sizeof (struct shared))) == NULL) {
+ return (NULL);
+ }
+ if ((out->obj = obj_alloc(obj)) == NULL) {
+ free(out);
+ return (NULL);
+ }
+ out->count = 1;
+ out->free = obj_free;
+
+ return (out);
+}
+
+static struct shared *
+create_shared_str(char *str)
+{
+ return (create_shared(str, (void *(*)(void *))strdup, free));
+}
+
+static struct shared *
+dup_shared(struct shared *obj)
+{
+ if (obj != NULL) {
+ obj->count++;
+ }
+ return (obj);
+}
+
+static void
+rel_shared(struct shared *obj)
+{
+ if (obj && (--obj->count) == 0) {
+ obj->free(obj->obj);
+ free(obj);
+ }
+}
+
+static void *
+get_obj(struct shared *obj)
+{
+ return (obj->obj);
+}
diff --git a/usr/src/cmd/cron/cron.h b/usr/src/cmd/cron/cron.h
index ca824e02bc..a859fa9f50 100644
--- a/usr/src/cmd/cron/cron.h
+++ b/usr/src/cmd/cron/cron.h
@@ -26,8 +26,6 @@
#ifndef _CRON_H
#define _CRON_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
@@ -88,6 +86,10 @@ struct message {
#define SHELL "/usr/bin/sh" /* shell to execute */
+#define ENV_SHELL "SHELL="
+#define ENV_TZ "TZ="
+#define ENV_HOME "HOME="
+
#define CTLINESIZE 1000 /* max chars in a crontab line */
#define UNAMESIZE 20 /* max chars in a user name */
@@ -99,6 +101,8 @@ void cron_sendmsg(char, char *, char *, char);
time_t num(char **);
void *xmalloc(size_t);
void *xcalloc(size_t, size_t);
+int isvalid_shell(const char *shell);
+int isvalid_dir(const char *dir);
#ifdef __cplusplus
}
diff --git a/usr/src/cmd/cron/crontab.c b/usr/src/cmd/cron/crontab.c
index 6878460300..266ad44da3 100644
--- a/usr/src/cmd/cron/crontab.c
+++ b/usr/src/cmd/cron/crontab.c
@@ -26,8 +26,6 @@
/* All Rights Reserved */
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -47,6 +45,8 @@
#include <libintl.h>
#include <spawn.h>
#include <security/pam_appl.h>
+#include <limits.h>
+#include <libzoneinfo.h>
#include "cron.h"
#include "getresponse.h"
@@ -91,6 +91,9 @@
" Edit again, to ensure crontab information is intact (%s/%s)?\n"\
" ('%s' will discard edits.)"
#define NAMETOOLONG "login name too long"
+#define BAD_TZ "Timezone unrecognized in: %s"
+#define BAD_SHELL "Invalid shell specified: %s"
+#define BAD_HOME "Unable to access directory: %s\t%s\n"
extern int per_errno;
extern char **environ;
@@ -435,6 +438,7 @@ FILE *fp;
FILE *tfp;
char pid[6], *tnam_end;
int t;
+ char buf[LINE_MAX];
sprintf(pid, "%-5d", getpid());
tnam = xmalloc(strlen(CRONDIR)+strlen(TMPFILE)+7);
@@ -462,6 +466,56 @@ FILE *fp;
/* fix for 1039689 - treat blank line like a comment */
if (line[cursor] == '#' || line[cursor] == '\n')
goto cont;
+
+ if (strncmp(&line[cursor], ENV_TZ, strlen(ENV_TZ)) == 0) {
+ char *x;
+
+ strncpy(buf, &line[cursor + strlen(ENV_TZ)],
+ sizeof (buf));
+ if ((x = strchr(buf, '\n')) != NULL)
+ *x = NULL;
+
+ if (isvalid_tz(buf, NULL, _VTZ_ALL)) {
+ goto cont;
+ } else {
+ err = 1;
+ fprintf(stderr, BAD_TZ, &line[cursor]);
+ continue;
+ }
+ } else if (strncmp(&line[cursor], ENV_SHELL,
+ strlen(ENV_SHELL)) == 0) {
+ char *x;
+
+ strncpy(buf, &line[cursor + strlen(ENV_SHELL)],
+ sizeof (buf));
+ if ((x = strchr(buf, '\n')) != NULL)
+ *x = NULL;
+
+ if (isvalid_shell(buf)) {
+ goto cont;
+ } else {
+ err = 1;
+ fprintf(stderr, BAD_SHELL, &line[cursor]);
+ continue;
+ }
+ } else if (strncmp(&line[cursor], ENV_HOME,
+ strlen(ENV_HOME)) == 0) {
+ char *x;
+
+ strncpy(buf, &line[cursor + strlen(ENV_HOME)],
+ sizeof (buf));
+ if ((x = strchr(buf, '\n')) != NULL)
+ *x = NULL;
+ if (chdir(buf) == 0) {
+ goto cont;
+ } else {
+ err = 1;
+ fprintf(stderr, BAD_HOME, &line[cursor],
+ strerror(errno));
+ continue;
+ }
+ }
+
if (next_field(0, 59)) continue;
if (next_field(0, 23)) continue;
if (next_field(1, 31)) continue;
diff --git a/usr/src/cmd/cron/funcs.c b/usr/src/cmd/cron/funcs.c
index 84ffbdc07f..0a5e78476a 100644
--- a/usr/src/cmd/cron/funcs.c
+++ b/usr/src/cmd/cron/funcs.c
@@ -27,8 +27,6 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
@@ -203,3 +201,33 @@ xcalloc(size_t nElements, size_t size)
}
return (p);
}
+
+int
+isvalid_shell(const char *shell)
+{
+ char *t;
+ int ret = 0;
+
+ while ((t = getusershell()) != NULL) {
+ if (strcmp(t, shell) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+ endusershell();
+ return (ret);
+}
+
+int
+isvalid_dir(const char *dir)
+{
+ char *cwd = getcwd(NULL, 0);
+
+ if (dir[0] != '/' || chdir(dir) == -1) {
+ return (0);
+ }
+ if (cwd != NULL) {
+ (void) chdir(cwd);
+ }
+ return (1);
+}