#include <sys/types.h>
#include <bitstring.h>
#include <ctype.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include "pathnames.h"
#include "macros.h"
#include "structs.h"
#include "funcs.h"
typedef enum ecode {
e_none, e_minute, e_hour, e_dom, e_month, e_dow,
e_cmd, e_timespec, e_username, e_option, e_memory, e_flags
} ecode_e;
static const char *ecodes[] = {
"no error",
"bad minute",
"bad hour",
"bad day-of-month",
"bad month",
"bad day-of-week",
"bad command",
"bad time specifier",
"bad username",
"bad option",
"out of memory",
"bad flags"
};
static const char *MonthNames[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
NULL
};
static const char *DowNames[] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
NULL
};
static int get_list(bitstr_t *, int, int, const char *[], int, FILE *),
get_range(bitstr_t *, int, int, const char *[], int, FILE *),
get_number(int *, int, int, const char *[], int, FILE *, const char *),
set_element(bitstr_t *, int, int, int),
set_range(bitstr_t *, int, int, int, int, int);
void
free_entry(entry *e)
{
free(e->cmd);
free(e->pwd);
if (e->envp)
env_free(e->envp);
free(e);
}
entry *
load_entry(FILE *file, void (*error_func)(const char *), struct passwd *pw,
char **envp)
{
ecode_e ecode = e_none;
entry *e;
int ch;
char cmd[MAX_COMMAND];
char envstr[MAX_ENVSTR];
char **tenvp;
skip_comments(file);
ch = get_char(file);
if (ch == EOF)
return (NULL);
e = calloc(sizeof(entry), 1);
if (e == NULL) {
ecode = e_memory;
goto eof;
}
if (ch == '@') {
ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
if (!strcmp("reboot", cmd)) {
e->flags |= WHEN_REBOOT;
} else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
set_element(e->minute, FIRST_MINUTE, LAST_MINUTE,
FIRST_MINUTE);
set_element(e->hour, FIRST_HOUR, LAST_HOUR, FIRST_HOUR);
set_element(e->dom, FIRST_DOM, LAST_DOM, FIRST_DOM);
set_element(e->month, FIRST_MONTH, LAST_MONTH,
FIRST_MONTH);
set_range(e->dow, FIRST_DOW, LAST_DOW, FIRST_DOW,
LAST_DOW, 1);
e->flags |= DOW_STAR;
} else if (!strcmp("monthly", cmd)) {
set_element(e->minute, FIRST_MINUTE, LAST_MINUTE,
FIRST_MINUTE);
set_element(e->hour, FIRST_HOUR, LAST_HOUR, FIRST_HOUR);
set_element(e->dom, FIRST_DOM, LAST_DOM, FIRST_DOM);
set_range(e->month, FIRST_MONTH, LAST_MONTH,
FIRST_MONTH, LAST_MONTH, 1);
set_range(e->dow, FIRST_DOW, LAST_DOW, FIRST_DOW,
LAST_DOW, 1);
e->flags |= DOW_STAR;
} else if (!strcmp("weekly", cmd)) {
set_element(e->minute, FIRST_MINUTE, LAST_MINUTE,
FIRST_MINUTE);
set_element(e->hour, FIRST_HOUR, LAST_HOUR, FIRST_HOUR);
set_range(e->dom, FIRST_DOM, LAST_DOM, FIRST_DOM,
LAST_DOM, 1);
set_range(e->month, FIRST_MONTH, LAST_MONTH,
FIRST_MONTH, LAST_MONTH, 1);
set_element(e->dow, FIRST_DOW, LAST_DOW, FIRST_DOW);
e->flags |= DOW_STAR;
} else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
set_element(e->minute, FIRST_MINUTE, LAST_MINUTE,
FIRST_MINUTE);
set_element(e->hour, FIRST_HOUR, LAST_HOUR, FIRST_HOUR);
set_range(e->dom, FIRST_DOM, LAST_DOM, FIRST_DOM,
LAST_DOM, 1);
set_range(e->month, FIRST_MONTH, LAST_MONTH,
FIRST_MONTH, LAST_MONTH, 1);
set_range(e->dow, FIRST_DOW, LAST_DOW, FIRST_DOW,
LAST_DOW, 1);
} else if (!strcmp("hourly", cmd)) {
set_element(e->minute, FIRST_MINUTE, LAST_MINUTE,
FIRST_MINUTE);
set_range(e->hour, FIRST_HOUR, LAST_HOUR, FIRST_HOUR,
LAST_HOUR, 1);
set_range(e->dom, FIRST_DOM, LAST_DOM, FIRST_DOM,
LAST_DOM, 1);
set_range(e->month, FIRST_MONTH, LAST_MONTH,
FIRST_MONTH, LAST_MONTH, 1);
set_range(e->dow, FIRST_DOW, LAST_DOW,
FIRST_DOW, LAST_DOW, 1);
e->flags |= HR_STAR;
} else {
ecode = e_timespec;
goto eof;
}
Skip_Blanks(ch, file);
if (ch == EOF || ch == '\n') {
ecode = e_cmd;
goto eof;
}
} else {
if (ch == '*')
e->flags |= MIN_STAR;
ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
NULL, ch, file);
if (ch == EOF) {
ecode = e_minute;
goto eof;
}
if (ch == '*')
e->flags |= HR_STAR;
ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
NULL, ch, file);
if (ch == EOF) {
ecode = e_hour;
goto eof;
}
if (ch == '*')
e->flags |= DOM_STAR;
ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
NULL, ch, file);
if (ch == EOF) {
ecode = e_dom;
goto eof;
}
ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
MonthNames, ch, file);
if (ch == EOF) {
ecode = e_month;
goto eof;
}
if (ch == '*')
e->flags |= DOW_STAR;
ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
DowNames, ch, file);
if (ch == EOF) {
ecode = e_dow;
goto eof;
}
}
if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
bit_set(e->dow, 0);
bit_set(e->dow, 7);
}
if (ch == '\n' || ch == '*') {
ecode = e_cmd;
goto eof;
}
if (!pw) {
char *username = cmd;
unget_char(ch, file);
ch = get_string(username, MAX_COMMAND, file, " \t\n");
if (ch == EOF || ch == '\n' || ch == '*') {
ecode = e_cmd;
goto eof;
}
Skip_Blanks(ch, file)
pw = getpwnam(username);
if (pw == NULL) {
ecode = e_username;
goto eof;
}
}
if ((e->pwd = pw_dup(pw)) == NULL) {
ecode = e_memory;
goto eof;
}
explicit_bzero(e->pwd->pw_passwd, strlen(e->pwd->pw_passwd));
if ((e->envp = env_copy(envp)) == NULL) {
ecode = e_memory;
goto eof;
}
if (!env_get("SHELL", e->envp)) {
if (snprintf(envstr, sizeof envstr, "SHELL=%s", _PATH_BSHELL) >=
sizeof(envstr))
syslog(LOG_ERR, "(CRON) ERROR (can't set SHELL)");
else {
if ((tenvp = env_set(e->envp, envstr)) == NULL) {
ecode = e_memory;
goto eof;
}
e->envp = tenvp;
}
}
if (!env_get("HOME", e->envp)) {
if (snprintf(envstr, sizeof envstr, "HOME=%s", pw->pw_dir) >=
sizeof(envstr))
syslog(LOG_ERR, "(CRON) ERROR (can't set HOME)");
else {
if ((tenvp = env_set(e->envp, envstr)) == NULL) {
ecode = e_memory;
goto eof;
}
e->envp = tenvp;
}
}
if (snprintf(envstr, sizeof envstr, "LOGNAME=%s", pw->pw_name) >=
sizeof(envstr))
syslog(LOG_ERR, "(CRON) ERROR (can't set LOGNAME)");
else {
if ((tenvp = env_set(e->envp, envstr)) == NULL) {
ecode = e_memory;
goto eof;
}
e->envp = tenvp;
}
if (snprintf(envstr, sizeof envstr, "USER=%s", pw->pw_name) >=
sizeof(envstr))
syslog(LOG_ERR, "(CRON) ERROR (can't set USER)");
else {
if ((tenvp = env_set(e->envp, envstr)) == NULL) {
ecode = e_memory;
goto eof;
}
e->envp = tenvp;
}
while (ch == '-') {
int flags = 0, loop = 1;
while (loop) {
switch (ch = get_char(file)) {
case 'n':
flags |= MAIL_WHEN_ERR;
break;
case 'q':
flags |= DONT_LOG;
break;
case 's':
flags |= SINGLE_JOB;
break;
case ' ':
case '\t':
Skip_Blanks(ch, file)
loop = 0;
break;
case EOF:
case '\n':
ecode = e_cmd;
goto eof;
default:
ecode = e_flags;
goto eof;
}
}
if (flags == 0) {
ecode = e_flags;
goto eof;
}
e->flags |= flags;
}
unget_char(ch, file);
ch = get_string(cmd, MAX_COMMAND, file, "\n");
if (ch == EOF) {
ecode = e_cmd;
goto eof;
}
if ((e->cmd = strdup(cmd)) == NULL) {
ecode = e_memory;
goto eof;
}
return (e);
eof:
if (e)
free_entry(e);
while (ch != '\n' && !feof(file))
ch = get_char(file);
if (ecode != e_none && error_func)
(*error_func)(ecodes[(int)ecode]);
return (NULL);
}
static int
get_list(bitstr_t *bits, int low, int high, const char *names[],
int ch, FILE *file)
{
int done;
bit_nclear(bits, 0, high - low);
done = FALSE;
while (!done) {
if ((ch = get_range(bits, low, high, names, ch, file)) == EOF)
return (EOF);
if (ch == ',')
ch = get_char(file);
else
done = TRUE;
}
Skip_Nonblanks(ch, file)
Skip_Blanks(ch, file)
return (ch);
}
static int
get_range(bitstr_t *bits, int low, int high, const char *names[],
int ch, FILE *file)
{
int num1, num2, num3, rndstep;
num1 = low;
num2 = high;
rndstep = 0;
if (ch == '*') {
ch = get_char(file);
if (ch == EOF)
return (EOF);
} else {
if (ch != '~') {
ch = get_number(&num1, low, high, names, ch, file, ",-~ \t\n");
if (ch == EOF)
return (EOF);
}
switch (ch) {
case '-':
ch = get_char(file);
if (ch == EOF)
return (EOF);
ch = get_number(&num2, low, high, names, ch, file, "/, \t\n");
if (ch == EOF || num1 > num2)
return (EOF);
break;
case '~':
ch = get_char(file);
if (ch == EOF)
return (EOF);
ch = get_number(&num2, low, high, names, ch, file, "/, \t\n");
if (ch == EOF) {
ch = get_char(file);
if (!strchr("/, \t\n", ch)) {
unget_char(ch, file);
return (EOF);
}
}
if (ch == EOF || num1 > num2) {
unget_char(ch, file);
return (EOF);
}
if (num1 < low || num2 > high)
return (EOF);
if (ch == '/') {
rndstep = 1;
break;
}
num3 = num1;
num1 = arc4random_uniform(num2 - num3 + 1) + num3;
default:
if (set_element(bits, low, high, num1) == EOF) {
unget_char(ch, file);
return (EOF);
}
return (ch);
}
}
if (ch == '/') {
const int max_step = high + 1 - low;
ch = get_char(file);
if (ch == EOF)
return (EOF);
ch = get_number(&num3, 0, max_step, NULL, ch, file, ", \t\n");
if (ch == EOF || num3 == 0)
return (EOF);
if (rndstep) {
num1 += arc4random_uniform(MINIMUM(num3, num2 - num1));
}
} else {
num3 = 1;
}
if (set_range(bits, low, high, num1, num2, num3) == EOF) {
unget_char(ch, file);
return (EOF);
}
return (ch);
}
static int
get_number(int *numptr, int low, int high, const char *names[], int ch,
FILE *file, const char *terms)
{
char temp[MAX_TEMPSTR], *pc;
int len, i;
pc = temp;
len = 0;
while (isdigit((unsigned char)ch)) {
if (++len >= MAX_TEMPSTR)
goto bad;
*pc++ = ch;
ch = get_char(file);
}
*pc = '\0';
if (len != 0) {
const char *errstr;
if (!strchr(terms, ch))
goto bad;
i = strtonum(temp, low, high, &errstr);
if (errstr != NULL)
goto bad;
*numptr = i;
return (ch);
}
if (names) {
while (isalpha((unsigned char)ch)) {
if (++len >= MAX_TEMPSTR)
goto bad;
*pc++ = ch;
ch = get_char(file);
}
*pc = '\0';
if (len != 0 && strchr(terms, ch)) {
for (i = 0; names[i] != NULL; i++) {
if (!strcasecmp(names[i], temp)) {
*numptr = i+low;
return (ch);
}
}
}
}
bad:
unget_char(ch, file);
return (EOF);
}
static int
set_element(bitstr_t *bits, int low, int high, int number)
{
if (number < low || number > high)
return (EOF);
number -= low;
bit_set(bits, number);
return (0);
}
static int
set_range(bitstr_t *bits, int low, int high, int start, int stop, int step)
{
int i;
if (start < low || stop > high)
return (EOF);
start -= low;
stop -= low;
if (step <= 1) {
bit_nset(bits, start, stop);
} else {
if (step > stop + 1)
return (EOF);
for (i = start; i <= stop; i += step)
bit_set(bits, i);
}
return (0);
}