1
0
Fork 0
mirror of https://github.com/pygos/init.git synced 2024-11-05 04:07:10 +01:00

Remove cron daemon, rewritten and split off into seperate repo

Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
David Oberhollenzer 2018-11-21 12:01:39 +01:00
parent dc6358b0e1
commit bf878d08dd
18 changed files with 2 additions and 1266 deletions

View file

@ -21,7 +21,6 @@ dist_man8_MANS =
include lib/Makemodule.am
include cmd/Makemodule.am
include initd/Makemodule.am
include crond/Makemodule.am
install-exec-hook:
(cd $(DESTDIR)$(sbindir); $(LN_S) shutdown reboot)
@ -31,6 +30,3 @@ install-data-local:
(cd $(DESTDIR)$(man8dir); $(LN_S) shutdown.8 reboot.8)
$(MKDIR_P) $(DESTDIR)$(SVCDIR)
$(MKDIR_P) $(DESTDIR)$(TEMPLATEDIR)
if GCROND
$(MKDIR_P) $(DESTDIR)$(GCRONDIR)
endif

View file

@ -1,9 +1,8 @@
# About
This directory contains the source code for a tiny service supervision
framework devised for the Pygos system, consisting of an init daemon,
a _definitely_ non standards compliant cron implementation and various
command line utilities.
framework devised for the Pygos system, consisting of an init daemon and
accompanying command line utilities.
The programs of this package are developed first and foremost for GNU/Linux
@ -46,8 +45,6 @@ command line tools.
See [docs/services.md](docs/services.md) for more information on service
description files.
See [docs/gcron.md](docs/gcron.md) for details on the cron implementation.
## Why

View file

@ -26,11 +26,6 @@ service_CFLAGS = $(AM_CFLAGS)
service_LDFLAGS = $(AM_LDFLAGS)
service_LDADD = libinit.a libcfg.a libutil.a
if GCROND
service_SOURCES += cmd/service/schedule.c
service_SOURCES += cmd/service/unschedule.c
endif
dist_man8_MANS += cmd/shutdown.8 cmd/service/service.8
EXTRA_DIST += $(SRVHEADERS)

View file

@ -1,90 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "servicecmd.h"
static int cmd_schedule(int argc, char **argv)
{
char *target, *linkname, *ptr;
int ret = EXIT_FAILURE;
struct stat sb;
if (check_arguments(argv[0], argc, 2, 2))
return EXIT_FAILURE;
for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr)
;
if (*ptr != '\0') {
fprintf(stderr, "Invalid service name '%s'\n", argv[1]);
tell_read_help(argv[0]);
return EXIT_FAILURE;
}
if (asprintf(&target, "%s/%s.gcron", TEMPLATEDIR, argv[1]) < 0) {
perror("asprintf");
return EXIT_FAILURE;
}
if (stat(target, &sb)) {
fprintf(stderr, "%s: %s\n", target, strerror(errno));
goto out_tgt;
}
if ((sb.st_mode & S_IFMT) != S_IFREG) {
fprintf(stderr, "%s: must be a regular file\n", target);
goto out_tgt;
}
if (asprintf(&linkname, "%s/%s", GCRONDIR, argv[1]) < 0) {
perror("asprintf");
goto out_tgt;
}
if (symlink(target, linkname)) {
fprintf(stderr, "creating symlink '%s' -> '%s: %s\n",
linkname, target, strerror(errno));
goto out;
}
ret = EXIT_SUCCESS;
out:
free(linkname);
out_tgt:
free(target);
return ret;
}
static command_t schedule = {
.cmd = "schedule",
.usage = "<name>",
.s_desc = "enable a gcrond service",
.l_desc = "This marks a gcrond service as enabled by creating a "
"symlink in " GCRONDIR " pointing to a template file in "
TEMPLATEDIR " with a .gcron extension.",
.run_cmd = cmd_schedule,
};
REGISTER_COMMAND(schedule)

View file

