summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Fiddaman <omnios@citrus-it.co.uk>2019-10-22 13:16:23 +0000
committerAndy Fiddaman <omnios@citrus-it.co.uk>2019-10-29 22:55:56 +0000
commit6b734416901818aa8c4bbb09c12b691ea771dc94 (patch)
tree5c971e88388d58cf2b9be50cc0d804f5eb4ec57d
parentece0bc848de931052064be9faf07f4e44c150a15 (diff)
downloadillumos-joyent-6b734416901818aa8c4bbb09c12b691ea771dc94.tar.gz
11858 crontab could support /step
Reviewed by: Matthias Scheler <matthias.scheler@wdc.com> Reviewed by: Dominik Hassler <hadfl@omniosce.org> Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r--usr/src/cmd/cron/Makefile33
-rw-r--r--usr/src/cmd/cron/cron.c109
-rw-r--r--usr/src/cmd/cron/cron.h19
-rw-r--r--usr/src/cmd/cron/crontab.c97
-rw-r--r--usr/src/cmd/cron/parse.c277
-rw-r--r--usr/src/man/man1/crontab.158
6 files changed, 407 insertions, 186 deletions
diff --git a/usr/src/cmd/cron/Makefile b/usr/src/cmd/cron/Makefile
index 2ba1fca040..b60ba3105e 100644
--- a/usr/src/cmd/cron/Makefile
+++ b/usr/src/cmd/cron/Makefile
@@ -23,6 +23,7 @@
# Use is subject to license terms.
#
# Copyright (c) 2018, Joyent, Inc.
+# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
DEFAULTFILES = cron.dfl
@@ -56,6 +57,7 @@ PROG2 = at atq atrm crontab
XPG6PROG = crontab
XPG4PROG = at crontab
PROG = $(PROG1) $(PROG2)
+PARSETEST = parsetest
SCRIPT = batch
XPG4SCRIPT = batch.xpg4
@@ -81,14 +83,15 @@ GETRESPOBJ= getresponse.o
COMMONOBJ1= permit.o
COMMONOBJ2= funcs.o
COMMONOBJS= $(COMMONOBJ1) $(COMMONOBJ2)
-CRONOBJS= cron.o elm.o
+CRONOBJS= cron.o elm.o parse.o
ATOBJS= at.o att1.o att2.o
XPG4OBJS= values-xpg4.o
ATRMOBJS1= atrm.o
ATRMOBJS= $(ATRMOBJS1) $(GETRESPOBJ)
ATQOBJS= atq.o
-CRONTABOBJS1= crontab.o
+CRONTABOBJS1= crontab.o parse.o
CRONTABOBJS= $(CRONTABOBJS1) $(GETRESPOBJ)
+PARSETESTOBJS= parse.o
# /usr/xpg*/bin/crontab isn't linked with values-xpg*.o since it isn't
# required by any specific behavior differences; this makes these
@@ -108,6 +111,7 @@ atq := POBJS = $(ATQOBJS) $(COMMONOBJS)
crontab := POBJS = $(CRONTABOBJS) $(COMMONOBJS)
crontab.xpg4 := POBJS = $(XPG4CTOBJS) $(XPG4COMMONOBJS)
crontab.xpg6 := POBJS = $(XPG6CTOBJS) $(XPG6COMMONOBJS)
+parsetest := POBJS = $(PARSETESTOBJS)
CFLAGS += $(CCVERBOSE)
@@ -138,17 +142,15 @@ at := LDLIBS += -lproject -lsecdb
at.xpg4 := LDLIBS += -lproject -lsecdb
atq := LDLIBS += -lsecdb
atrm := LDLIBS += -lsecdb
-cron := LDLIBS += -lpam -lproject -lcontract -lzoneinfo
-crontab := LDLIBS += -lsecdb -lpam -lzoneinfo
-crontab.xpg6 := LDLIBS += -lsecdb -lpam -lzoneinfo
-crontab.xpg4 := LDLIBS += -lsecdb -lpam -lzoneinfo
+cron := LDLIBS += -lpam -lproject -lcontract -lzoneinfo -lcustr
+crontab := LDLIBS += -lsecdb -lpam -lzoneinfo -lcustr
+crontab.xpg6 := LDLIBS += -lsecdb -lpam -lzoneinfo -lcustr
+crontab.xpg4 := LDLIBS += -lsecdb -lpam -lzoneinfo -lcustr
+parsetest := LDLIBS += -lcustr
-lint := LDLIBS += -lproject -lsecdb -lcontract -lpam
-
-$(XPG4) := CFLAGS += -DXPG4
-$(XPG6) := CFLAGS += -DXPG6
-
-LINTFLAGS += -u
+$(XPG4) := CFLAGS += -DXPG4
+$(XPG6) := CFLAGS += -DXPG6
+parsetest := CFLAGS += -DPARSETEST
$(ROOTSVCSYSTEM)/cron.xml := FILEMODE = 0444
$(ROOTLIBSVCMETHOD)/svc-cron := FILEMODE = 0555
@@ -156,12 +158,13 @@ $(ROOTLIBSVCMETHOD)/svc-cron := FILEMODE = 0555
.KEEP_STATE:
-all : $(PROG) $(XPG4) $(XPG6) $(SCRIPT) $(XPG4SCRIPT) $(FILES)
+all : $(PROG) $(XPG4) $(XPG6) $(SCRIPT) $(XPG4SCRIPT) $(FILES) \
+ $(PARSETEST)
install : all $(ROOTPROG) $(ROOTETCDEFAULTFILES) $(ROOTSYMLINK) \
$(ROOTMANIFEST) $(ROOTMETHOD)
-$(PROG) : $$(POBJS)
+$(PROG) $(PARSETEST): $$(POBJS)
$(LINK.c) $(POBJS) -o $@ $(LDLIBS)
$(POST_PROCESS)
@@ -225,8 +228,6 @@ $(POFILE): $(POFILES)
clean :
$(RM) $(OBJS) att1.h att1.c att2.c
-lint : lint_SRCS
-
strip :
$(STRIP) $(PROG) $(XPG4) $(XPG6)
diff --git a/usr/src/cmd/cron/cron.c b/usr/src/cmd/cron/cron.c
index a803c1cc70..faefcbebd0 100644
--- a/usr/src/cmd/cron/cron.c
+++ b/usr/src/cmd/cron/cron.c
@@ -26,6 +26,7 @@
*
* Copyright (c) 2014 Gary Mills
* Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -34,11 +35,6 @@
/* Copyright (c) 1987, 1988 Microsoft Corporation */
/* All Rights Reserved */
-#ifdef lint
-/* make lint happy */
-#define __EXTENSIONS__
-#endif
-
#include <sys/contract/process.h>
#include <sys/ctfs.h>
#include <sys/param.h>
@@ -315,7 +311,6 @@ static struct usr *find_usr(char *);
static int ex(struct event *e);
static void read_dirs(int);
static void mail(char *, char *, int);
-static char *next_field(int, int);
static void readcron(struct usr *, time_t);
static int next_ge(int, char *);
static void free_if_unused(struct usr *);
@@ -460,7 +455,7 @@ begin:
(void) setlocale(LC_ALL, "");
/* fork unless 'nofork' is specified */
if ((argc <= 1) || (strcmp(argv[1], "nofork"))) {
- if (rfork = fork()) {
+ if ((rfork = fork()) != 0) {
if (rfork == (pid_t)-1) {
(void) sleep(30);
goto begin;
@@ -978,7 +973,8 @@ mod_ctab(char *name, time_t reftime)
return;
}
#ifdef DEBUG
- (void) fprintf(stderr, "%s has revised his crontab\n", u->name);
+ (void) fprintf(stderr, "%s has revised their crontab\n",
+ u->name);
#endif
rm_ctevents(u);
el_remove(u->ctid, 0);
@@ -986,9 +982,8 @@ mod_ctab(char *name, time_t reftime)
}
}
-/* ARGSUSED */
static void
-mod_atjob(char *name, time_t reftime)
+mod_atjob(char *name, time_t reftime __unused)
{
char *ptr;
time_t tim;
@@ -1211,11 +1206,18 @@ readcron(struct usr *u, time_t reftime)
e = xmalloc(sizeof (struct event));
e->etype = CRONEVENT;
- if (!(((e->of.ct.minute = next_field(0, 59)) != NULL) &&
- ((e->of.ct.hour = next_field(0, 23)) != NULL) &&
- ((e->of.ct.daymon = next_field(1, 31)) != NULL) &&
- ((e->of.ct.month = next_field(1, 12)) != NULL) &&
- ((e->of.ct.dayweek = next_field(0, 6)) != NULL))) {
+
+ if (next_field(0, 59, line, &cursor,
+ &e->of.ct.minute) != CFOK ||
+ next_field(0, 23, line, &cursor, &e->of.ct.hour) != CFOK ||
+ next_field(1, 31, line, &cursor,
+ &e->of.ct.daymon) != CFOK ||
+ next_field(1, 12, line, &cursor, &e->of.ct.month) != CFOK ||
+ next_field(0, 6, line, &cursor,
+ &e->of.ct.dayweek) != CFOK) {
+#ifdef DEBUG
+ (void) fprintf(stderr, "Error: %d %s", lineno, line);
+#endif
free(e);
cte_add(lineno, line);
continue;
@@ -1444,67 +1446,6 @@ mail(char *usrname, char *mesg, int format)
}
}
-static char *
-next_field(int lower, int upper)
-{
- /*
- * next_field returns a pointer to a string which holds the next
- * field of a line of a crontab file.
- * if (numbers in this field are out of range (lower..upper),
- * or there is a syntax error) then
- * NULL is returned, and a mail message is sent to the
- * user telling them which line the error was in.
- */
-
- char *s;
- int num, num2, start;
-
- while ((line[cursor] == ' ') || (line[cursor] == '\t'))
- cursor++;
- start = cursor;
- if (line[cursor] == '\0') {
- return (NULL);
- }
- if (line[cursor] == '*') {
- cursor++;
- if ((line[cursor] != ' ') && (line[cursor] != '\t'))
- return (NULL);
- s = xmalloc(2);
- (void) strcpy(s, "*");
- return (s);
- }
- for (;;) {
- if (!isdigit(line[cursor]))
- return (NULL);
- num = 0;
- do {
- num = num*10 + (line[cursor]-'0');
- } while (isdigit(line[++cursor]));
- if ((num < lower) || (num > upper))
- return (NULL);
- if (line[cursor] == '-') {
- if (!isdigit(line[++cursor]))
- return (NULL);
- num2 = 0;
- do {
- num2 = num2*10 + (line[cursor]-'0');
- } while (isdigit(line[++cursor]));
- if ((num2 < lower) || (num2 > upper))
- return (NULL);
- }
- if ((line[cursor] == ' ') || (line[cursor] == '\t'))
- break;
- if (line[cursor] == '\0')
- return (NULL);
- if (line[cursor++] != ',')
- return (NULL);
- }
- s = xmalloc(cursor-start + 1);
- (void) strncpy(s, line + start, cursor-start);
- s[cursor-start] = '\0';
- return (s);
-}
-
#define tm_cmp(t1, t2) (\
(t1)->tm_year == (t2)->tm_year && \
(t1)->tm_mon == (t2)->tm_mon && \
@@ -2931,24 +2872,21 @@ rinfo_free(struct runinfo *entry)
}
}
-/* ARGSUSED */
static void
-thaw_handler(int sig)
+thaw_handler(int sig __unused)
{
refresh = 1;
}
-/* ARGSUSED */
static void
-cronend(int sig)
+cronend(int sig __unused)
{
crabort("SIGTERM", REMOVE_FIFO);
}
-/*ARGSUSED*/
static void
-child_handler(int sig)
+child_handler(int sig __unused)
{
;
}
@@ -3474,10 +3412,9 @@ process_anc_files(int del)
}
}
-/*ARGSUSED*/
static int
cron_conv(int num_msg, struct pam_message **msgs,
- struct pam_response **response, void *appdata_ptr)
+ struct pam_response **response __unused, void *appdata_ptr __unused)
{
struct pam_message **m = msgs;
int i;
@@ -3599,14 +3536,14 @@ contract_abandon_latest(pid_t pid)
crabort("repeated failure to abandon contracts",
REMOVE_FIFO | CONSOLE_MSG);
- if (r = contract_latest(&id)) {
+ if ((r = contract_latest(&id)) != 0) {
msg("could not obtain latest contract for "
"PID %ld: %s", pid, strerror(r));
cts_lost++;
return;
}
- if (r = contract_abandon_id(id)) {
+ if ((r = contract_abandon_id(id)) != 0) {
msg("could not abandon latest contract %ld: %s", id,
strerror(r));
cts_lost++;
diff --git a/usr/src/cmd/cron/cron.h b/usr/src/cmd/cron/cron.h
index a76016299c..e071209673 100644
--- a/usr/src/cmd/cron/cron.h
+++ b/usr/src/cmd/cron/cron.h
@@ -23,13 +23,17 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ */
+
#ifndef _CRON_H
#define _CRON_H
#include <unistd.h>
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
-/* All Rights Reserved */
+/* All Rights Reserved */
#ifdef __cplusplus
extern "C" {
@@ -71,6 +75,18 @@ struct message {
char logname[LLEN];
};
+/*
+ * Errors from the crontab field parser.
+ */
+typedef enum {
+ CFOK = 0,
+ CFEOLN,
+ CFUNEXPECT,
+ CFOUTOFBOUND,
+ CFEOVERFLOW,
+ CFENOMEM
+} cferror_t;
+
#define CRONDIR "/var/spool/cron/crontabs"
#define ATDIR "/var/spool/cron/atjobs"
#define ACCTFILE "/var/cron/log"
@@ -104,6 +120,7 @@ extern int isvalid_shell(const char *shell);
extern int isvalid_dir(const char *dir);
extern int cron_admin(const char *);
+extern cferror_t next_field(uint_t, uint_t, char *, int *, char **);
#ifdef __cplusplus
}
diff --git a/usr/src/cmd/cron/crontab.c b/usr/src/cmd/cron/crontab.c
index 88302f8bd7..296049a77a 100644
--- a/usr/src/cmd/cron/crontab.c
+++ b/usr/src/cmd/cron/crontab.c
@@ -25,6 +25,9 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
+/*
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ */
#include <sys/types.h>
#include <sys/stat.h>
@@ -81,6 +84,7 @@
#define EOLN "unexpected end of line."
#define UNEXPECT "unexpected character found in line."
#define OUTOFBOUND "number out of bounds."
+#define OVERFLOW "too many elements."
#define ERRSFND "errors detected in input, no crontab file generated."
#define ED_ERROR \
" The editor indicates that an error occurred while you were\n"\
@@ -108,7 +112,6 @@ char edtemp[5+13+1];
char line[CTLINESIZE];
static char login[UNAMESIZE];
-static int next_field(int, int);
static void catch(int);
static void crabort(char *);
static void cerror(char *);
@@ -411,6 +414,7 @@ copycron(FILE *fp)
char pid[6], *tnam_end;
int t;
char buf[LINE_MAX];
+ cferror_t cferr;
sprintf(pid, "%-5d", getpid());
tnam = xmalloc(strlen(CRONDIR)+strlen(TMPFILE)+7);
@@ -488,11 +492,34 @@ copycron(FILE *fp)
}
}
- if (next_field(0, 59)) continue;
- if (next_field(0, 23)) continue;
- if (next_field(1, 31)) continue;
- if (next_field(1, 12)) continue;
- if (next_field(0, 06)) continue;
+ if ((cferr = next_field(0, 59, line, &cursor, NULL)) != CFOK ||
+ (cferr = next_field(0, 23, line, &cursor, NULL)) != CFOK ||
+ (cferr = next_field(1, 31, line, &cursor, NULL)) != CFOK ||
+ (cferr = next_field(1, 12, line, &cursor, NULL)) != CFOK ||
+ (cferr = next_field(0, 6, line, &cursor, NULL)) != CFOK) {
+ switch (cferr) {
+ case CFEOLN:
+ cerror(EOLN);
+ break;
+ case CFUNEXPECT:
+ cerror(UNEXPECT);
+ break;
+ case CFOUTOFBOUND:
+ cerror(OUTOFBOUND);
+ break;
+ case CFEOVERFLOW:
+ cerror(OVERFLOW);
+ break;
+ case CFENOMEM:
+ (void) fprintf(stderr, "Out of memory\n");
+ exit(55);
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+
if (line[++cursor] == '\0') {
cerror(EOLN);
continue;
@@ -522,64 +549,6 @@ cont:
unlink(tnam);
}
-static int
-next_field(int lower, int upper)
-{
- int num, num2;
-
- while ((line[cursor] == ' ') || (line[cursor] == '\t')) cursor++;
- if (line[cursor] == '\0') {
- cerror(EOLN);
- return (1);
- }
- if (line[cursor] == '*') {
- cursor++;
- if ((line[cursor] != ' ') && (line[cursor] != '\t')) {
- cerror(UNEXPECT);
- return (1);
- }
- return (0);
- }
- while (TRUE) {
- if (!isdigit(line[cursor])) {
- cerror(UNEXPECT);
- return (1);
- }
- num = 0;
- do {
- num = num*10 + (line[cursor]-'0');
- } while (isdigit(line[++cursor]));
- if ((num < lower) || (num > upper)) {
- cerror(OUTOFBOUND);
- return (1);
- }
- if (line[cursor] == '-') {
- if (!isdigit(line[++cursor])) {
- cerror(UNEXPECT);
- return (1);
- }
- num2 = 0;
- do {
- num2 = num2*10 + (line[cursor]-'0');
- } while (isdigit(line[++cursor]));
- if ((num2 < lower) || (num2 > upper)) {
- cerror(OUTOFBOUND);
- return (1);
- }
- }
- if ((line[cursor] == ' ') || (line[cursor] == '\t')) break;
- if (line[cursor] == '\0') {
- cerror(EOLN);
- return (1);
- }
- if (line[cursor++] != ',') {
- cerror(UNEXPECT);
- return (1);
- }
- }
- return (0);
-}
-
static void
cerror(char *msg)
{
diff --git a/usr/src/cmd/cron/parse.c b/usr/src/cmd/cron/parse.c
new file mode 100644
index 0000000000..e82c751bd2
--- /dev/null
+++ b/usr/src/cmd/cron/parse.c
@@ -0,0 +1,277 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#include <stdio.h>
+#include <err.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+#include <libcustr.h>
+#include "cron.h"
+
+#ifdef PARSETEST
+#define xstrdup(x) strdup((x))
+#endif
+
+#define MAX_ELEMENTS 60
+
+#define READNUMBER(x) \
+ do { \
+ (x) = (x) * 10 + (line[cursor] - '0'); \
+ if ((x) > MAX_ELEMENTS) { \
+ err = CFOUTOFBOUND; \
+ goto out; \
+ } \
+ } while (isdigit(line[++cursor]))
+
+#define ADDELEMENT(x) \
+ do { \
+ if (eindex >= MAX_ELEMENTS) { \
+ err = CFEOVERFLOW; \
+ goto out; \
+ } \
+ elements[eindex++] = (x); \
+ } while (0)
+
+/* A more restrictive version of isspace(3C) that only looks for space/tab */
+#define ISSPACE(x) \
+ ((x) == ' ' || (x) == '\t')
+
+cferror_t
+next_field(uint_t lower, uint_t upper, char *line, int *cursorp, char **ret)
+{
+ uint_t elements[MAX_ELEMENTS];
+ uint_t eindex = 0, i;
+ int cursor = *cursorp;
+ cferror_t err = CFOK;
+
+ assert(upper - lower <= MAX_ELEMENTS);
+
+ if (ret != NULL)
+ *ret = NULL;
+
+ while (ISSPACE(line[cursor]))
+ cursor++;
+
+ if (line[cursor] == '\0') {
+ err = CFEOLN;
+ goto out;
+ }
+
+ for (;;) {
+ uint_t num = 0, num2 = 0, step = 0;
+
+ if (line[cursor] == '*') {
+ cursor++;
+
+ /* Short circuit for plain '*' */
+ if (ISSPACE(line[cursor])) {
+ if (ret != NULL)
+ *ret = xstrdup("*");
+ goto out;
+ }
+
+ /*
+ * '*' is only permitted alongside other elements if
+ * it has an associated step.
+ */
+
+ if (line[cursor] != '/') {
+ err = CFUNEXPECT;
+ goto out;
+ }
+
+ /* Treat it as a range covering all values */
+ num = lower;
+ num2 = upper;
+ } else {
+ if (!isdigit(line[cursor])) {
+ err = CFUNEXPECT;
+ goto out;
+ }
+
+ READNUMBER(num);
+
+ if (num < lower || num > upper) {
+ err = CFOUTOFBOUND;
+ goto out;
+ }
+
+ if (line[cursor] == '-') {
+ cursor++;
+ if (!isdigit(line[cursor])) {
+ err = CFUNEXPECT;
+ goto out;
+ }
+
+ READNUMBER(num2);
+
+ if (num2 < lower || num2 > upper) {
+ err = CFOUTOFBOUND;
+ goto out;
+ }
+ } else {
+ ADDELEMENT(num);
+ goto next;
+ }
+ }
+
+ /* Look for a step definition */
+ if (line[cursor] == '/') {
+ cursor++;
+ if (!isdigit(line[cursor])) {
+ err = CFUNEXPECT;
+ goto out;
+ }
+
+ READNUMBER(step);
+
+ if (step == 0) {
+ err = CFOUTOFBOUND;
+ goto out;
+ }
+ } else {
+ step = 1;
+ }
+
+ if (num <= num2) {
+ for (i = num; i <= num2; i += step) {
+ ADDELEMENT(i);
+ }
+ } else {
+ /* Wrap-around range */
+ for (i = num; i <= upper; i += step) {
+ ADDELEMENT(i);
+ }
+
+ i -= (upper - lower + 1);
+ for (; i <= num2; i += step) {
+ ADDELEMENT(i);
+ }
+ }
+
+next:
+
+ if (line[cursor] != ',')
+ break;
+
+ cursor++;
+ }
+
+ if (line[cursor] == '\0') {
+ err = CFEOLN;
+ goto out;
+ }
+
+ if (!ISSPACE(line[cursor])) {
+ err = CFUNEXPECT;
+ goto out;
+ }
+
+ if (ret != NULL) {
+ custr_t *cs = NULL;
+
+ if (custr_alloc(&cs) != 0) {
+ err = CFENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < eindex; i++) {
+ if (custr_len(cs) > 0) {
+ if (custr_appendc(cs, ',') != 0) {
+ custr_free(cs);
+ err = CFENOMEM;
+ goto out;
+ }
+ }
+ if (custr_append_printf(cs, "%u", elements[i]) != 0) {
+ custr_free(cs);
+ err = CFENOMEM;
+ goto out;
+ }
+ }
+
+ if (custr_len(cs) != 0)
+ *ret = xstrdup(custr_cstr(cs));
+ custr_free(cs);
+ }
+
+out:
+
+ *cursorp = cursor;
+
+ return (err);
+}
+
+#ifdef PARSETEST
+int
+main(int argc, char **argv)
+{
+ int lower, upper, cursor = 0;
+ char *ret;
+
+ if (argc != 4)
+ errx(1, "<lower> <upper> <string>");
+
+ lower = atoi(argv[1]);
+ upper = atoi(argv[2]);
+
+ switch (next_field(lower, upper, argv[3], &cursor, &ret)) {
+ case CFOK:
+ (void) printf("%s\n", ret);
+ break;
+ case CFEOLN:
+ (void) printf("UnexpectedEOL\n");
+ break;
+ case CFUNEXPECT:
+ (void) printf("UnexpectedChar\n");
+ break;
+ case CFOUTOFBOUND:
+ (void) printf("OutOfBounds\n");
+ break;
+ case CFEOVERFLOW:
+ (void) printf("Overflow\n");
+ break;
+ case CFENOMEM:
+ (void) printf("OutOfMemory\n");
+ break;
+ default:
+ (void) printf("UnknownError\n");
+ break;
+ }
+
+ return (0);
+}
+#endif
diff --git a/usr/src/man/man1/crontab.1 b/usr/src/man/man1/crontab.1
index df6c3a8619..ccb52bbffb 100644
--- a/usr/src/man/man1/crontab.1
+++ b/usr/src/man/man1/crontab.1
@@ -43,12 +43,12 @@
.\" Copyright 1989 AT&T
.\" Portions Copyright (c) 1992, X/Open Company Limited All Rights Reserved
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
.\"
-.TH CRONTAB 1 "Mar 6, 2017"
+.TH CRONTAB 1 "Oct 22, 2019"
.SH NAME
crontab \- user crontab file
.SH SYNOPSIS
-.LP
.nf
\fB/usr/bin/crontab\fR [\fIfilename\fR]
.fi
@@ -109,7 +109,6 @@ crontab \- user crontab file
.fi
.SH DESCRIPTION
-.LP
The \fBcrontab\fR utility manages a user's access with \fBcron\fR (see
\fBcron\fR(1M)) by copying, creating, listing, and removing \fBcrontab\fR
files. If invoked without options, \fBcrontab\fR copies the specified file, or
@@ -120,7 +119,6 @@ users' crontabs.
If \fBcrontab\fR is invoked with \fIfilename\fR, this overwrites an existing
\fBcrontab\fR entry for the user that invokes it.
.SS "\fBcrontab\fR Access Control"
-.LP
Users: Access to \fBcrontab\fR is allowed:
.RS +4
.TP
@@ -175,7 +173,6 @@ The rules for \fBallow\fR and \fBdeny\fR apply to \fBroot\fR only if the
.LP
The \fBallow\fR/\fBdeny\fR files consist of one user name per line.
.SS "\fBcrontab\fR Entry Format"
-.LP
A \fBcrontab\fR file consists of lines of six fields each. The fields are
separated by spaces or tabs. The first five are integer patterns that specify
the following:
@@ -195,10 +192,16 @@ day of the week (0\(mi6 with 0=Sunday).
.LP
Each of these patterns can be either an asterisk (meaning all legal values) or
a list of elements separated by commas. An element is either a number or two
-numbers separated by a minus sign (meaning an inclusive range). Time specified
-here is interpreted in the currently active timezone. At the top of the crontab
-file this is the timezone which is set system-wide in /etc/default/init. A user
-can add a line such as:
+numbers separated by a hyphen (meaning an inclusive range).
+.LP
+A range or asterisk can optionally be followed by a step value as
+\fI/<number>\fR. For example, \fI2\(mi59/3\fR can be used in the minutes field
+to specify every three minutes starting at 2 past the hour, or \fI*/2\fR in
+the hours field means every two hours.
+.LP
+Time specified here is interpreted in the currently active timezone. At the top
+of the crontab file this is the timezone which is set system-wide in
+/etc/default/init. A user can add a line such as:
.sp
.in +2
.nf
@@ -265,7 +268,6 @@ environment variables are set to match those that are in effect in the
If you do not redirect the standard output and standard error of your commands,
any generated output or errors are mailed to you.
.SS "\fBcrontab\fR Environment Variables"
-.LP
The following variables are supported:
.sp
.ne 2
@@ -337,7 +339,6 @@ The lines that are not setting these environment variables are the same as
crontab entries that conform to the UNIX standard and are described elsewhere
in this man page.
.SS "Setting \fBcron\fR Jobs Across Timezones"
-.LP
The default timezone of the \fBcron\fR daemon sets the system-wide timezone for
\fBcron\fR entries. This, in turn, is by set by default system-wide using
\fB/etc/default/init\fR.
@@ -347,7 +348,6 @@ If some form of \fBdaylight savings\fR or \fBsummer/winter time\fR is in
effect, then jobs scheduled during the switchover period could be executed
once, twice, or not at all.
.SH OPTIONS
-.LP
The following options are supported:
.sp
.ne 2
@@ -397,7 +397,6 @@ user.
.RE
.SH EXAMPLES
-.LP
\fBExample 1 \fRCleaning up Core Files
.sp
.LP
@@ -458,7 +457,33 @@ example:
would run a command only on Mondays.
.LP
-\fBExample 4 \fRUsing Environment Variables
+\fBExample 4 \fRUsing step values:
+.sp
+.LP
+This example runs a job every hour during the night and every 3 hours during
+working hours.
+
+.sp
+.in +2
+.nf
+\fB0 8-18/3,19-7 * * *\fR
+.fi
+.in -2
+.sp
+
+.LP
+and to run a job every 2 minutes, use:
+
+.sp
+.in +2
+.nf
+\fB*/2 * * * *\fR
+.fi
+.in -2
+.sp
+
+.LP
+\fBExample 5 \fRUsing Environment Variables
.sp
.LP
The following entries take advantage of \fBcrontab\fR support for certain
@@ -489,7 +514,6 @@ Korn shell. The file concludes with \fBTZ\fR, \fBHOME\fR, and \fBSHELL\fR
entries that return those variable to their default values.
.SH ENVIRONMENT VARIABLES
-.LP
See \fBenviron\fR(5) for descriptions of the following environment variables
that affect the execution of \fBcrontab\fR: \fBLANG\fR, \fBLC_ALL\fR,
\fBLC_CTYPE\fR, \fBLC_MESSAGES\fR, and \fBNLSPATH\fR.
@@ -546,7 +570,6 @@ default editor is \fB/usr/xpg6/bin/vi\fR.
.RE
.SH EXIT STATUS
-.LP
The following exit values are returned:
.sp
.ne 2
@@ -621,7 +644,6 @@ spool area for \fBcrontab\fR
.RE
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.SS "\fB/usr/bin/crontab\fR"
@@ -657,12 +679,10 @@ Interface Stability Standard
.TE
.SH SEE ALSO
-.LP
\fBatq\fR(1), \fBatrm\fR(1), \fBauths\fR(1), \fBed\fR(1), \fBsh\fR(1),
\fBvi\fR(1), \fBcron\fR(1M), \fBsu\fR(1M), \fBauth_attr\fR(4),
\fBattributes\fR(5), \fBenviron\fR(5), \fBstandards\fR(5)
.SH NOTES
-.LP
If you inadvertently enter the \fBcrontab\fR command with no arguments, do not
attempt to get out with Control-d. This removes all entries in your
\fBcrontab\fR file. Instead, exit with Control-c.