diff --git a/.gitignore b/.gitignore
index ca9c061..014b055 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ runsvc
syslog
usyslogd
klogd
+gcrond
services/sigkill
services/sigterm
diff --git a/Makefile.am b/Makefile.am
index 063a8e8..806b777 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,6 +20,7 @@ dist_man8_MANS =
include lib/Makemodule.am
include cmd/Makemodule.am
include initd/Makemodule.am
+include crond/Makemodule.am
include scripts/Makemodule.am
include services/Makemodule.am
include syslogd/Makemodule.am
@@ -51,6 +52,10 @@ install-data-local:
$(LN_S) $(TEMPLATEDIR)/ifcfg $(DESTDIR)$(SVCDIR)/ifcfg
$(LN_S) $(TEMPLATEDIR)/modules $(DESTDIR)$(SVCDIR)/modules
$(LN_S) $(TEMPLATEDIR)/network $(DESTDIR)$(SVCDIR)/network
+if GCROND
+ $(MKDIR_P) $(DESTDIR)$(GCRONDIR)
+ $(LN_S) $(TEMPLATEDIR)/gcrond $(DESTDIR)$(SVCDIR)/gcrond
+endif
if USYSLOGD
$(LN_S) $(TEMPLATEDIR)/usyslogd $(DESTDIR)$(SVCDIR)/usyslogd
endif
diff --git a/cmd/runsvc/runsvc.c b/cmd/runsvc/runsvc.c
index 9e646e7..e36010b 100644
--- a/cmd/runsvc/runsvc.c
+++ b/cmd/runsvc/runsvc.c
@@ -17,51 +17,6 @@
*/
#include "runsvc.h"
-static int setup_tty(service_t *svc)
-{
- int fd;
-
- if (svc->ctty != NULL) {
- fd = open(svc->ctty, O_RDWR);
- if (fd < 0) {
- perror(svc->ctty);
- return -1;
- }
-
- if (svc->flags & SVC_FLAG_TRUNCATE_OUT)
- ftruncate(fd, 0);
-
- close(STDIN_FILENO);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
-
- setsid();
-
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- close(fd);
- }
-
- return 0;
-}
-
-/*****************************************************************************/
-
-static NORETURN void argv_exec(exec_t *e)
-{
- char **argv = alloca(e->argc + 1), *ptr;
- int i;
-
- for (ptr = e->args, i = 0; i < e->argc; ++i, ptr += strlen(ptr) + 1)
- argv[i] = ptr;
-
- argv[i] = NULL;
- execvp(argv[0], argv);
- perror(argv[0]);
- exit(EXIT_FAILURE);
-}
-
static int runlst_wait(exec_t *list)
{
pid_t ret, pid;
@@ -128,7 +83,7 @@ int main(int argc, char **argv)
if (initenv())
goto out;
- if (setup_tty(svc))
+ if (setup_tty(svc->ctty, (svc->flags & SVC_FLAG_TRUNCATE_OUT) != 0))
goto out;
if (svc->exec->next == NULL)
diff --git a/configure.ac b/configure.ac
index e0374c5..84ab673 100644
--- a/configure.ac
+++ b/configure.ac
@@ -55,9 +55,19 @@ AC_ARG_WITH([klogd],
esac],
[AM_CONDITIONAL([KLOGD], [true])])
+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])
diff --git a/crond/Makemodule.am b/crond/Makemodule.am
new file mode 100644
index 0000000..51960e9
--- /dev/null
+++ b/crond/Makemodule.am
@@ -0,0 +1,9 @@
+if GCROND
+gcrond_SOURCES = crond/main.c crond/gcrond.h crond/runjob.c
+gcrond_CPPFLAGS = $(AM_CPPFLAGS)
+gcrond_CFLAGS = $(AM_CFLAGS)
+gcrond_LDFLAGS = $(AM_LDFLAGS)
+gcrond_LDADD = libcron.a libinit.a libcfg.a
+
+sbin_PROGRAMS += gcrond
+endif
diff --git a/crond/gcrond.h b/crond/gcrond.h
new file mode 100644
index 0000000..4cc2417
--- /dev/null
+++ b/crond/gcrond.h
@@ -0,0 +1,37 @@
+/* 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 .
+ */
+#ifndef GCROND_H
+#define GCROND_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "crontab.h"
+#include "config.h"
+#include "util.h"
+
+int runjob(crontab_t *tab);
+
+#endif /* GCROND_H */
+
diff --git a/crond/main.c b/crond/main.c
new file mode 100644
index 0000000..d7cc33e
--- /dev/null
+++ b/crond/main.c
@@ -0,0 +1,139 @@
+/* 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 .
+ */
+#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)
+{
+ switch (signo) {
+ case SIGINT:
+ case SIGTERM:
+ run = 0;
+ break;
+ case SIGHUP:
+ rescan = 1;
+ break;
+ }
+}
+
+int main(void)
+{
+ struct timespec stime;
+ struct sigaction act;
+ crontab_t *t;
+ int timeout;
+ pid_t pid;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sighandler;
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGHUP, &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;
+ }
+ }
+
+ while ((pid = waitpid(-1, NULL, WNOHANG)) != -1) {
+ for (t = jobs; t != NULL; t = t->next) {
+ if (t->pid == pid) {
+ t->pid = -1;
+ break;
+ }
+ }
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/crond/runjob.c b/crond/runjob.c
new file mode 100644
index 0000000..7650748
--- /dev/null
+++ b/crond/runjob.c
@@ -0,0 +1,87 @@
+/* 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 .
+ */
+#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) {
+ tab->pid = pid;
+ 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);
+
+ 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);
+}
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index 7620e63..fb6ca92 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -4,7 +4,7 @@ libinit_a_SOURCES = lib/util/delsvc.c lib/util/svcmap.c lib/util/enum_by_name.c
libinit_a_SOURCES += lib/util/rdsvc.c lib/util/svcscan.c lib/util/mksock.c
libinit_a_SOURCES += lib/util/del_svc_list.c lib/util/svc_tsort.c
libinit_a_SOURCES += lib/util/opensock.c lib/util/enum_to_name.c
-libinit_a_SOURCES += lib/util/print_version.c $(HEADRS)
+libinit_a_SOURCES += lib/util/print_version.c lib/util/argv_exec.c $(HEADRS)
libinit_a_CPPFLAGS = $(AM_CPPFLAGS)
libinit_a_CFLAGS = $(AM_CFLAGS)
diff --git a/lib/cron/rdcron.c b/lib/cron/rdcron.c
index 42936ce..c581b7e 100644
--- a/lib/cron/rdcron.c
+++ b/lib/cron/rdcron.c
@@ -490,6 +490,11 @@ crontab_t *rdcron(int dirfd, const char *filename)
}
cron->pid = -1;
+ cron->minute = 0xFFFFFFFFFFFFFFFFUL;
+ cron->hour = 0xFFFFFFFF;
+ cron->dayofmonth = 0xFFFFFFFF;
+ cron->month = 0xFFFF;
+ cron->dayofweek = 0xFF;
rdline_init(&rd, fd, filename, 0, NULL);
ret = rdcfg(cron, &rd, cron_params, ARRAY_SIZE(cron_params), 0);
diff --git a/lib/include/service.h b/lib/include/service.h
index ceaf782..abe4968 100644
--- a/lib/include/service.h
+++ b/lib/include/service.h
@@ -20,6 +20,8 @@
#include
+#include "util.h"
+
enum {
/*
Start the service in the background and continue with
@@ -123,5 +125,9 @@ const char *svc_target_to_string(int target);
int svc_target_from_string(const char *target);
+int setup_tty(const char *tty, bool truncate);
+
+NORETURN void argv_exec(exec_t *e);
+
#endif /* SERVICE_H */
diff --git a/lib/util/argv_exec.c b/lib/util/argv_exec.c
new file mode 100644
index 0000000..e39153c
--- /dev/null
+++ b/lib/util/argv_exec.c
@@ -0,0 +1,67 @@
+/* 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 .
+ */
+#include "service.h"
+
+#include
+#include
+#include
+#include
+#include
+
+int setup_tty(const char *tty, bool truncate)
+{
+ int fd;
+
+ if (tty == NULL)
+ return 0;
+
+ fd = open(tty, O_RDWR);
+ if (fd < 0) {
+ perror(tty);
+ return -1;
+ }
+
+ if (truncate)
+ ftruncate(fd, 0);
+
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ setsid();
+
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ return 0;
+}
+
+void argv_exec(exec_t *e)
+{
+ char **argv = alloca(e->argc + 1), *ptr;
+ int i;
+
+ for (ptr = e->args, i = 0; i < e->argc; ++i, ptr += strlen(ptr) + 1)
+ argv[i] = ptr;
+
+ argv[i] = NULL;
+ execvp(argv[0], argv);
+ perror(argv[0]);
+ exit(EXIT_FAILURE);
+}
diff --git a/services/Makemodule.am b/services/Makemodule.am
index 0cea6ff..ac297a6 100644
--- a/services/Makemodule.am
+++ b/services/Makemodule.am
@@ -18,9 +18,13 @@ if USYSLOGD
init_DATA += services/klogd
endif
+if GCROND
+init_DATA += services/gcrond
+endif
+
EXTRA_DIST += services/sysinit services/vfs services/agetty services/hostname
EXTRA_DIST += services/hwclock services/loopback services/klogd
EXTRA_DIST += services/sync services/sysctl services/tmpfs
EXTRA_DIST += services/dhcpcd services/dhcpcdmaster services/unbound
EXTRA_DIST += services/usyslogd services/dnsmasq services/network
-EXTRA_DIST += services/consolefont
+EXTRA_DIST += services/consolefont services/gcrond
diff --git a/services/gcrond b/services/gcrond
new file mode 100644
index 0000000..0ee1ee5
--- /dev/null
+++ b/services/gcrond
@@ -0,0 +1,5 @@
+description start gcron daemon
+exec gcrond
+type respawn
+target boot
+after network