@ -28,12 +28,6 @@ configuration directory, pointing to the service template file.
An optional argument can be supplied to parameterize the template.
.TP
.BR schedule " " \fI<service>\fP
If built with support for gcrond, enable a gcron service by creating a symlink
in the gcrond configuration directory, pointing to the service file.
The extension \fB.gcron\fP is automatically appended to the service name.
.TP
.BR disable " " \fI<service>\fP " " \fI[arguments]\fP
Disable (but do not stop) a system service by removing the corresponding
symlink in the configuration directory.
@ -41,10 +35,6 @@ symlink in the configuration directory.
If the service is parameterized, arguments have to be specified to disable
the desired service instance.
.TP
.BR unschedule " " \fI<service>\fP
If built with support for gcrond, disable a gcron service by removing the
corresponding symlink in the gcron configuration directory.
.TP
.BR dumpscript " " \fI<service>\fP " " \fI[arguments]\fP
Parse a service file from and produce a pseudo shell script containing the
exact commands executed when starting the service.

View file

@ -1,82 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "servicecmd.h"
static int cmd_unschedule(int argc, char **argv)
{
int ret = EXIT_FAILURE;
char *linkname, *ptr;
struct stat sb;
if (check_arguments(argv[0], argc, 2, 2))
return EXIT_FAILURE;
for (ptr = argv[1]; isalnum(*ptr) || *ptr == '_'; ++ptr)
;
if (*ptr != '\0') {
fprintf(stderr, "Invalid service name '%s'\n", argv[1]);
tell_read_help(argv[0]);
return EXIT_FAILURE;
}
if (asprintf(&linkname, "%s/%s.gcron", GCRONDIR, argv[1]) < 0) {
perror("asprintf");
return EXIT_FAILURE;
}
if (lstat(linkname, &sb)) {
fprintf(stderr, "lstat %s: %s\n", linkname, strerror(errno));
goto out;
}
if ((sb.st_mode & S_IFMT) != S_IFLNK) {
fprintf(stderr, "error: '%s' is not a symlink!", linkname);
goto out;
}
if (unlink(linkname)) {
fprintf(stderr, "removing %s: %s\n",
linkname, strerror(errno));
goto out;
}
ret = EXIT_SUCCESS;
out:
free(linkname);
return ret;
}
static command_t unschedule = {
.cmd = "unschedule",
.usage = "<name>",
.s_desc = "disable a gcrond service",
.l_desc = "This disables a gcrond service by removing the coresponding "
"symlink in " GCRONDIR ".",
.run_cmd = cmd_unschedule,
};
REGISTER_COMMAND(unschedule)

View file

@ -36,20 +36,8 @@ UL_WARN_ADD([-pedantic])
AC_SUBST([WARN_CFLAGS])
AC_ARG_WITH([gcrond],
[AS_HELP_STRING([--without-gcrond], [Build without gcron daemon])],
[case "${withval}" in
yes) AM_CONDITIONAL([GCROND], [true]) ;;
no) AM_CONDITIONAL([GCROND], [false]) ;;
*) AC_MSG_ERROR([bad value ${withval} for --without-gcron]) ;;
esac],
[AM_CONDITIONAL([GCROND], [true])])
AC_CONFIG_HEADERS([lib/include/config.h])
AC_DEFINE_DIR(SVCDIR, sysconfdir/init.d, [Startup service directory])
AC_DEFINE_DIR(GCRONDIR, sysconfdir/gcron.d, [Cron service directory])
AC_DEFINE_DIR(TEMPLATEDIR, datadir/init, [Service template directory])
AC_DEFINE_DIR(SCRIPTDIR, libexecdir/init, [Helper script directory])
AC_DEFINE_DIR(SOCKDIR, localstatedir/run, [Directory for initd socket])

View file

@ -1,11 +0,0 @@
if GCROND
gcrond_SOURCES = crond/main.c crond/gcrond.h crond/runjob.c
gcrond_SOURCES += crond/rdcron.c crond/delcron.c crond/crontab.c
gcrond_SOURCES += crond/cronscan.c crond/crontab.h
gcrond_CPPFLAGS = $(AM_CPPFLAGS)
gcrond_CFLAGS = $(AM_CFLAGS)
gcrond_LDFLAGS = $(AM_LDFLAGS)
gcrond_LDADD = libcfg.a libutil.a
sbin_PROGRAMS += gcrond
endif

View file

@ -1,77 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stddef.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include "crontab.h"
int cronscan(const char *directory, crontab_t **list)
{
struct dirent *ent;
int dfd, ret = 0;
crontab_t *cron;
DIR *dir;
dir = opendir(directory);
if (dir == NULL) {
perror(directory);
return -1;
}
dfd = dirfd(dir);
if (dfd < 0) {
perror(directory);
closedir(dir);
return -1;
}
for (;;) {
errno = 0;
ent = readdir(dir);
if (ent == NULL) {
if (errno != 0) {
perror(directory);
ret = -1;
}
break;
}
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
continue;
cron = rdcron(dfd, ent->d_name);
if (cron == NULL) {
ret = -1;
continue;
}
cron->next = *list;
*list = cron;
}
closedir(dir);
return ret;
}

View file

@ -1,50 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "crontab.h"
void cron_tm_to_mask(crontab_t *out, struct tm *t)
{
memset(out, 0, sizeof(*out));
out->minute = 1UL << ((unsigned long)t->tm_min);
out->hour = 1 << t->tm_hour;
out->dayofmonth = 1 << (t->tm_mday - 1);
out->month = 1 << t->tm_mon;
out->dayofweek = 1 << t->tm_wday;
}
bool cron_should_run(const crontab_t *t, const crontab_t *mask)
{
if ((t->minute & mask->minute) == 0)
return false;
if ((t->hour & mask->hour) == 0)
return false;
if ((t->dayofmonth & mask->dayofmonth) == 0)
return false;
if ((t->month & mask->month) == 0)
return false;
if ((t->dayofweek & mask->dayofweek) == 0)
return false;
return true;
}

View file

@ -1,55 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CRONTAB_H
#define CRONTAB_H
#include <sys/types.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include "service.h"
typedef struct crontab_t {
struct crontab_t *next;
exec_t *exec;
char *ctty;
uid_t uid;
gid_t gid;
uint64_t minute;
uint32_t hour;
uint32_t dayofmonth;
uint16_t month;
uint8_t dayofweek;
unsigned int tty_truncate : 1;
} crontab_t;
crontab_t *rdcron(int dirfd, const char *filename);
void delcron(crontab_t *cron);
int cronscan(const char *directory, crontab_t **list);
void cron_tm_to_mask(crontab_t *out, struct tm *t);
bool cron_should_run(const crontab_t *t, const crontab_t *mask);
#endif /* CRONTAB_H */

View file

@ -1,38 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "crontab.h"
void delcron(crontab_t *cron)
{
exec_t *e;
if (cron == NULL)
return;
while (cron->exec != NULL) {
e = cron->exec;
cron->exec = e->next;
free(e);
}
free(cron->ctty);
free(cron);
}

View file

@ -1,37 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef GCROND_H
#define GCROND_H
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include "crontab.h"
#include "config.h"
#include "util.h"
int runjob(crontab_t *tab);
#endif /* GCROND_H */

View file

@ -1,135 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "gcrond.h"
static crontab_t *jobs;
static sig_atomic_t run = 1;
static sig_atomic_t rescan = 1;
static void read_config(void)
{
if (cronscan(GCRONDIR, &jobs)) {
fputs("Error reading configuration. Continuing anyway.\n",
stderr);
}
}
static void cleanup_config(void)
{
crontab_t *t;
while (jobs != NULL) {
t = jobs;
jobs = jobs->next;
delcron(t);
}
}
static int calc_timeout(void)
{
time_t now = time(NULL), future;
struct tm tmstruct;
crontab_t mask, *t;
int minutes;
for (minutes = 0; minutes < 120; ++minutes) {
future = now + minutes * 60;
localtime_r(&future, &tmstruct);
cron_tm_to_mask(&mask, &tmstruct);
for (t = jobs; t != NULL; t = t->next) {
if (cron_should_run(t, &mask))
goto out;
}
}
out:
return minutes ? minutes * 60 : 60;
}
static void runjobs(void)
{
time_t now = time(NULL);
struct tm tmstruct;
crontab_t mask, *t;
localtime_r(&now, &tmstruct);
cron_tm_to_mask(&mask, &tmstruct);
for (t = jobs; t != NULL; t = t->next) {
if (cron_should_run(t, &mask))
runjob(t);
}
}
static void sighandler(int signo)
{
pid_t pid;
switch (signo) {
case SIGINT:
case SIGTERM:
run = 0;
break;
case SIGHUP:
rescan = 1;
break;
case SIGCHLD:
while ((pid = waitpid(-1, NULL, WNOHANG)) != -1)
;
break;
}
}
int main(void)
{
struct timespec stime;
struct sigaction act;
int timeout;
memset(&act, 0, sizeof(act));
act.sa_handler = sighandler;
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
sigaction(SIGHUP, &act, NULL);
sigaction(SIGCHLD, &act, NULL);
while (run) {
if (rescan == 1) {
cleanup_config();
read_config();
timeout = 60;
rescan = 0;
} else {
runjobs();
timeout = calc_timeout();
}
stime.tv_sec = timeout;
stime.tv_nsec = 0;
while (nanosleep(&stime, &stime) != 0 && run && !rescan) {
if (errno != EINTR) {
perror("nanosleep");
break;
}
}
}
return EXIT_SUCCESS;
}

View file

@ -1,503 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include "crontab.h"
#include "libcfg.h"
#include "util.h"
static const enum_map_t weekday[] = {
{ "MON", 1 },
{ "TUE", 2 },
{ "WED", 3 },
{ "THU", 4 },
{ "FRI", 5 },
{ "SAT", 6 },
{ "SUN", 0 },
};
static const enum_map_t month[] = {
{ "JAN", 1 },
{ "FEB", 2 },
{ "MAR", 3 },
{ "APR", 4 },
{ "MAY", 5 },
{ "JUN", 6 },
{ "JUL", 7 },
{ "AUG", 8 },
{ "SEP", 9 },
{ "OCT", 10 },
{ "NOV", 11 },
{ "DEC", 12 },
};
static const struct {
const char *macro;
crontab_t tab;
} intervals[] = {
{
.macro = "yearly",
.tab = {
.minute = 0x01,
.hour = 0x01,
.dayofmonth = 0x01,
.month = 0x01,
.dayofweek = 0xFF
},
}, {
.macro = "annually",
.tab = {
.minute = 0x01,
.hour = 0x01,
.dayofmonth = 0x01,
.month = 0x01,
.dayofweek = 0xFF
},
}, {
.macro = "monthly",
.tab = {
.minute = 0x01,
.hour = 0x01,
.dayofmonth = 0x01,
.month = 0xFFFF,
.dayofweek = 0xFF
},
}, {
.macro = "weekly",
.tab = {
.minute = 0x01,
.hour = 0x01,
.dayofmonth = 0xFFFFFFFF,
.month = 0xFFFF,
.dayofweek = 0x01
},
}, {
.macro = "daily",
.tab = {
.minute = 0x01,
.hour = 0x01,
.dayofmonth = 0xFFFFFFFF,
.month = 0xFFFF,
.dayofweek = 0xFF
},
}, {
.macro = "hourly",
.tab = {
.minute = 0x01,
.hour = 0xFFFFFFFF,
.dayofmonth = 0xFFFFFFFF,
.month = 0xFFFF,
.dayofweek = 0xFF
},
},
};
/*****************************************************************************/
static int try_unescape(char *arg, rdline_t *rd)
{
if (unescape(arg)) {
fprintf(stderr, "%s: %zu: malformed string constant\n",
rd->filename, rd->lineno);
return -1;
}
return 0;
}
static char *try_strdup(const char *str, rdline_t *rd)
{
char *out = strdup(str);
if (out == NULL) {
fprintf(stderr, "%s: %zu: out of memory\n",
rd->filename, rd->lineno);
}
return out;
}
static char *readnum(char *line, int *out, int minval, int maxval,
const enum_map_t *mnemonic, rdline_t *rd)
{
int i, temp, value = 0;
const enum_map_t *ev;
if (!isdigit(*line)) {
if (!mnemonic)
goto fail_mn;
for (i = 0; isalnum(line[i]); ++i)
;
if (i == 0)
goto fail_mn;
temp = line[i];
line[i] = '\0';
ev = enum_by_name(mnemonic, line);
if (!ev) {
fprintf(stderr, "%s: %zu: unexpected '%s'",
rd->filename, rd->lineno, line);
}
line[i] = temp;
if (!ev)
return NULL;
*out = ev->value;
return line + i;
}
while (isdigit(*line)) {
i = ((*(line++)) - '0');
if (value > (maxval - i) / 10)
goto fail_of;
value = value * 10 + i;
}
if (value < minval)
goto fail_uf;
*out = value;
return line;
fail_of:
fprintf(stderr, "%s: %zu: value exceeds maximum (%d > %d)\n",
rd->filename, rd->lineno, value, maxval);
return NULL;
fail_uf:
fprintf(stderr, "%s: %zu: value too small (%d < %d)\n",
rd->filename, rd->lineno, value, minval);
return NULL;
fail_mn:
fprintf(stderr, "%s: %zu: expected numeric value",
rd->filename, rd->lineno);
return NULL;
}
static char *readfield(char *line, uint64_t *out, int minval, int maxval,
const enum_map_t *mnemonic, rdline_t *rd)
{
int value, endvalue, step;
uint64_t v = 0;
next:
if (*line == '*') {
++line;
value = minval;
endvalue = maxval;
} else {
line = readnum(line, &value, minval, maxval, mnemonic, rd);
if (!line)
goto fail;
if (*line == '-') {
line = readnum(line + 1, &endvalue, minval, maxval,
mnemonic, rd);
if (!line)
goto fail;
} else {
endvalue = value;
}
}
if (endvalue < value)
goto fail;
if (*line == '/') {
line = readnum(line + 1, &step, 1, maxval + 1, NULL, rd);
if (!line)
goto fail;
} else {
step = 1;
}
while (value <= endvalue) {
v |= 1UL << (unsigned long)(value - minval);
value += step;
}
if (*line == ',' || *line == ' ') {
++line;
goto next;
}
if (*line != '\0')
goto fail;
*out = v;
return line;
fail:
fprintf(stderr, "%s: %zu: invalid time range expression\n",
rd->filename, rd->lineno);
return NULL;
}
/*****************************************************************************/
static int cron_exec(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
exec_t *e, *end;
(void)flags;
e = calloc(1, sizeof(*e) + strlen(arg) + 1);
if (e == NULL) {
fprintf(stderr, "%s: %zu: out of memory\n",
rd->filename, rd->lineno);
return -1;
}
strcpy(e->args, arg);
e->argc = pack_argv(e->args);
if (e->argc < 0) {
fprintf(stderr, "%s: %zu: malformed string constant\n",
rd->filename, rd->lineno);
return -1;
}
if (cron->exec == NULL) {
cron->exec = e;
} else {
for (end = cron->exec; end->next != NULL; end = end->next)
;
end->next = e;
}
return 0;
}
static int cron_hour(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
uint64_t value;
(void)flags;
if (!readfield(arg, &value, 0, 23, NULL, rd))
return -1;
cron->hour = value;
return 0;
}
static int cron_minute(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
uint64_t value;
(void)flags;
if (!readfield(arg, &value, 0, 59, NULL, rd))
return -1;
cron->minute = value;
return 0;
}
static int cron_dayofmonth(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
uint64_t value;
(void)flags;
if (!readfield(arg, &value, 1, 31, NULL, rd))
return -1;
cron->dayofmonth = value;
return 0;
}
static int cron_dayofweek(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
uint64_t value;
(void)flags;
if (!readfield(arg, &value, 0, 6, weekday, rd))
return -1;
cron->dayofweek = value;
return 0;
}
static int cron_month(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
uint64_t value;
(void)flags;
if (!readfield(arg, &value, 1, 12, month, rd))
return -1;
cron->month = value;
return 0;
}
static int cron_interval(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
size_t i;
(void)flags;
for (i = 0; i < ARRAY_SIZE(intervals); ++i) {
if (!strcmp(intervals[i].macro, arg)) {
cron->minute = intervals[i].tab.minute;
cron->hour = intervals[i].tab.hour;
cron->dayofmonth = intervals[i].tab.dayofmonth;
cron->month = intervals[i].tab.month;
cron->dayofweek = intervals[i].tab.dayofweek;
return 0;
}
}
fprintf(stderr, "%s: %zu: unknown interval '%s'\n",
rd->filename, rd->lineno, arg);
return -1;
}
static int cron_user(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
struct passwd *pwd;
bool isnumeric;
char *ptr;
int value;
(void)flags;
for (ptr = arg; isdigit(*ptr); ++ptr)
;
isnumeric = (*ptr == '\0');
pwd = getpwnam(arg);
if (pwd == NULL && !isnumeric) {
fprintf(stderr, "%s: %zu: unknown user '%s'\n",
rd->filename, rd->lineno, arg);
return -1;
}
if (pwd != NULL) {
cron->uid = pwd->pw_uid;
} else {
if (readnum(arg, &value, 0, INT_MAX, NULL, rd))
return -1;
cron->uid = value;
}
return 0;
}
static int cron_group(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
struct group *group;
bool isnumeric;
char *ptr;
int value;
(void)flags;
for (ptr = arg; isdigit(*ptr); ++ptr)
;
isnumeric = (*ptr == '\0');
group = getgrnam(arg);
if (group == NULL && !isnumeric) {
fprintf(stderr, "%s: %zu: unknown group '%s'\n",
rd->filename, rd->lineno, arg);
return -1;
}
if (group != NULL) {
cron->gid = group->gr_gid;
} else {
if (readnum(arg, &value, 0, INT_MAX, NULL, rd))
return -1;
cron->gid = value;
}
return 0;
}
static int cron_tty(void *user, char *arg, rdline_t *rd, int flags)
{
crontab_t *cron = user;
(void)flags;
if (strncmp(arg, "truncate", 8) == 0 && isspace(arg[8])) {
cron->tty_truncate = 1;
arg += 8;
while (isspace(*arg))
++arg;
}
if (try_unescape(arg, rd))
return -1;
cron->ctty = try_strdup(arg, rd);
return cron->ctty == NULL ? -1 : 0;
}
static const cfg_param_t cron_params[] = {
{ "hour", 0, cron_hour },
{ "minute", 0, cron_minute },
{ "dayofmonth", 0, cron_dayofmonth },
{ "dayofweek", 0, cron_dayofweek },
{ "month", 0, cron_month },
{ "interval", 0, cron_interval },
{ "user", 0, cron_user },
{ "group", 0, cron_group },
{ "tty", 0, cron_tty },
{ "exec", 1, cron_exec },
};
crontab_t *rdcron(int dirfd, const char *filename)
{
crontab_t *cron = NULL;
rdline_t rd;
int ret;
if (rdline_init(&rd, dirfd, filename, 0, NULL))
return NULL;
cron = calloc(1, sizeof(*cron));
if (cron == NULL) {
fputs("out of memory\n", stderr);
goto out;
}
cron->minute = 0xFFFFFFFFFFFFFFFFUL;
cron->hour = 0xFFFFFFFF;
cron->dayofmonth = 0xFFFFFFFF;
cron->month = 0xFFFF;
cron->dayofweek = 0xFF;
ret = rdcfg(cron, &rd, cron_params, ARRAY_SIZE(cron_params), 0);
if (ret) {
delcron(cron);
cron = NULL;
}
out:
rdline_cleanup(&rd);
return cron;
}

View file

@ -1,86 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "gcrond.h"
int runjob(crontab_t *tab)
{
struct sigaction act;
pid_t pid;
exec_t *e;
int ret;
if (tab->exec == NULL)
return 0;
pid = fork();
if (pid == -1) {
perror("fork");
return -1;
}
if (pid != 0)
return 0;
/* XXX: inside the child process */
memset(&act, 0, sizeof(act));
act.sa_handler = SIG_DFL;
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
sigaction(SIGHUP, &act, NULL);
sigaction(SIGCHLD, &act, NULL);
if (setup_tty(tab->ctty, tab->tty_truncate))
exit(EXIT_FAILURE);
if (tab->gid != 0) {
if (setresgid(tab->gid, tab->gid, tab->gid)) {
perror("setgid");
exit(EXIT_FAILURE);
}
}
if (tab->uid != 0) {
if (setresuid(tab->uid, tab->uid, tab->uid)) {
perror("setuid");
exit(EXIT_FAILURE);
}
}
if (tab->exec->next == NULL)
argv_exec(tab->exec);
for (e = tab->exec; e != NULL; e = e->next) {
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0)
argv_exec(e);
while (waitpid(pid, &ret, 0) != pid)
;
ret = WIFEXITED(ret) ? WEXITSTATUS(ret) : EXIT_FAILURE;
if (ret != EXIT_SUCCESS)
break;
}
exit(ret);
}

View file

@ -13,10 +13,6 @@ Currently available service commands are:
* disable - disable a service. If the service is parameterized, requires the
same arguments used for enabling, to disable the specific instance of the
service.
* schedule - enable a gcrond service. Only available if this package is built
with gcrond.
* unschedule - disnable a gcrond service. Only available if this package is
built with gcrond.
* dumpscript - generate an equivalent shell script from the `exec` lines of
a service after applying all parameter substitutions.
* list - list all enabled service. A target can be specified to only list

View file

@ -1,62 +0,0 @@
# Gcron
Gcron is a small daemon that executes batch commands once a certain
condition is met.
In a typical installation, it reads configuration files from `/etc/gcron.d`.
If used together with the init system in this package, the `service` command
can be used to administer symlinks in that directory, pointing
to `/usr/share/init/<name>.gcron`.
Each file in the configuration directory represents a single scheduled batch
job. The syntax and most of the keywords are similar to `initd` service files
(See [services.md](services.md)).
## Cron Style Patterns
The following keywords can be used to specify classic cron style patterns for
when a job should be run:
* `hour`
* `minute`
* `dayofmonth`
* `dayofweek`
* `month`
For each of those keywords, a comma separated sequence of times can be
specified. Time ranges can be specified using the syntax `<start>-<end>`,
or using `*` for every possible value. A sequence (either range or star)
can be suffixed with `/<step>` to specify an increment.
For instance, `minute */5` means every five minutes and `minute 15-30/2`
means every two minutes between quarter past and half past.
In addition to numeric values, the keywords `dayofweek` and `month` allow
specifying 3 letter, uppercase week day and moth names such as `MON`, `TUE`,
etc and `JAN`, `FEB`, ...
The job is only run when all specified conditions are met. Omitting a field
is the same as specifying `*`.
## Named Intervals
Alternatively to the above, the keyword `interval` can be used. The following
intervals can be specified:
* `yearly` or `annually` means on every January the first at midnight.
* `monthly` means on every first of the month at midnight.
* `weekly` means every Sunday at midnight.
* `daily` means every day at midnight.
* `hourly` means every first minute of the hour.
## Command Specification
To specify *what* should be done once the condition is met, the following
keywords can be used:
* `exec` - the command to run. Multiple commands can be grouped
using curly braces.
* `user` - a user name or ID to set before running the commands.
* `group` - a group name or ID to set before running the commands.
* `tty` - similar to init service files, the controlling tty or output file
for the batch commands. Like init service files, the `truncate` keyword
can be used.