SummaryRefsLogTreeCommitDiffStats
diff options
context:
space:
mode:
authorMathieu Lirzin <mthl@gnu.org>2017-09-27 23:16:54 +0200
committerMathieu Lirzin <mthl@gnu.org>2017-09-27 23:16:54 +0200
commitd72716ce16e8326f14ff7ae9ca41af5315e94ae4 (patch)
treee208805a5558748bd010dda915a126e6f043308b
parenta0b580448c4b24830ea37190eda53aa84b36cd60 (diff)
parentba294d6a3ba4d086bc9571d62c705ab6eab200e3 (diff)
downloadmcron-d72716ce16e8326f14ff7ae9ca41af5315e94ae4.tar.gz
mcron-d72716ce16e8326f14ff7ae9ca41af5315e94ae4.tar.bz2
mcron-d72716ce16e8326f14ff7ae9ca41af5315e94ae4.zip
Merge branch 'devel'
-rw-r--r--.dir-locals.el10
-rw-r--r--.gitignore40
-rw-r--r--AUTHORS21
-rw-r--r--BUGS16
-rw-r--r--ChangeLog167
-rw-r--r--ChangeLog.old147
-rw-r--r--Makefile.am203
-rw-r--r--README14
-rw-r--r--README--git11
-rw-r--r--TODO6
-rwxr-xr-xbuild-aux/git-version-gen226
-rwxr-xr-xbuild-aux/gitlog-to-changelog492
-rw-r--r--build-aux/guix.scm66
-rw-r--r--build-aux/pre-inst-env.in35
-rw-r--r--build-aux/test-driver.scm193
-rw-r--r--configure.ac78
-rw-r--r--doc/config.texi.in5
-rw-r--r--doc/fdl.texi505
-rw-r--r--doc/mcron.texi (renamed from mcron.texinfo.in)157
-rw-r--r--makefile.am84
-rw-r--r--makefile.ed34
-rw-r--r--mcron.c.template120
-rw-r--r--scm/mcron/config.scm.in35
-rw-r--r--scm/mcron/crontab.scm228
-rw-r--r--scm/mcron/environment.scm105
-rw-r--r--scm/mcron/job-specifier.scm272
-rw-r--r--scm/mcron/main.scm503
-rw-r--r--scm/mcron/mcron-core.scm271
-rw-r--r--src/mcron/base.scm247
-rw-r--r--src/mcron/config.scm.in35
-rw-r--r--src/mcron/environment.scm97
-rw-r--r--src/mcron/job-specifier.scm256
-rw-r--r--src/mcron/redirect.scm (renamed from scm/mcron/redirect.scm)75
-rw-r--r--src/mcron/scripts/cron.scm177
-rw-r--r--src/mcron/scripts/crontab.scm225
-rw-r--r--src/mcron/scripts/mcron.scm136
-rw-r--r--src/mcron/utils.scm116
-rw-r--r--src/mcron/vixie-specification.scm (renamed from scm/mcron/vixie-specification.scm)92
-rw-r--r--src/mcron/vixie-time.scm (renamed from scm/mcron/vixie-time.scm)56
-rw-r--r--src/wrapper.c85
-rw-r--r--tests/environment.scm39
-rw-r--r--tests/job-specifier.scm43
42 files changed, 3629 insertions, 2094 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 0000000..c94ea0c
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,10 @@
+;; Per-directory local variables for GNU Emacs 23 and later.
+
+((nil . ((fill-column . 78)
+ (tab-width . 8)))
+ (c-mode . ((c-file-style . "gnu")
+ (indent-tabs-mode . nil)))
+ (scheme-mode
+ .
+ ((indent-tabs-mode . nil)
+ (eval . (put 'mcron-error 'scheme-indent-function 1)))))
diff --git a/.gitignore b/.gitignore
index f8096eb..44aed99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,22 +1,46 @@
+*.go
+*.log
+*.o
+*.trs
*~
.deps
+.dirstamp
+/bin/cron
+/bin/crontab
+/bin/mcron
+/build-aux/compile
+/build-aux/config.guess
+/build-aux/config.sub
+/build-aux/depcomp
+/build-aux/install-sh
+/build-aux/mdate-sh
+/build-aux/missing
+/build-aux/texinfo.tex
+/doc/config.texi
+/doc/cron.8
+/doc/crontab.1
+/doc/mcron.1
+/doc/mcron.info
+/doc/stamp-vti
+/doc/version.texi
+/mdate-sh
+/.version
INSTALL
+Makefile
+Makefile.in
aclocal.m4
autom4te.cache
compile
+config.cache
+config.h
+config.h.in
config.log
config.scm
config.status
configure
-core.scm
depcomp
install-sh
-makefile
-makefile.in
-mcron
-mcron.c
-mcron.info
-*.o
-mcron.texinfo
missing
+pre-inst-env
+stamp-h1
texinfo.tex
diff --git a/AUTHORS b/AUTHORS
index 821f761..ed0888b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,18 +1,3 @@
-Authors of GNU mcron.
-
- Copyright (C) 2003, 2005, 2006 Dale Mellor
-
- Copying and distribution of this file, with or without modification,
- are permitted in any medium without royalty provided the copyright
- notice and this notice are preserved.
-
-
-
-Dale Mellor (dale_mellor@users.sourceforge.net)
- wrote everything from scratch, with some reference to Paul Vixie's code,
- with the exceptions noted below.
-
-The section of the manual which describes in detail the syntax for Vixie-style
- configuration files is copied verbatim from Paul Vixie's own distribution,
- on the understanding that this is permitted under his copyright notice,
- which is reproduced in its entirety in this section of the manual.
+Dale Mellor <dale_mellor@users.sourceforge.net>
+Mathieu Lirzin <mthl@gnu.org>
+Sergey Poznyakoff <cray@gnu.org.ua>
diff --git a/BUGS b/BUGS
deleted file mode 100644
index 9aaa710..0000000
--- a/BUGS
+++ /dev/null
@@ -1,16 +0,0 @@
-GNU mcron --- BUGS -*-text-*-
-
- Copyright (C) 2003, 2005, 2006 Dale Mellor
-
- Copying and distribution of this file, with or without modification,
- are permitted in any medium without royalty provided the copyright
- notice and this notice are preserved.
-
-
-
-Please send bug reports to bug-mcron@gnu.org.
-
-
-The currently-known bugs are:-
-
- -NONE-
diff --git a/ChangeLog b/ChangeLog
index 15e01c0..fe5459d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,165 +1,4 @@
-2014-05-25 Dale Mellor <dale_mellor@users.sourceforge.net>
+Normally a ChangeLog is generated at "make dist" time and available in
+source tarballs.
- * Juggled build infrastructure so that we can make the minimal man
- page in the proper autotools way.
-
- * configure.ac: version to 1.0.8.
-
-
-2014-04-28 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * We now run against, and require, guile-2.0.
-
- * configure.ac: version to 1.0.7.
-
-
-2012-02-04 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * main.scm: added search for initial files in
- $XDG_CONFIG_HOME/cron directory, defaulting to ~/.config/cron if
- the environment variable is not set) as well as in ~/.cron
- directory (this is in line with the current FreeDesktop.org
- standards).
-
-
-2010-06-13 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * configure.ac: added --enable-no-vixie-clobber argument to
- configure so that the root user can avoid overwriting a legacy
- cron installation.
-
- * mcron.1: added simple, minimal man page using help2man (the
- texinfo file is still the primary documentation source).
-
- * makefile.am: replaced use of mkinstalldirs with install; the
- former is not supplied with the latest automake (1.11).
-
-
-2008-02-21 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * ALL FILES: Replaced version 2 GPL notices with version 3 ones.
-
- * makefile.am: Do not remove COPYING file with make
- maintainer-clean; if you do it will eventually get replaced with
- the old version 2 GPL by a subsequent automake.
-
- * configure.ac: Bumped version to 1.0.4.
-
-
-2008-01-25 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * main.scm (command-type): Files which are listed on the command
- line are assumed to be guile configurations if they do not end in
- .guile or .vixie (previously they were silently ignored).
-
- * main.scm: Argument to --schedule is no longer optional (the
- options system goes really screwy with optional values, usually
- pulling the first non-option argument as a value if one was not
- intended!)
-
- * makefile.am: Moved target-specific CFLAGS and LDFLAGS to global
- AM_* variables, to remove problem with automake requiring
- AM_PROGS_CC_C_O in configure.ac (!)
-
- * Version is currently at 1.0.3.
-
-
-2005-09-02 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * makefile.am, mcron.c.template (main): Modified install-exec-hook
- so that a proper installation of a Vixie-compatible cron only
- takes place if we are root - otherwise only mcron is installed as
- a user-owned program. The guile modules are now installed under
- mcron's shared data directory, not guile's global directories.
-
- * mcron-core.scm: Removed job:advance-time, put the code inline
- where it was called, and changed the instance in the main loop to
- compute the new time based on the current-time, rather than the
- previous job time (this makes things behave more reasonably when a
- laptop awakes from suspend mode).
-
- * Bumped version to 1.0.2.
-
-
-2004-05-15 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * Modified all auxiliary files to reflect that the package is now
- properly homed at www.gnu.org.
-
- * Bumped version to 1.0.1.
-
-
-2003-12-11 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * Modified all auxiliary files to reflect that we are now a GNU
- package.
-
- * Bumped version to 1.0.0.
-
-
-2003-12-07 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * configure.ac: Added switches for files and directories used by
- mcron: --spool-dir, --socket-file, --allow-file, --deny-file,
- --pid-file and --tmp-dir. All the code has been modified to use
- these configure options (including the source for the texinfo
- manual).
-
-
-2003-12-05 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * configure.ac: Added test for guile version >= 1.6.4.
-
- * bumped version to 0.99.4.
-
-
-2003-08-03 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * Third cut, fully functional, modular, production quality, still
- needs testing...
-
- * Pulled all functionality into modules, so it can be incorporated
- into other programs.
-
- * Bumped version to 0.99.3.
-
-
-2003-07-20 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * Second cut, now _really_ fully functional (100% Vixie
- compatible), production quality code, still needs lots of testing
- doing...
-
- * Converted from SIGUP-/var/cron/update to select-/var/cron/socket
- method of communication between crontab and cron.
-
- * Added implicit job which checks every minute for updates to
- /etc/crontab.
-
- * Removed --enable-vixie configuration option - the Vixie programs
- are built and installed by default now.
-
- * Bumped version to 0.99.2.
-
-
-2003-06-28 Dale Mellor <dale_mellor@users.sourceforge.net>
-
- * First cut, fully functional, production quality code, just needs
- testing...
-
- * Broken/incomplete Guile prevents vixie compatibility from
- working - this has been disabled by default in the configuration.
-
- * Version set at 0.99.1
-
-
-
-
-
-
-________________________________________________________________________________
-Copyright (C) 2003, 2005, 2006, 2014 Dale Mellor
-
-Copying and distribution of this file, with or without modification,
-are permitted in any medium without royalty provided the copyright
-notice and this notice are preserved.
+If not, see the Git commit log at <http://git.sv.gnu.org/cgit/mcron.git/>.
diff --git a/ChangeLog.old b/ChangeLog.old
new file mode 100644
index 0000000..09c4147
--- /dev/null
+++ b/ChangeLog.old
@@ -0,0 +1,147 @@
+2014-05-25 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * Juggled build infrastructure so that we can make the minimal man
+ page in the proper autotools way.
+
+ * configure.ac: version to 1.0.8.
+
+2014-04-28 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * We now run against, and require, guile-2.0.
+
+ * configure.ac: version to 1.0.7.
+
+2012-02-04 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * main.scm: added search for initial files in
+ $XDG_CONFIG_HOME/cron directory, defaulting to ~/.config/cron if
+ the environment variable is not set) as well as in ~/.cron
+ directory (this is in line with the current FreeDesktop.org
+ standards).
+
+2010-06-13 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * configure.ac: added --enable-no-vixie-clobber argument to
+ configure so that the root user can avoid overwriting a legacy
+ cron installation.
+
+ * mcron.1: added simple, minimal man page using help2man (the
+ texinfo file is still the primary documentation source).
+
+ * makefile.am: replaced use of mkinstalldirs with install; the
+ former is not supplied with the latest automake (1.11).
+
+2008-02-21 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * ALL FILES: Replaced version 2 GPL notices with version 3 ones.
+
+ * makefile.am: Do not remove COPYING file with make
+ maintainer-clean; if you do it will eventually get replaced with
+ the old version 2 GPL by a subsequent automake.
+
+ * configure.ac: Bumped version to 1.0.4.
+
+2008-01-25 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * main.scm (command-type): Files which are listed on the command
+ line are assumed to be guile configurations if they do not end in
+ .guile or .vixie (previously they were silently ignored).
+
+ * main.scm: Argument to --schedule is no longer optional (the
+ options system goes really screwy with optional values, usually
+ pulling the first non-option argument as a value if one was not
+ intended!)
+
+ * makefile.am: Moved target-specific CFLAGS and LDFLAGS to global
+ AM_* variables, to remove problem with automake requiring
+ AM_PROGS_CC_C_O in configure.ac (!)
+
+ * Version is currently at 1.0.3.
+
+2005-09-02 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * makefile.am, mcron.c.template (main): Modified install-exec-hook
+ so that a proper installation of a Vixie-compatible cron only
+ takes place if we are root - otherwise only mcron is installed as
+ a user-owned program. The guile modules are now installed under
+ mcron's shared data directory, not guile's global directories.
+
+ * mcron-core.scm: Removed job:advance-time, put the code inline
+ where it was called, and changed the instance in the main loop to
+ compute the new time based on the current-time, rather than the
+ previous job time (this makes things behave more reasonably when a
+ laptop awakes from suspend mode).
+
+ * Bumped version to 1.0.2.
+
+2004-05-15 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * Modified all auxiliary files to reflect that the package is now
+ properly homed at www.gnu.org.
+
+ * Bumped version to 1.0.1.
+
+2003-12-11 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * Modified all auxiliary files to reflect that we are now a GNU
+ package.
+
+ * Bumped version to 1.0.0.
+
+2003-12-07 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * configure.ac: Added switches for files and directories used by
+ mcron: --spool-dir, --socket-file, --allow-file, --deny-file,
+ --pid-file and --tmp-dir. All the code has been modified to use
+ these configure options (including the source for the texinfo
+ manual).
+
+2003-12-05 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * configure.ac: Added test for guile version >= 1.6.4.
+
+ * bumped version to 0.99.4.
+
+2003-08-03 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * Third cut, fully functional, modular, production quality, still
+ needs testing...
+
+ * Pulled all functionality into modules, so it can be incorporated
+ into other programs.
+
+ * Bumped version to 0.99.3.
+
+2003-07-20 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * Second cut, now _really_ fully functional (100% Vixie
+ compatible), production quality code, still needs lots of testing
+ doing...
+
+ * Converted from SIGUP-/var/cron/update to select-/var/cron/socket
+ method of communication between crontab and cron.
+
+ * Added implicit job which checks every minute for updates to
+ /etc/crontab.
+
+ * Removed --enable-vixie configuration option - the Vixie programs
+ are built and installed by default now.
+
+ * Bumped version to 0.99.2.
+
+2003-06-28 Dale Mellor <dale_mellor@users.sourceforge.net>
+
+ * First cut, fully functional, production quality code, just needs
+ testing...
+
+ * Broken/incomplete Guile prevents vixie compatibility from
+ working - this has been disabled by default in the configuration.
+
+ * Version set at 0.99.1
+
+________________________________________________________________________________
+Copyright (C) 2003, 2005, 2006, 2014, 2015 Dale Mellor
+
+Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..45bb941
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,203 @@
+## Process this file with automake to produce Makefile.in.
+# Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
+# Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+#
+# This file is part of GNU Mcron.
+#
+# GNU Mcron 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.
+#
+# GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+bin_PROGRAMS = bin/mcron bin/crontab
+sbin_PROGRAMS = bin/cron
+
+AM_CPPFLAGS = -DPACKAGE_LOAD_PATH=\"$(moduledir)\"
+AM_CFLAGS = @GUILE_CFLAGS@
+LDADD = @GUILE_LIBS@
+
+bin_mcron_SOURCES = src/wrapper.c
+bin_mcron_CPPFLAGS = $(AM_CPPFLAGS) -DPROGRAM="\"mcron\""
+bin_mcron_DEPENDENCIES = $(modules:.scm=.go)
+
+bin_cron_SOURCES = src/wrapper.c
+bin_cron_CPPFLAGS = $(AM_CPPFLAGS) -DPROGRAM="\"cron\""
+bin_cron_DEPENDENCIES = $(modules:.scm=.go)
+
+bin_crontab_SOURCES = src/wrapper.c
+bin_crontab_CPPFLAGS = $(AM_CPPFLAGS) -DPROGRAM="\"crontab\""
+bin_crontab_DEPENDENCIES = $(modules:.scm=.go)
+
+dist_mcronmodule_DATA = \
+ src/mcron/base.scm \
+ src/mcron/environment.scm \
+ src/mcron/job-specifier.scm \
+ src/mcron/redirect.scm \
+ src/mcron/utils.scm \
+ src/mcron/vixie-specification.scm \
+ src/mcron/vixie-time.scm
+
+mcronmodule_DATA = \
+ $(dist_mcronmodule_DATA:.scm=.go) \
+ src/mcron/config.scm \
+ src/mcron/config.go
+
+mcronscriptdir = $(mcronmoduledir)/scripts
+dist_mcronscript_DATA = \
+ src/mcron/scripts/cron.scm \
+ src/mcron/scripts/crontab.scm \
+ src/mcron/scripts/mcron.scm
+
+mcronscript_DATA = $(dist_mcronscript_DATA:.scm=.go)
+
+modules = \
+ $(dist_mcronmodule_DATA) \
+ $(dist_mcronscript_DATA) \
+ src/mcron/config.scm
+
+TEST_EXTENSIONS = .scm
+AM_TESTS_ENVIRONMENT = env GUILE_AUTO_COMPILE='0'
+
+SCM_LOG_DRIVER = \
+ $(builddir)/pre-inst-env $(GUILE) \
+ $(srcdir)/build-aux/test-driver.scm
+
+TESTS = \
+ tests/environment.scm \
+ tests/job-specifier.scm
+
+# Unset 'GUILE_LOAD_COMPILED_PATH' altogether while compiling. Otherwise, if
+# $GUILE_LOAD_COMPILED_PATH contains $(mcronmoduledir), we may find .go files
+# in there that are newer than the local .scm files (for instance because the
+# user ran 'make install' recently). When that happens, we end up loading
+# those previously-installed .go files, which may be stale, thereby breaking
+# the whole thing. Set GUILE_AUTO_COMPILE to 0 to avoid auto-compiling guild
+# as a consequence of the previous hack.
+#
+# XXX: Use the C locale for when Guile lacks
+# <http://git.sv.gnu.org/cgit/guile.git/commit/?h=stable-2.0&id=e2c6bf3866d1186c60bacfbd4fe5037087ee5e3f>.
+.scm.go:
+ $(guilec_verbose)$(MKDIR_P) `dirname "$@"` ; \
+ export GUILE_AUTO_COMPILE=0 ; unset GUILE_LOAD_COMPILED_PATH ; \
+ LC_ALL=C \
+ $(top_builddir)/pre-inst-env $(GUILD) compile \
+ --load-path="$(top_builddir)/src" \
+ --load-path="$(top_srcdir)/src" \
+ --warn=format --warn=unbound-variable --warn=arity-mismatch \
+ --target="$(host)" --output="$@" "$<" $(devnull_verbose)
+
+SUFFIXES = .go
+noinst_SCRIPTS = pre-inst-env
+
+BUILT_SOURCES= .version
+.version:
+ $(AM_V_GEN)echo $(VERSION) > $@-t && mv $@-t $@
+
+dist-hook: gen-ChangeLog
+ $(AM_V_GEN)echo $(VERSION) > $(distdir)/.tarball-version
+
+gen_start_date = 2015-06-26
+.PHONY: gen-ChangeLog
+gen-ChangeLog:
+ $(AM_V_GEN)if test -d $(srcdir)/.git; then \
+ log_fix="$(srcdir)/build-aux/git-log-fix"; \
+ test -e "$$log_fix" \
+ && amend_git_log="--amend=$$log_fix" \
+ || amend_git_log=; \
+ $(top_srcdir)/build-aux/gitlog-to-changelog \
+ $$amend_git_log --since=$(gen_start_date) > $(distdir)/cl-t && \
+ { rm -f $(distdir)/ChangeLog && \
+ mv $(distdir)/cl-t $(distdir)/ChangeLog; } \
+ fi
+
+#full program prefix
+fpp = $(DESTDIR)$(bindir)/@real_program_prefix@
+
+install-exec-hook:
+ @if [ "x@NO_VIXIE_CLOBBER@" != "xyes" -a "`id -u`" -eq "0" ]; then \
+ rm -f $(fpp)cron$(EXEEXT) > /dev/null 2>&1; \
+ $(INSTALL) --mode='u=rwx' mcron$(EXEEXT) $(fpp)cron$(EXEEXT); \
+ rm -f $(fpp)crontab$(EXEEXT) > /dev/null 2>&1; \
+ $(INSTALL) --mode='u=rwxs,og=rx' mcron$(EXEEXT) $(fpp)crontab$(EXEEXT); \
+ $(INSTALL) -d --mode='u=rwx' $(DESTDIR)/var/cron; \
+ $(INSTALL) -d --mode='u=rwx,og=rx' $(DESTDIR)/var/run; \
+ $(INSTALL) -d --mode='u=rwx,og=rx' $(DESTDIR)@GUILE_SITE@; \
+ $(INSTALL) -d --mode='u=rwx,og=rx' $(DESTDIR)@GUILE_SITE@/mcron; \
+ elif [ "x@NO_VIXIE_CLOBBER@" = "xyes" ]; then \
+ echo "Not installing Vixie-style programs"; \
+ else \
+ echo "+++ WARNING: NON-ROOT INSTALL: ONLY mcron WILL BE INSTALLED, NOT ANY OF THE VIXIE REPLACEMENT PROGRAMS"; \
+ fi
+
+uninstall-hook:
+ if [ "`id -u`" -eq "0" ]; then \
+ rm -f $(fpp){cron,crontab}$(EXEEXT); \
+ fi
+
+EXTRA_DIST = \
+ build-aux/guix.scm \
+ .version \
+ $(TESTS)
+
+MAINTAINERCLEANFILES = $(dist_man_MANS)
+DISTCLEANFILES = src/config.scm
+CLEANFILES = \
+ $(modules:.scm=.go) \
+ src/mcron/config.go
+
+## --------------- ##
+## Documentation. ##
+## --------------- ##
+
+info_TEXINFOS = doc/mcron.texi
+doc_mcron_TEXINFOS = doc/fdl.texi
+nodist_doc_mcron_TEXINFOS = doc/config.texi
+dist_man_MANS = \
+ $(srcdir)/doc/mcron.1 \
+ $(srcdir)/doc/crontab.1 \
+ $(srcdir)/doc/cron.8
+
+# XXX: Allow the inclusion of 'doc/fdl.texi' and 'doc/config.texi' inside
+# 'doc/mcron.texi' for 'dvi' and 'pdf' targets.
+TEXI2DVI = texi2dvi -I doc
+
+# The 'case' ensures the man pages are only generated if the corresponding
+# source script (the first prerequisite) has been changed. The second
+# prerequisites is solely meant to force these docs to be made only after
+# executables have been compiled.
+gen_man = \
+ case '$?' in \
+ *$<*) $(AM_V_P) && set -x || echo " HELP2MAN $@"; \
+ LANGUAGE= $(top_builddir)/pre-inst-env $(HELP2MAN) \
+ -s $$man_section -S GNU -p $(PACKAGE_TARNAME) -o $@ $$prog;; \
+ *) : ;; \
+ esac
+
+$(srcdir)/doc/mcron.1: src/mcron/scripts/mcron.scm bin/mcron
+ -@prog="mcron"; man_section=1; $(gen_man)
+
+$(srcdir)/doc/crontab.1: src/mcron/scripts/crontab.scm bin/crontab
+ -@prog="crontab"; man_section=1; $(gen_man)
+
+$(srcdir)/doc/cron.8: src/mcron/scripts/cron.scm bin/cron
+ -@prog="cron"; man_section=8; $(gen_man)
+
+## -------------- ##
+## Silent rules. ##
+## -------------- ##
+
+guilec_verbose = $(guilec_verbose_@AM_V@)
+guilec_verbose_ = $(guilec_verbose_@AM_DEFAULT_V@)
+guilec_verbose_0 = @echo " GUILEC " $@;
+
+devnull_verbose = $(devnull_verbose_@AM_V@)
+devnull_verbose_ = $(devnull_verbose_@AM_DEFAULT_V@)
+devnull_verbose_0 = >/dev/null
diff --git a/README b/README
index 9f4f625..057f962 100644
--- a/README
+++ b/README
@@ -17,8 +17,6 @@ likely fail on others (but you never know...).
----------------------------------------------------------------------
IMPORTANT NOTICES
-Read the BUGS file.
-
Do not (yet) install this software on a machine which relies for its
functioning on its current set of crontabs.
@@ -54,13 +52,13 @@ m.mcron, m.cron (or m.crond) and m.crontab.
See the file INSTALL for generic building and installation instructions.
-After installation, read the info file for full instructions for use (typing
-`info mcron' at the command line should suffice). Notes for end users,
-sysadmins, and developers who wish to incorporate mcron into their own programs
-are included here.
+After compilation, read the info file for full instructions for use (typing
+'info -f doc/mcron.info' at the command line should suffice). Notes for end
+users, sysadmins, and developers who wish to incorporate mcron into their own
+programs are included here.
-Known bugs are noted in the BUGS file, and features which might be implemented
-sometime sooner or later are noted in the TODO file.
+Features which might be implemented sometime sooner or later are noted in the
+TODO file.
Please send all other bug reports to bug-mcron@gnu.org. Other mailing lists you
could subscribe to are help-mcron@gnu.org (for help and advice from the
diff --git a/README--git b/README--git
index 43e9890..0b24ded 100644
--- a/README--git
+++ b/README--git
@@ -1,6 +1,7 @@
GNU mcron --- README--git -*-text-*-
Copyright (C) 2012, 2014 Dale Mellor
+ Copyright (C) 2015, 2016 Mathieu Lirzin
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
@@ -10,13 +11,9 @@ GNU mcron --- README--git -*-text-*-
If you have pulled mcron from the GIT repository, these are the steps you will
need to take to build it the first time:
-1) aclocal
-2) autoconf
-3) automake -a (will error)
-4) ./configure (will error)
-5) automake -a
-6) ./configure --prefix={wherever}
-7) make install
+1) autoreconf -vfi
+2) ./configure --prefix={wherever}
+3) make install
After that it should just be a simple matter of typing `make install' when you
diff --git a/TODO b/TODO
index 9ed8bca..2b7329f 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,6 @@
GNU mcron --- TODO -*-text-*-
+ Copyright (C) 2015, 2016 Mathieu Lirzin
Copyright (C) 2003, 2005, 2006, 2014 Dale Mellor
Copying and distribution of this file, with or without modification,
@@ -19,6 +20,11 @@ Maybe in the near future...
core or other users' files up. Then allow scheme code in the system
crontabs.
+ * Provide a test suite using SRFI-64 API.
+ <http://srfi.schemers.org/srfi-64/srfi-64.html>.
+
+ * Internationalize Mcron using GNU Gettext and ask the Translation
+ Project to handle the localization.
There are no plans to actually do the following any time soon...
diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen
new file mode 100755
index 0000000..bd2c4b6
--- /dev/null
+++ b/build-aux/git-version-gen
@@ -0,0 +1,226 @@
+#!/bin/sh
+# Print a version string.
+scriptversion=2016-05-08.18; # UTC
+
+# Copyright (C) 2007-2016 Free Software Foundation, Inc.
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+# produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+# presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+# a checked-out repository. Created with contents that were learned at
+# the last time autoconf was run, and used by git-version-gen. Must not
+# be present in either $(srcdir) or $(builddir) for git-version-gen to
+# give accurate answers during normal development with a checked out tree,
+# but must be present in a tarball when there is no version control system.
+# Therefore, it cannot be used in any dependencies. GNUmakefile has
+# hooks to force a reconfigure at distribution time to get the value
+# correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+# tarball. Usable in dependencies, particularly for files that don't
+# want to depend on config.h but do want to track version changes.
+# Delete this file prior to any autoconf run where you want to rebuild
+# files to pick up a version string change; and leave it stale to
+# minimize rebuild time after unrelated changes to configure sources.
+#
+# As with any generated file in a VC'd directory, you should add
+# /.version to .gitignore, so that you don't accidentally commit it.
+# .tarball-version is never generated in a VC'd directory, so needn't
+# be listed there.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+# [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .version and
+# .tarball-version will exist in distribution tarballs.
+#
+# EXTRA_DIST = $(top_srcdir)/.version
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+# echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+# echo $(VERSION) > $(distdir)/.tarball-version
+
+
+me=$0
+
+version="git-version-gen $scriptversion
+
+Copyright 2011 Free Software Foundation, Inc.
+There is NO warranty. You may redistribute this software
+under the terms of the GNU General Public License.
+For more information about these matters, see the files named COPYING."
+
+usage="\
+Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT]
+Print a version string.
+
+Options:
+
+ --prefix PREFIX prefix of git tags (default 'v')
+ --fallback VERSION
+ fallback version to use if \"git --version\" fails
+
+ --help display this help and exit
+ --version output version information and exit
+
+Running without arguments will suffice in most cases."
+
+prefix=v
+fallback=
+
+while test $# -gt 0; do
+ case $1 in
+ --help) echo "$usage"; exit 0;;
+ --version) echo "$version"; exit 0;;
+ --prefix) shift; prefix=${1?};;
+ --fallback) shift; fallback=${1?};;
+ -*)
+ echo "$0: Unknown option '$1'." >&2
+ echo "$0: Try '--help' for more information." >&2
+ exit 1;;
+ *)
+ if test "x$tarball_version_file" = x; then
+ tarball_version_file="$1"
+ elif test "x$tag_sed_script" = x; then
+ tag_sed_script="$1"
+ else
+ echo "$0: extra non-option argument '$1'." >&2
+ exit 1
+ fi;;
+ esac
+ shift
+done
+
+if test "x$tarball_version_file" = x; then
+ echo "$usage"
+ exit 1
+fi
+
+tag_sed_script="${tag_sed_script:-s/x/x/}"
+
+nl='
+'
+
+# Avoid meddling by environment variable of the same name.
+v=
+v_from_git=
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+ v=`cat $tarball_version_file` || v=
+ case $v in
+ *$nl*) v= ;; # reject multi-line output
+ [0-9]*) ;;
+ *) v= ;;
+ esac
+ test "x$v" = x \
+ && echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2
+fi
+
+if test "x$v" != x
+then
+ : # use $v
+# Otherwise, if there is at least one git commit involving the working
+# directory, and "git describe" output looks sensible, use that to
+# derive a version string.
+elif test "`git log -1 --pretty=format:x . 2>&1`" = x \
+ && v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \
+ || git describe --abbrev=4 HEAD 2>/dev/null` \
+ && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \
+ && case $v in
+ $prefix[0-9]*) ;;
+ *) (exit 1) ;;
+ esac
+then
+ # Is this a new git that lists number of commits since the last
+ # tag or the previous older version that did not?
+ # Newer: v6.10-77-g0f8faeb
+ # Older: v6.10-g0f8faeb
+ case $v in
+ *-*-*) : git describe is okay three part flavor ;;
+ *-*)
+ : git describe is older two part flavor
+ # Recreate the number of commits and rewrite such that the
+ # result is the same as if we were using the newer version
+ # of git describe.
+ vtag=`echo "$v" | sed 's/-.*//'`
+ commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \
+ || { commit_list=failed;
+ echo "$0: WARNING: git rev-list failed" 1>&2; }
+ numcommits=`echo "$commit_list" | wc -l`
+ v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+ test "$commit_list" = failed && v=UNKNOWN
+ ;;
+ esac
+
+ # Change the first '-' to a '.', so version-comparing tools work properly.
+ # Remove the "g" in git describe's output string, to save a byte.
+ v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+ v_from_git=1
+elif test "x$fallback" = x || git --version >/dev/null 2>&1; then
+ v=UNKNOWN
+else
+ v=$fallback
+fi
+
+v=`echo "$v" |sed "s/^$prefix//"`
+
+# Test whether to append the "-dirty" suffix only if the version
+# string we're using came from git. I.e., skip the test if it's "UNKNOWN"
+# or if it came from .tarball-version.
+if test "x$v_from_git" != x; then
+ # Don't declare a version "dirty" merely because a time stamp has changed.
+ git update-index --refresh > /dev/null 2>&1
+
+ dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty=
+ case "$dirty" in
+ '') ;;
+ *) # Append the suffix only if there isn't one already.
+ case $v in
+ *-dirty) ;;
+ *) v="$v-dirty" ;;
+ esac ;;
+ esac
+fi
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+printf %s "$v"
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/build-aux/gitlog-to-changelog b/build-aux/gitlog-to-changelog
new file mode 100755
index 0000000..8f38318
--- /dev/null
+++ b/build-aux/gitlog-to-changelog
@@ -0,0 +1,492 @@
+eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}'
+ & eval 'exec perl -wS "$0" $argv:q'
+ if 0;
+# Convert git log output to ChangeLog format.
+
+my $VERSION = '2015-07-21 22:45'; # UTC
+# The definition above must lie within the first 8 lines in order
+# for the Emacs time-stamp write hook (at end) to update it.
+# If you change this file with Emacs, please let the write hook
+# do its job. Otherwise, update this string manually.
+
+# Copyright (C) 2008-2015 Free Software Foundation, Inc.
+
+# 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 <http://www.gnu.org/licenses/>.
+
+# Written by Jim Meyering
+
+use strict;
+use warnings;
+use Getopt::Long;
+use POSIX qw(strftime);
+
+(my $ME = $0) =~ s|.*/||;
+
+# use File::Coda; # http://meyering.net/code/Coda/
+END {
+ defined fileno STDOUT or return;
+ close STDOUT and return;
+ warn "$ME: failed to close standard output: $!\n";
+ $? ||= 1;
+}
+
+sub usage ($)
+{
+ my ($exit_code) = @_;
+ my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
+ if ($exit_code != 0)
+ {
+ print $STREAM "Try '$ME --help' for more information.\n";
+ }
+ else
+ {
+ print $STREAM <<EOF;
+Usage: $ME [OPTIONS] [ARGS]
+
+Convert git log output to ChangeLog format. If present, any ARGS
+are passed to "git log". To avoid ARGS being parsed as options to
+$ME, they may be preceded by '--'.
+
+OPTIONS:
+
+ --amend=FILE FILE maps from an SHA1 to perl code (i.e., s/old/new/) that
+ makes a change to SHA1's commit log text or metadata.
+ --append-dot append a dot to the first line of each commit message if
+ there is no other punctuation or blank at the end.
+ --no-cluster never cluster commit messages under the same date/author
+ header; the default is to cluster adjacent commit messages
+ if their headers are the same and neither commit message
+ contains multiple paragraphs.
+ --srcdir=DIR the root of the source tree, from which the .git/
+ directory can be derived.
+ --since=DATE convert only the logs since DATE;
+ the default is to convert all log entries.
+ --until=DATE convert only the logs older than DATE.
+ --ignore-matching=PAT ignore commit messages whose first lines match PAT.
+ --ignore-line=PAT ignore lines of commit messages that match PAT.
+ --format=FMT set format string for commit subject and body;
+ see 'man git-log' for the list of format metacharacters;
+ the default is '%s%n%b%n'
+ --strip-tab remove one additional leading TAB from commit message lines.
+ --strip-cherry-pick remove data inserted by "git cherry-pick";
+ this includes the "cherry picked from commit ..." line,
+ and the possible final "Conflicts:" paragraph.
+ --help display this help and exit
+ --version output version information and exit
+
+EXAMPLE:
+
+ $ME --since=2008-01-01 > ChangeLog
+ $ME -- -n 5 foo > last-5-commits-to-branch-foo
+
+SPECIAL SYNTAX:
+
+The following types of strings are interpreted specially when they appear
+at the beginning of a log message line. They are not copied to the output.
+
+ Copyright-paperwork-exempt: Yes
+ Append the "(tiny change)" notation to the usual "date name email"
+ ChangeLog header to mark a change that does not require a copyright
+ assignment.
+ Co-authored-by: Joe User <user\@example.com>
+ List the specified name and email address on a second
+ ChangeLog header, denoting a co-author.
+ Signed-off-by: Joe User <user\@example.com>
+ These lines are simply elided.
+
+In a FILE specified via --amend, comment lines (starting with "#") are ignored.
+FILE must consist of <SHA,CODE+> pairs where SHA is a 40-byte SHA1 (alone on
+a line) referring to a commit in the current project, and CODE refers to one
+or more consecutive lines of Perl code. Pairs must be separated by one or
+more blank line.
+
+Here is sample input for use with --amend=FILE, from coreutils:
+
+3a169f4c5d9159283548178668d2fae6fced3030
+# fix typo in title:
+s/all tile types/all file types/
+
+1379ed974f1fa39b12e2ffab18b3f7a607082202
+# Due to a bug in vc-dwim, I mis-attributed a patch by Paul to myself.
+# Change the author to be Paul. Note the escaped "@":
+s,Jim .*>,Paul Eggert <eggert\\\@cs.ucla.edu>,
+
+EOF
+ }
+ exit $exit_code;
+}
+
+# If the string $S is a well-behaved file name, simply return it.
+# If it contains white space, quotes, etc., quote it, and return the new string.
+sub shell_quote($)
+{
+ my ($s) = @_;
+ if ($s =~ m![^\w+/.,-]!)
+ {
+ # Convert each single quote to '\''
+ $s =~ s/\'/\'\\\'\'/g;
+ # Then single quote the string.
+ $s = "'$s'";
+ }
+ return $s;
+}
+
+sub quoted_cmd(@)
+{
+ return join (' ', map {shell_quote $_} @_);
+}
+
+# Parse file F.
+# Comment lines (starting with "#") are ignored.
+# F must consist of <SHA,CODE+> pairs where SHA is a 40-byte SHA1
+# (alone on a line) referring to a commit in the current project, and
+# CODE refers to one or more consecutive lines of Perl code.
+# Pairs must be separated by one or more blank line.
+sub parse_amend_file($)
+{
+ my ($f) = @_;
+
+ open F, '<', $f
+ or die "$ME: $f: failed to open for reading: $!\n";
+
+ my $fail;
+ my $h = {};
+ my $in_code = 0;
+ my $sha;
+ while (defined (my $line = <F>))
+ {
+ $line =~ /^\#/
+ and next;
+ chomp $line;
+ $line eq ''
+ and $in_code = 0, next;
+
+ if (!$in_code)
+ {
+ $line =~ /^([0-9a-fA-F]{40})$/
+ or (warn "$ME: $f:$.: invalid line; expected an SHA1\n"),
+ $fail = 1, next;
+ $sha = lc $1;
+ $in_code = 1;
+ exists $h->{$sha}
+ and (warn "$ME: $f:$.: duplicate SHA1\n"),
+ $fail = 1, next;
+ }
+ else
+ {
+ $h->{$sha} ||= '';
+ $h->{$sha} .= "$line\n";
+ }
+ }
+ close F;
+
+ $fail
+ and exit 1;
+
+ return $h;
+}
+
+# git_dir_option $SRCDIR
+#
+# From $SRCDIR, the --git-dir option to pass to git (none if $SRCDIR
+# is undef). Return as a list (0 or 1 element).
+sub git_dir_option($)
+{
+ my ($srcdir) = @_;
+ my @res = ();
+ if (defined $srcdir)
+ {
+ my $qdir = shell_quote $srcdir;
+ my $cmd = "cd $qdir && git rev-parse --show-toplevel";
+ my $qcmd = shell_quote $cmd;
+ my $git_dir = qx($cmd);
+ defined $git_dir
+ or die "$ME: cannot run $qcmd: $!\n";
+ $? == 0
+ or die "$ME: $qcmd had unexpected exit code or signal ($?)\n";
+ chomp $git_dir;
+ push @res, "--git-dir=$git_dir/.git";
+ }
+ @res;
+}
+
+{
+ my $since_date;
+ my $until_date;
+ my $format_string = '%s%n%b%n';
+ my $amend_file;
+ my $append_dot = 0;
+ my $cluster = 1;
+ my $ignore_matching;
+ my $ignore_line;
+ my $strip_tab = 0;
+ my $strip_cherry_pick = 0;
+ my $srcdir;
+ GetOptions
+ (
+ help => sub { usage 0 },
+ version => sub { print "$ME version $VERSION\n"; exit },
+ 'since=s' => \$since_date,
+ 'until=s' => \$until_date,
+ 'format=s' => \$format_string,
+ 'amend=s' => \$amend_file,
+ 'append-dot' => \$append_dot,
+ 'cluster!' => \$cluster,
+ 'ignore-matching=s' => \$ignore_matching,
+ 'ignore-line=s' => \$ignore_line,
+ 'strip-tab' => \$strip_tab,
+ 'strip-cherry-pick' => \$strip_cherry_pick,
+ 'srcdir=s' => \$srcdir,
+ ) or usage 1;
+
+ defined $since_date
+ and unshift @ARGV, "--since=$since_date";
+ defined $until_date
+ and unshift @ARGV, "--until=$until_date";
+
+ # This is a hash that maps an SHA1 to perl code (i.e., s/old/new/)
+ # that makes a correction in the log or attribution of that commit.
+ my $amend_code = defined $amend_file ? parse_amend_file $amend_file : {};
+
+ my @cmd = ('git',
+ git_dir_option $srcdir,
+ qw(log --log-size),
+ '--pretty=format:%H:%ct %an <%ae>%n%n'.$format_string, @ARGV);
+ open PIPE, '-|', @cmd
+ or die ("$ME: failed to run '". quoted_cmd (@cmd) ."': $!\n"
+ . "(Is your Git too old? Version 1.5.1 or later is required.)\n");
+
+ my $prev_multi_paragraph;
+ my $prev_date_line = '';
+ my @prev_coauthors = ();
+ my @skipshas = ();
+ while (1)
+ {
+ defined (my $in = <PIPE>)
+ or last;
+ $in =~ /^log size (\d+)$/
+ or die "$ME:$.: Invalid line (expected log size):\n$in";
+ my $log_nbytes = $1;
+
+ my $log;
+ my $n_read = read PIPE, $log, $log_nbytes;
+ $n_read == $log_nbytes
+ or die "$ME:$.: unexpected EOF\n";
+
+ # Extract leading hash.
+ my ($sha, $rest) = split ':', $log, 2;
+ defined $sha
+ or die "$ME:$.: malformed log entry\n";
+ $sha =~ /^[0-9a-fA-F]{40}$/
+ or die "$ME:$.: invalid SHA1: $sha\n";
+
+ my $skipflag = 0;
+ if (@skipshas)
+ {
+ foreach(@skipshas)
+ {
+ if ($sha =~ /^$_/)
+ {
+ $skipflag = 1;
+ ## Perhaps only warn if a pattern matches more than once?
+ warn "$ME: warning: skipping $sha due to $_\n";
+ last;
+ }
+ }
+ }
+
+ # If this commit's log requires any transformation, do it now.
+ my $code = $amend_code->{$sha};
+ if (defined $code)
+ {
+ eval 'use Safe';
+ my $s = new Safe;
+ # Put the unpreprocessed entry into "$_".
+ $_ = $rest;
+
+ # Let $code operate on it, safely.
+ my $r = $s->reval("$code")
+ or die "$ME:$.:$sha: failed to eval \"$code\":\n$@\n";
+
+ # Note that we've used this entry.
+ delete $amend_code->{$sha};
+
+ # Update $rest upon success.
+ $rest = $_;
+ }
+
+ # Remove lines inserted by "git cherry-pick".
+ if ($strip_cherry_pick)
+ {
+ $rest =~ s/^\s*Conflicts:\n.*//sm;
+ $rest =~ s/^\s*\(cherry picked from commit [\da-f]+\)\n//m;
+ }
+
+ my @line = split /\s*\n/, $rest;
+ my $author_line = shift @line;
+ defined $author_line
+ or die "$ME:$.: unexpected EOF\n";
+ $author_line =~ /^(\d+) (.*>)$/
+ or die "$ME:$.: Invalid line "
+ . "(expected date/author/email):\n$author_line\n";
+
+ # Format 'Copyright-paperwork-exempt: Yes' as a standard ChangeLog
+ # `(tiny change)' annotation.
+ my $tiny = (grep (/^(?:Copyright-paperwork-exempt|Tiny-change):\s+[Yy]es$/, @line)
+ ? ' (tiny change)' : '');
+
+ my $date_line = sprintf "%s %s$tiny\n",
+ strftime ("%Y-%m-%d", localtime ($1)), $2;
+
+ my @coauthors = grep /^Co-authored-by:.*$/, @line;
+ # Omit meta-data lines we've already interpreted.
+ @line = grep !/^(?:Signed-off-by:[ ].*>$
+ |Co-authored-by:[ ]
+ |Copyright-paperwork-exempt:[ ]
+ |Tiny-change:[ ]
+ )/x, @line;
+
+ # Remove leading and trailing blank lines.
+ if (@line)
+ {
+ while ($line[0] =~ /^\s*$/) { shift @line; }
+ while ($line[$#line] =~ /^\s*$/) { pop @line; }
+ }
+
+ # Handle Emacs gitmerge.el "skipped" commits.
+ # Yes, this should be controlled by an option. So sue me.
+ if ( grep /^(; )?Merge from /, @line )
+ {
+ my $found = 0;
+ foreach (@line)
+ {
+ if (grep /^The following commit.*skipped:$/, $_)
+ {
+ $found = 1;
+ ## Reset at each merge to reduce chance of false matches.
+ @skipshas = ();
+ next;
+ }
+ if ($found && $_ =~ /^([0-9a-fA-F]{7,}) [^ ]/)
+ {
+ push ( @skipshas, $1 );
+ }
+ }
+ }
+
+ # Ignore commits that match the --ignore-matching pattern, if specified.
+ if (! ($skipflag || (defined $ignore_matching
+ && @line && $line[0] =~ /$ignore_matching/)))
+ {
+ if (defined $ignore_line && @line)
+ {
+ @line = grep ! /$ignore_line/, @line;
+ while ($line[$#line] =~ /^\s*$/) { pop @line; }
+ }
+
+ # Record whether there are two or more paragraphs.
+ my $multi_paragraph = grep /^\s*$/, @line;
+
+ # Format 'Co-authored-by: A U Thor <email@example.com>' lines in
+ # standard multi-author ChangeLog format.
+ for (@coauthors)
+ {
+ s/^Co-authored-by:\s*/\t /;
+ s/\s*</ </;
+
+ /<.*?@.*\..*>/
+ or warn "$ME: warning: missing email address for "
+ . substr ($_, 5) . "\n";
+ }
+
+ # If clustering of commit messages has been disabled, if this header
+ # would be different from the previous date/name/etc. header,
+ # or if this or the previous entry consists of two or more paragraphs,
+ # then print the header.
+ if ( ! $cluster
+ || $date_line ne $prev_date_line
+ || "@coauthors" ne "@prev_coauthors"
+ || $multi_paragraph
+ || $prev_multi_paragraph)
+ {
+ $prev_date_line eq ''
+ or print "\n";
+ print $date_line;
+ @coauthors
+ and print join ("\n", @coauthors), "\n";
+ }
+ $prev_date_line = $date_line;
+ @prev_coauthors = @coauthors;
+ $prev_multi_paragraph = $multi_paragraph;
+
+ # If there were any lines
+ if (@line == 0)
+ {
+ warn "$ME: warning: empty commit message:\n $date_line\n";
+ }
+ else
+ {
+ if ($append_dot)
+ {
+ # If the first line of the message has enough room, then
+ if (length $line[0] < 72)
+ {
+ # append a dot if there is no other punctuation or blank
+ # at the end.
+ $line[0] =~ /[[:punct:]\s]$/
+ or $line[0] .= '.';
+ }
+ }
+
+ # Remove one additional leading TAB from each line.
+ $strip_tab
+ and map { s/^\t// } @line;
+
+ # Prefix each non-empty line with a TAB.
+ @line = map { length $_ ? "\t$_" : '' } @line;
+
+ print "\n", join ("\n", @line), "\n";
+ }
+ }
+
+ defined ($in = <PIPE>)
+ or last;
+ $in ne "\n"
+ and die "$ME:$.: unexpected line:\n$in";
+ }
+
+ close PIPE
+ or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n";
+ # FIXME-someday: include $PROCESS_STATUS in the diagnostic
+
+ # Complain about any unused entry in the --amend=F specified file.
+ my $fail = 0;
+ foreach my $sha (keys %$amend_code)
+ {
+ warn "$ME:$amend_file: unused entry: $sha\n";
+ $fail = 1;
+ }
+
+ exit $fail;
+}
+
+# Local Variables:
+# mode: perl
+# indent-tabs-mode: nil
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "my $VERSION = '"
+# time-stamp-format: "%:y-%02m-%02d %02H:%02M"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "'; # UTC"
+# End:
diff --git a/build-aux/guix.scm b/build-aux/guix.scm
new file mode 100644
index 0000000..d90e0be
--- /dev/null
+++ b/build-aux/guix.scm
@@ -0,0 +1,66 @@
+;;;; guix.scm -- Guix package definition
+;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+(use-modules (ice-9 popen)
+ (ice-9 rdelim)
+ (gnu)
+ (guix)
+ (srfi srfi-1))
+
+(define (keep-mcron-file? file stat)
+ ;; Return #t if FILE in Mcron repository must be kept, #f otherwise. FILE
+ ;; is an absolute file name and STAT is the result of 'lstat' applied to
+ ;; FILE.
+ (not (or (any (λ (str) (string-contains file str))
+ '(".git" "autom4te" "Makefile.in" ".go" ".log"
+ "stamp-vti" ".dirstamp"))
+ (any (λ (str) (string-suffix? str file))
+ '("trs""configure" "Makefile" "config.status" "pre-inst-env"
+ "aclocal.m4" "bin/cron" "bin/mcron" "bin/crontab" "config.cache"
+ "guix.scm")))))
+
+(define %srcdir
+ (or (current-source-directory) "."))
+
+(define (git-version-gen)
+ ;; Return a string containing Cuirass version number.
+ (let* ((cmd "git-version-gen .version")
+ (port (open-input-pipe (string-append %srcdir "/" cmd)))
+ (str (read-line port)))
+ (close-pipe port)
+ str))
+
+(package
+ (inherit (specification->package "mcron2"))
+ (version (git-version-gen))
+ (source (local-file (dirname %srcdir) #:recursive? #t
+ #:select? keep-mcron-file?))
+ (arguments
+ '(#:phases
+ (modify-phases %standard-phases
+ (add-before 'configure 'bootstrap
+ (λ _ (zero? (system* "autoreconf" "-vfi")))))))
+ (inputs
+ `(("guile" ,(specification->package "guile@2.0"))))
+ (native-inputs
+ `(("autoconf" ,(specification->package "autoconf"))
+ ("automake" ,(specification->package "automake"))
+ ("help2man" ,(specification->package "help2man"))
+ ("pkg-config" ,(specification->package "pkg-config"))
+ ("texinfo" ,(specification->package "texinfo"))
+ ("which" ,(specification->package "which")))))
diff --git a/build-aux/pre-inst-env.in b/build-aux/pre-inst-env.in
new file mode 100644
index 0000000..75a8d70
--- /dev/null
+++ b/build-aux/pre-inst-env.in
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+# Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+#
+# This file is part of GNU Mcron.
+#
+# GNU Mcron 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.
+#
+# GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+abs_top_srcdir="`cd "@abs_top_srcdir@" > /dev/null; pwd`"
+abs_top_builddir="`cd "@abs_top_builddir@" > /dev/null; pwd`"
+
+GUILE_LOAD_COMPILED_PATH="$abs_top_builddir/src${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_LOAD_COMPILED_PATH"
+GUILE_LOAD_PATH="$abs_top_builddir/src:$abs_top_srcdir/src${GUILE_LOAD_PATH:+:}:$GUILE_LOAD_PATH"
+export GUILE_LOAD_COMPILED_PATH GUILE_LOAD_PATH
+
+PATH="$abs_top_builddir/bin:$PATH"
+export PATH
+
+# Define $MCRON_UNINSTALLED to prevent 'mcron' from prepending @moduledir@ to
+# the Guile load paths.
+MCRON_UNINSTALLED=1
+export MCRON_UNINSTALLED
+
+exec "$@"
diff --git a/build-aux/test-driver.scm b/build-aux/test-driver.scm
new file mode 100644
index 0000000..01edf93
--- /dev/null
+++ b/build-aux/test-driver.scm
@@ -0,0 +1,193 @@
+;;;; test-driver.scm - Guile test driver for Automake testsuite harness
+
+(define script-version "2016-05-09.22") ;UTC
+
+;;; Copyright (C) 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; 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 <http://www.gnu.org/licenses/>.
+;;;
+;;; As a special exception to the GNU General Public License, if you
+;;; distribute this file as part of a program that contains a configuration
+;;; script generated by Autoconf, you may include it under the same
+;;; distribution terms that you use for the rest of that program.
+
+;;;; Commentary:
+;;;
+;;; This script provides a Guile test driver using the SRFI-64 Scheme API for
+;;; test suites. SRFI-64 is distributed with Guile since version 2.0.9.
+;;;
+;;;; Code:
+
+(use-modules (ice-9 getopt-long)
+ (ice-9 pretty-print)
+ (srfi srfi-26)
+ (srfi srfi-64))
+
+(define (show-help)
+ (display "Usage:
+ test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
+ [--expect-failure={yes|no}] [--color-tests={yes|no}]
+ [--enable-hard-errors={yes|no}] [--brief={yes|no}}] [--]
+ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
+The '--test-name', '--log-file' and '--trs-file' options are mandatory.\n"))
+
+(define %options
+ '((test-name (value #t))
+ (log-file (value #t))
+ (trs-file (value #t))
+ (color-tests (value #t))
+ (expect-failure (value #t)) ;XXX: not implemented yet
+ (enable-hard-errors (value #t)) ;not implemented in SRFI-64
+ (brief (value #t))
+ (help (single-char #\h) (value #f))
+ (version (single-char #\V) (value #f))))
+
+(define (option->boolean options key)
+ "Return #t if the value associated with KEY in OPTIONS is \"yes\"."
+ (and=> (option-ref options key #f) (cut string=? <> "yes")))
+
+(define* (test-display field value #:optional (port (current-output-port))
+ #:key pretty?)
+ "Display \"FIELD: VALUE\\n\" on PORT."
+ (if pretty?
+ (begin
+ (format port "~A:~%" field)
+ (pretty-print value port #:per-line-prefix "+ "))
+ (format port "~A: ~A~%" field value)))
+
+(define* (result->string symbol #:key colorize?)
+ "Return SYMBOL as an upper case string. Use colors when COLORIZE is #t."
+ (let ((result (string-upcase (symbol->string symbol))))
+ (if colorize?
+ (string-append (case symbol
+ ((pass) "") ;green
+ ((xfail) "") ;light green
+ ((skip) "") ;blue
+ ((fail xpass) "") ;red
+ ((error) "")) ;magenta
+ result
+ "") ;no color
+ result)))
+
+(define* (test-runner-gnu test-name #:key color? brief? out-port trs-port)
+ "Return an custom SRFI-64 test runner. TEST-NAME is a string specifying the
+file name of the current the test. COLOR? specifies whether to use colors,
+and BRIEF?, well, you know. OUT-PORT and TRS-PORT must be output ports. The
+current output port is supposed to be redirected to a '.log' file."
+
+ (define (test-on-test-begin-gnu runner)
+ ;; Procedure called at the start of an individual test case, before the
+ ;; test expression (and expected value) are evaluated.
+ (let ((result (cute assq-ref (test-result-alist runner) <>)))
+ (test-display "test-name" (result 'test-name))
+ (test-display "location"
+ (string-append (result 'source-file) ":"
+ (number->string (result 'source-line))))
+ (test-display "source" (result 'source-form) #:pretty? #t)))
+
+ (define (test-on-test-end-gnu runner)
+ ;; Procedure called at the end of an individual test case, when the result
+ ;; of the test is available.
+ (let* ((results (test-result-alist runner))
+ (result? (cut assq <> results))
+ (result (cut assq-ref results <>)))
+ (unless brief?
+ ;; Display the result of each test case on the console.
+ (test-display
+ (result->string (test-result-kind runner) #:colorize? color?)
+ (string-append test-name " - " (test-runner-test-name runner))
+ out-port))
+ (when (result? 'expected-value)
+ (test-display "expected-value" (result 'expected-value)))
+ (when (result? 'expected-error)
+ (test-display "expected-error" (result 'expected-error) #:pretty? #t))
+ (when (result? 'actual-value)
+ (test-display "actual-value" (result 'actual-value)))
+ (when (result? 'actual-error)
+ (test-display "actual-error" (result 'actual-error) #:pretty? #t))
+ (test-display "result" (result->string (result 'result-kind)))
+ (newline)
+ (test-display ":test-result"
+ (string-append (result->string (test-result-kind runner))
+ " " (test-runner-test-name runner))
+ trs-port)))
+
+ (define (test-on-group-end-gnu runner)
+ ;; Procedure called by a 'test-end', including at the end of a test-group.
+ (let ((fail (or (positive? (test-runner-fail-count runner))
+ (positive? (test-runner-xpass-count runner))))
+ (skip (or (positive? (test-runner-skip-count runner))
+ (positive? (test-runner-xfail-count runner)))))
+ ;; XXX: The global results need some refinements for XPASS.
+ (test-display ":global-test-result"
+ (if fail "FAIL" (if skip "SKIP" "PASS"))
+ trs-port)
+ (test-display ":recheck"
+ (if fail "yes" "no")
+ trs-port)
+ (test-display ":copy-in-global-log"
+ (if (or fail skip) "yes" "no")
+ trs-port)
+ (when brief?
+ ;; Display the global test group result on the console.
+ (test-display (result->string (if fail 'fail (if skip 'skip 'pass))
+ #:colorize? color?)
+ test-name
+ out-port))
+ #f))
+
+ (let ((runner (test-runner-null)))
+ (test-runner-on-test-begin! runner test-on-test-begin-gnu)
+ (test-runner-on-test-end! runner test-on-test-end-gnu)
+ (test-runner-on-group-end! runner test-on-group-end-gnu)
+ (test-runner-on-bad-end-name! runner test-on-bad-end-name-simple)
+ runner))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(let* ((opts (getopt-long (command-line) %options))
+ (option (cut option-ref opts <> <>)))
+ (cond
+ ((option 'help #f) (show-help))
+ ((option 'version #f) (format #t "test-driver.scm ~A~%" script-version))
+ (else
+ (let ((log (open-file (option 'log-file "") "w0"))
+ (trs (open-file (option 'trs-file "") "wl"))
+ (out (duplicate-port (current-output-port) "wl")))
+ (redirect-port log (current-output-port))
+ (redirect-port log (current-warning-port))
+ (redirect-port log (current-error-port))
+ (test-with-runner
+ (test-runner-gnu (option 'test-name #f)
+ #:color? (option->boolean opts 'color-tests)
+ #:brief? (option->boolean opts 'brief)
+ #:out-port out #:trs-port trs)
+ (load (string-append (getcwd) "/" (car (option '() '(""))))))
+ (close-port log)
+ (close-port trs)
+ (close-port out))))
+ (exit 0))
+
+;;; Local Variables:
+;;; eval: (add-hook 'write-file-functions 'time-stamp)
+;;; time-stamp-start: "(define script-version \""
+;;; time-stamp-format: "%:y-%02m-%02d.%02H"
+;;; time-stamp-time-zone: "UTC"
+;;; time-stamp-end: "\") ;UTC"
+;;; End:
+
+;;;; test-driver.scm ends here.
diff --git a/configure.ac b/configure.ac
index 764ea03..5940555 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,30 +1,37 @@
-# -*- Autoconf -*-
-# Process this file with autoconf to produce a configure script.
-
-
-# Copyright (C) 2003, 2005, 2012, 2014 Dale Mellor
-#
-# This file is part of GNU mcron.
-#
-# GNU mcron 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.
-#
-# GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
+## Process this file with autoconf to produce a configure script.
+# Copyright © 2003, 2005, 2012, 2014 Dale Mellor
+# <dale_mellor@users.sourceforge.net>
+# Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+#
+# This file is part of GNU Mcron.
+#
+# GNU Mcron 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.
+#
+# GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ(2.61)
-AC_INIT([mcron], [1.0.8], [dale_mellor@users.sourceforge.net])
-AM_INIT_AUTOMAKE
-
-
+AC_INIT([GNU Mcron],
+ m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+ [bug-mcron@gnu.org])
+AC_CONFIG_SRCDIR([src/wrapper.c])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_REQUIRE_AUX_FILE([test-driver.scm])
+AC_REQUIRE_AUX_FILE([git-version-gen])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Wno-override])
+AM_SILENT_RULES([yes]) # enables silent rules by default
+
+moduledir="${datarootdir}/guile/site/2.0"
+AC_SUBST([moduledir])
+AC_SUBST([mcronmoduledir], ["${moduledir}/mcron"])
AC_MSG_CHECKING([whether debugging is requested])
AC_ARG_ENABLE(debug,
AC_HELP_STRING([--enable-debug],
@@ -43,12 +50,18 @@ AC_SUBST(CONFIG_DEBUG)
# We have no interest (hence a no-op), but Debian wants this.
AC_ARG_ENABLE(maintainer-mode)
-
+AC_CANONICAL_HOST
AC_PROG_AWK
AC_PROG_EGREP
AM_PROG_CC_C_O
-PKG_CHECK_MODULES(GUILE, guile-2.0)
+PKG_CHECK_MODULES([GUILE], [guile-2.0 >= 2.0.7])
+AC_PATH_PROG([GUILE], [guile])
+
+# search guild
+AC_PATH_PROG([GUILD], [guild])
+AS_IF([test -z "$ac_cv_path_GUILD"],
+ [AC_MSG_ERROR(['guild' program cannot be found.])])
# Checks for programs.
@@ -62,10 +75,6 @@ AC_CHECK_PROGS(HEAD, head)
if test "x$ac_cv_prog_HEAD" = "x"; then
AC_MSG_ERROR(head not found)
fi
-AC_CHECK_PROGS(ED, ed)
-if test "x$ac_cv_prog_ED" = "x"; then
- AC_MSG_ERROR(ed not found)
-fi
AC_CHECK_PROGS(WHICH, which)
if test "x$ac_cv_prog_WHICH" = "x"; then
AC_MSG_ERROR(which not found)
@@ -174,6 +183,9 @@ AC_SUBST(CONFIG_TMP_DIR)
real_program_prefix=`echo $program_prefix | sed s/NONE//`
AC_SUBST(real_program_prefix)
-
-AC_CONFIG_FILES(mcron.texinfo makefile scm/mcron/makefile scm/mcron/config.scm)
+AC_CONFIG_FILES([pre-inst-env:build-aux/pre-inst-env.in],
+ [chmod +x pre-inst-env])
+AC_CONFIG_FILES([doc/config.texi
+ Makefile
+ src/mcron/config.scm])
AC_OUTPUT
diff --git a/doc/config.texi.in b/doc/config.texi.in
new file mode 100644
index 0000000..50d9a18
--- /dev/null
+++ b/doc/config.texi.in
@@ -0,0 +1,5 @@
+@set CONFIG_SOCKET_FILE @CONFIG_SOCKET_FILE@
+@set CONFIG_SPOOL_DIR @CONFIG_SPOOL_DIR@
+@set CONFIG_PID_FILE @CONFIG_PID_FILE@
+@set CONFIG_ALLOW_FILE @CONFIG_ALLOW_FILE@
+@set CONFIG_DENY_FILE @CONFIG_DENY_FILE@
diff --git a/doc/fdl.texi b/doc/fdl.texi
new file mode 100644
index 0000000..9c3bbe5
--- /dev/null
+++ b/doc/fdl.texi
@@ -0,0 +1,505 @@
+@c The GNU Free Documentation License.
+@center Version 1.3, 3 November 2008
+
+@c This file is intended to be included within another document,
+@c hence no sectioning command or @node.
+
+@display
+Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
+@uref{http://fsf.org/}
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+@end display
+
+@enumerate 0
+@item
+PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document @dfn{free} in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of ``copyleft'', which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+@item
+APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The ``Document'', below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as ``you''. You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A ``Modified Version'' of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A ``Secondary Section'' is a named appendix or a front-matter section
+of the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall
+subject (or to related matters) and contains nothing that could fall
+directly within that overall subject. (Thus, if the Document is in
+part a textbook of mathematics, a Secondary Section may not explain
+any mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The ``Invariant Sections'' are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The ``Cover Texts'' are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A ``Transparent'' copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not ``Transparent'' is called ``Opaque''.
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, La@TeX{} input
+format, SGML or XML using a publicly available
+DTD, and standard-conforming simple HTML,
+PostScript or PDF designed for human modification. Examples
+of transparent image formats include PNG, XCF and
+JPG@. Opaque formats include proprietary formats that can be
+read and edited only by proprietary word processors, SGML or
+XML for which the DTD and/or processing tools are
+not generally available, and the machine-generated HTML,
+PostScript or PDF produced by some word processors for
+output purposes only.
+
+The ``Title Page'' means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, ``Title Page'' means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+The ``publisher'' means any person or entity that distributes copies
+of the Document to the public.
+
+A section ``Entitled XYZ'' means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as ``Acknowledgements'',
+``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title''
+of such a section when you modify the Document means that it remains a
+section ``Entitled XYZ'' according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+@item
+VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+@item
+COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+@item
+MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+@enumerate A
+@item
+Use in the Title Page (and on the covers, if any) a title distinct
+from that of the Document, and from those of previous versions
+(which should, if there were any, be listed in the History section
+of the Document). You may use the same title as a previous version
+if the original publisher of that version gives permission.
+
+@item
+List on the Title Page, as authors, one or more persons or entities
+responsible for authorship of the modifications in the Modified
+Version, together with at least five of the principal authors of the
+Document (all of its principal authors, if it has fewer than five),
+unless they release you from this requirement.
+
+@item
+State on the Title page the name of the publisher of the
+Modified Version, as the publisher.
+
+@item
+Preserve all the copyright notices of the Document.
+
+@item
+Add an appropriate copyright notice for your modifications
+adjacent to the other copyright notices.
+
+@item
+Include, immediately after the copyright notices, a license notice
+giving the public permission to use the Modified Version under the
+terms of this License, in the form shown in the Addendum below.
+
+@item
+Preserve in that license notice the full lists of Invariant Sections
+and required Cover Texts given in the Document's license notice.
+
+@item
+Include an unaltered copy of this License.
+
+@item
+Preserve the section Entitled ``History'', Preserve its Title, and add
+to it an item stating at least the title, year, new authors, and
+publisher of the Modified Version as given on the Title Page. If
+there is no section Entitled ``History'' in the Document, create one
+stating the title, year, authors, and publisher of the Document as
+given on its Title Page, then add an item describing the Modified
+Version as stated in the previous sentence.
+
+@item
+Preserve the network location, if any, given in the Document for
+public access to a Transparent copy of the Document, and likewise
+the network locations given in the Document for previous versions
+it was based on. These may be placed in the ``History'' section.
+You may omit a network location for a work that was published at
+least four years before the Document itself, or if the original
+publisher of the version it refers to gives permission.
+
+@item
+For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve
+the Title of the section, and preserve in the section all the
+substance and tone of each of the contributor acknowledgements and/or
+dedications given therein.
+
+@item
+Preserve all the Invariant Sections of the Document,
+unaltered in their text and in their titles. Section numbers
+or the equivalent are not considered part of the section titles.
+
+@item
+Delete any section Entitled ``Endorsements''. Such a section
+may not be included in the Modified Version.
+
+@item
+Do not retitle any existing section to be Entitled ``Endorsements'' or
+to conflict in title with any Invariant Section.
+
+@item
+Preserve any Warranty Disclaimers.
+@end enumerate
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled ``Endorsements'', provided it contains
+nothing but endorsements of your Modified Version by various
+parties---for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+@item
+COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled ``History''
+in the various original documents, forming one section Entitled
+``History''; likewise combine any sections Entitled ``Acknowledgements'',
+and any sections Entitled ``Dedications''. You must delete all
+sections Entitled ``Endorsements.''
+
+@item
+COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+@item
+AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an ``aggregate'' if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+@item
+TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled ``Acknowledgements'',
+``Dedications'', or ``History'', the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+@item
+TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense, or distribute it is void, and
+will automatically terminate your rights under this License.
+
+However, if you cease all violation of this License, then your license
+from a particular copyright holder is reinstated (a) provisionally,
+unless and until the copyright holder explicitly and finally
+terminates your license, and (b) permanently, if the copyright holder
+fails to notify you of the violation by some reasonable means prior to
+60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, receipt of a copy of some or all of the same material does
+not give you any rights to use it.
+
+@item
+FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+@uref{http://www.gnu.org/copyleft/}.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License ``or any later version'' applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation. If the Document
+specifies that a proxy can decide which future versions of this
+License can be used, that proxy's public statement of acceptance of a
+version permanently authorizes you to choose that version for the
+Document.
+
+@item
+RELICENSING
+
+``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any
+World Wide Web server that publishes copyrightable works and also
+provides prominent facilities for anybody to edit those works. A
+public wiki that anybody can edit is an example of such a server. A
+``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the
+site means any set of copyrightable works thus published on the MMC
+site.
+
+``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0
+license published by Creative Commons Corporation, a not-for-profit
+corporation with a principal place of business in San Francisco,
+California, as well as future copyleft versions of that license
+published by that same organization.
+
+``Incorporate'' means to publish or republish a Document, in whole or
+in part, as part of another Document.
+
+An MMC is ``eligible for relicensing'' if it is licensed under this
+License, and if all works that were first published under this License
+somewhere other than this MMC, and subsequently incorporated in whole
+or in part into the MMC, (1) had no cover texts or invariant sections,
+and (2) were thus incorporated prior to November 1, 2008.
+
+The operator of an MMC Site may republish an MMC contained in the site
+under CC-BY-SA on the same site at any time before August 1, 2009,
+provided the MMC is eligible for relicensing.
+
+@end enumerate
+
+@page
+@heading ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+@smallexample
+@group
+ Copyright (C) @var{year} @var{your name}.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.3
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ Texts. A copy of the license is included in the section entitled ``GNU
+ Free Documentation License''.
+@end group
+@end smallexample
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the ``with@dots{}Texts.''@: line with this:
+
+@smallexample
+@group
+ with the Invariant Sections being @var{list their titles}, with
+ the Front-Cover Texts being @var{list}, and with the Back-Cover Texts
+ being @var{list}.
+@end group
+@end smallexample
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+
+@c Local Variables:
+@c ispell-local-pdict: "ispell-dict"
+@c End:
diff --git a/mcron.texinfo.in b/doc/mcron.texi
index bbf8e5b..a59505d 100644
--- a/mcron.texinfo.in
+++ b/doc/mcron.texi
@@ -1,12 +1,14 @@
\input texinfo
@c %**start of header
@setfilename mcron.info
-@settitle mcron @VERSION@
+@include config.texi
+@include version.texi
+@settitle mcron @value{VERSION}
@c %**end of header
@syncodeindex fn cp
-@copying This manual is for GNU mcron (version @VERSION@), which is a
+@copying This manual is for GNU mcron (version @value{VERSION}), which is a
program for running jobs at scheduled times.
Copyright @copyright{} 2003, 2005, 2006, 2012, 2014 Dale Mellor
@@ -61,6 +63,7 @@ running jobs at scheduled times.
* Syntax:: All the possibilities for configuring cron jobs.
* Invoking:: What happens when you run the mcron command.
* Guile modules:: Incorporating mcron into another Guile program.
+* GNU Free Documentation License:: The license of this manual.
* Index:: The complete index.
@detailmenu
@@ -68,40 +71,40 @@ running jobs at scheduled times.
Simple examples
-* Guile Simple Examples::
-* Vixie Simple Examples::
+* Guile Simple Examples::
+* Vixie Simple Examples::
Full available syntax
-* Guile Syntax::
-* Extended Guile examples::
-* Vixie Syntax::
+* Guile Syntax::
+* Extended Guile examples::
+* Vixie Syntax::
Extended Guile examples
-* AT commands::
-* Every second Sunday::
-* Two hours every day::
-* Missing the first appointment::
-* Penultimate day of every month::
+* AT commands::
+* Every second Sunday::
+* Two hours every day::
+* Missing the first appointment::
+* Penultimate day of every month::
Vixie
-* Paul Vixie's copyright::
-* Crontab file::
-* Incompatibilities with old Unices::
+* Paul Vixie's copyright::
+* Crontab file::
+* Incompatibilities with old Unices::
Detailed invoking
-* Invoking mcron::
-* Invoking cron or crond::
+* Invoking mcron::
+* Invoking cron or crond::
* Invoking crontab::
* Behaviour on laptops::
-* Exit codes::
+* Exit codes::
Guile modules
-* The core module:: The job list and execution loop.
+* The base module:: The job list and execution loop.
* The redirect module:: Sending output of jobs to a mail box.
* The vixie-time module:: Parsing vixie-style time specifications.
* The job-specifier module:: All commands for scheme configuration files.
@@ -182,8 +185,8 @@ been to allow such simple specifications to be made easily. The
examples show how to create the command descriptions, and subsequently
how to run mcron to make them happen.
@menu
-* Guile Simple Examples::
-* Vixie Simple Examples::
+* Guile Simple Examples::
+* Vixie Simple Examples::
@end menu
@node Guile Simple Examples, Vixie Simple Examples, Simple examples, Simple examples
@@ -258,9 +261,9 @@ on your system, as root.
@node Syntax, Invoking, Simple examples, Top
@chapter Full available syntax
@menu
-* Guile Syntax::
-* Extended Guile examples::
-* Vixie Syntax::
+* Guile Syntax::
+* Extended Guile examples::
+* Vixie Syntax::
@end menu
@node Guile Syntax, Extended Guile examples, Syntax, Syntax
@section Guile Syntax
@@ -268,11 +271,13 @@ on your system, as root.
@cindex guile syntax
@cindex syntax, guile
@findex job
-In Guile-formatted configuration files each command that needs
-executing is introduced with the @code{job} function. This function
-always takes two arguments, the first a time specification, and the
-second a command specification. An optional third argument may contain
-a string to display when this job is listed in a schedule.
+In Guile-formatted configuration files each command that needs executing is
+introduced with the @code{job} function. This function always takes two
+arguments, the first a time specification, and the second a command
+specification. An optional third argument may contain a string to display
+when this job is listed in a schedule. Additionally a @var{user} keyword
+argument can be supplied to use a different user than the one defined in
+@code{configuration-user} global variable.
@cindex time specification, procedure
@cindex procedure time specification
@@ -324,7 +329,7 @@ taken to be program code made up of the functions @code{(next-second
. args)}, @code{(next-minute...)}, etc, where the optional arguments
can be supplied with the @code{(range)} function above (these
functions are analogous to the ones above except that they implicitly
-assume the current time; it is supplied by the mcron core when the
+assume the current time; it is supplied by the mcron base when the
list is eval'd).
@cindex time specification
@@ -339,13 +344,12 @@ on Vixie syntax for this.
@cindex job execution
@cindex command execution
@cindex execution
-The second argument to the @code{(job)} function can be either a
-string, a list, or a function. In all cases the command is executed in
-the user's home directory, under the user's own UID. If a string is
-passed, it is assumed to be shell script and is executed with the
-user's default shell. If a list is passed it is assumed to be scheme
-code and is eval'd as such. A supplied function should take exactly
-zero arguments, and will be called at the pertinent times.
+The second argument to the @code{(job)} function can be either a string, a
+list, or a function. The command is executed in the home directory and with
+the UID of @var{user}. If a string is passed, it is assumed to be shell
+script and is executed with the user's default shell. If a list is passed it
+is assumed to be scheme code and is eval'd as such. A supplied function
+should take exactly zero arguments, and will be called at the pertinent times.
@subsection Sending output as e-mail
@cindex email output
@@ -392,11 +396,11 @@ they seem. The following examples illustrate some pitfalls, and
demonstrate how to code around them.
@menu
-* AT commands::
-* Every second Sunday::
-* Two hours every day::
-* Missing the first appointment::
-* Penultimate day of every month::
+* AT commands::
+* Every second Sunday::
+* Two hours every day::
+* Missing the first appointment::
+* Penultimate day of every month::
@end menu
@node AT commands, Every second Sunday, Extended Guile examples, Extended Guile examples
@@ -545,9 +549,9 @@ the variable and runs the command in the user's default shell, as
advertised by the /etc/passwd file.
@menu
-* Paul Vixie's copyright::
-* Crontab file::
-* Incompatibilities with old Unices::
+* Paul Vixie's copyright::
+* Crontab file::
+* Incompatibilities with old Unices::
@end menu
@@ -796,11 +800,11 @@ place in the part which implements the mcron personality.
@menu
-* Invoking mcron::
-* Invoking cron or crond::
+* Invoking mcron::
+* Invoking cron or crond::
* Invoking crontab::
* Behaviour on laptops::
-* Exit codes::
+* Exit codes::
@end menu
@node Invoking mcron, Invoking cron or crond, Invoking, Invoking
@@ -893,21 +897,21 @@ standard output.
@cindex invoking cron
@cindex crond, invokation
@cindex invoking crond
-@cindex @CONFIG_SPOOL_DIR@
-@cindex @CONFIG_SOCKET_FILE@
+@cindex @value{CONFIG_SPOOL_DIR}
+@cindex @value{CONFIG_SOCKET_FILE}
NOTE THAT THIS SECTION ONLY APPLIES IF THE @code{cron} or
@code{crond}, and @code{crontab} PROGRAMS HAVE BEEN INSTALLED BY THE
SYSTEM ADMINISTRATOR.
If the program runs by the name of @code{cron} or @code{crond}, then
-it will read all the files in @code{@CONFIG_SPOOL_DIR@} (which should only
-be readable by root) and the file @code{/etc/crontab}, and then
-detaches itself from the terminal to live forever as a daemon
+it will read all the files in @code{@value{CONFIG_SPOOL_DIR}} (which
+should only be readable by root) and the file @code{/etc/crontab}, and
+then detaches itself from the terminal to live forever as a daemon
process. Additionally, it creates a UNIX socket at
-@code{@CONFIG_SOCKET_FILE@}, and listens for messages sent to that socket
-consisting of a user name whose crontabs have been changed. In this
-case, the program will re-read that user's crontab. This is for
-correct functioning with the crontab program.
+@code{@value{CONFIG_SOCKET_FILE}}, and listens for messages sent to
+that socket consisting of a user name whose crontabs have been
+changed. In this case, the program will re-read that user's crontab.
+This is for correct functioning with the crontab program.
Further, if the @code{--noetc} option was not used, a job is scheduled
to run every minute to check if /etc/crontab has been modified
@@ -1060,7 +1064,7 @@ No problems.
@item 1
An attempt has been made to start cron but there is already a
-@CONFIG_PID_FILE@ file. If there really is no other cron daemon
+@value{CONFIG_PID_FILE} file. If there really is no other cron daemon
running (this does not include invokations of mcron) then you should
remove this file before attempting to run cron.
@@ -1078,9 +1082,9 @@ to be specified in one of these forms.
@item 4
An attempt to run cron has been made by a user who does not have
-permission to access the crontabs in @CONFIG_SPOOL_DIR@. These files
-should be readable only by root, and the cron daemon must be run as
-root.
+permission to access the crontabs in @value{CONFIG_SPOOL_DIR}. These
+files should be readable only by root, and the cron daemon must be run
+as root.
@item 5
An attempt to run mcron has been made, but there are no jobs to
@@ -1088,7 +1092,7 @@ schedule!
@item 6
The system administrator has blocked this user from using crontab with
-the files @CONFIG_ALLOW_FILE@ and @CONFIG_DENY_FILE@.
+the files @value{CONFIG_ALLOW_FILE} and @value{CONFIG_DENY_FILE}.
@item 7
Crontab has been run with more than one of the arguments @code{-l},
@@ -1147,26 +1151,26 @@ non-absolute time specified on the Gregorian calendar (the first day
of next week, for example). Finally, it may be the wish of the user to
provide a program with the functionality of mcron plus a bit extra.
-The core module maintains mcron's internal job lists, and provides the
+The base module maintains mcron's internal job lists, and provides the
main wait-run-wait loop that is mcron's main function. It also
introduces the facilities for accumulating a set of environment
modifiers, which take effect when jobs run.
@menu
-* The core module:: The job list and execution loop.
+* The base module:: The job list and execution loop.
* The redirect module:: Sending output of jobs to a mail box.
* The vixie-time module:: Parsing vixie-style time specifications.
* The job-specifier module:: All commands for scheme configuration files.
* The vixie-specification module:: Commands for reading vixie-style crontabs.
@end menu
-@node The core module, The redirect module, Guile modules, Guile modules
-@section The core module
+@node The base module, The redirect module, Guile modules, Guile modules
+@section The base module
@cindex guile module
-@cindex core module
-@cindex modules, core
+@cindex base module
+@cindex modules, base
-This module may be used by including @code{(use-modules (mcron core))}
+This module may be used by including @code{(use-modules (mcron base))}
in a program. The main functions are @code{add-job} and
@code{run-job-loop}, which allow a program to create a list of job
specifications to run, and then to initiate the wait-run-wait loop
@@ -1218,7 +1222,7 @@ becoming available for reading on one of the file descriptors in the
fd-list, if supplied. Only in this case will the procedure return to
the calling program, which may then make modifications to the job list
before calling the @code{run-job-loop} procedure again to resume execution of
-the mcron core.
+the mcron base.
@end deffn
@deffn{Scheme procedure} remove-user-jobs user
@@ -1239,7 +1243,7 @@ last job that was reported in the schedule report. The report itself
is returned to the calling program as a string.
@end deffn
-@node The redirect module, The vixie-time module, The core module, Guile modules
+@node The redirect module, The vixie-time module, The base module, Guile modules
@section The redirect module
@cindex redirect module
@cindex modules, redirect
@@ -1248,7 +1252,7 @@ This module is introduced to a program with the command
@code{(use-modules (mcron redirect))}.
This module provides the @code{with-mail-out} function, described
-fully in @ref{Guile Syntax}.
+fully in @ref{Guile Syntax}.
@node The vixie-time module, The job-specifier module, The redirect module, Guile modules
@section The vixie-time module
@@ -1260,7 +1264,7 @@ vixie-time))}.
This module provides a single method for converting a vixie-style time
specification into a procedure which can be used as the
-@code{next-time-function} to the core @code{add-job} procedure, or to
+@code{next-time-function} to the base @code{add-job} procedure, or to
the @code{job-specifier} @code{job} procedure. See @ref{Vixie Syntax}
for full details of the allowed format for the time string.
@@ -1325,7 +1329,12 @@ return silently. Otherwise, the behaviour is identical to
Once this module has been declared in a program, a crontab file can be
used to augment the current job list with a call to
-@code{read-vixie-file}.
+@code{read-vixie-file}.
+
+@node GNU Free Documentation License
+@appendix GNU Free Documentation License
+
+@include fdl.texi
@node Index, , Guile modules, Top
@unnumbered Index
diff --git a/makefile.am b/makefile.am
deleted file mode 100644
index 633cf2f..0000000
--- a/makefile.am
+++ /dev/null
@@ -1,84 +0,0 @@
-## Makefile for the toplevel directory of mcron.
-## Copyright (C) 2003 Dale Mellor
-##
-# This file is part of GNU mcron.
-#
-# GNU mcron 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.
-#
-# GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
-## Process this file with automake to produce Makefile.in
-
-SUBDIRS = scm/mcron .
-
-ED = @ED@ # !!!! Are these needed?
-CP = @CP@
-
-MAINTAINERCLEANFILES = configure makefile makefile.in config.guess config.sub \
- install-sh missing texinfo.tex INSTALL \
- aclocal.m4 compile depcomp mcron.1
-
-CLEANFILES = mcron.c
-
-EXTRA_DIST = makefile.ed mcron.c.template BUGS
-
-info_TEXINFOS = mcron.texinfo
-
-dist_man_MANS = mcron.1
-
-bin_PROGRAMS = mcron
-mcron_SOURCES = mcron.c
-mcron_LDADD = @GUILE_LIBS@
-
-# The second option is so that we can execute the binary in the local directory,
-# in turn so that we can do mcron --help during the build process.
-mcron_CFLAGS = @GUILE_CFLAGS@ -DGUILE_LOAD_PATH=\"$(datadir):./scm:...\"
-
-
-mcron.c : scm/mcron/main.scm scm/mcron/crontab.scm makefile.ed mcron.c.template
- @echo 'Building mcron.c...'
- @$(ED) < makefile.ed > /dev/null 2>&1
- @rm -f mcron.escaped.scm > /dev/null 2>&1
-
-
-#full program prefix
-fpp = $(DESTDIR)$(bindir)/@real_program_prefix@
-
-
-install-exec-hook:
- @if [ "x@NO_VIXIE_CLOBBER@" != "xyes" -a "`id -u`" -eq "0" ]; then \
- rm -f $(fpp)cron$(EXEEXT) > /dev/null 2>&1; \
- $(INSTALL) --mode='u=rwx' mcron$(EXEEXT) $(fpp)cron$(EXEEXT); \
- rm -f $(fpp)crontab$(EXEEXT) > /dev/null 2>&1; \
- $(INSTALL) --mode='u=rwxs,og=rx' mcron$(EXEEXT) $(fpp)crontab$(EXEEXT); \
- $(INSTALL) -d --mode='u=rwx' $(DESTDIR)/var/cron; \
- $(INSTALL) -d --mode='u=rwx,og=rx' $(DESTDIR)/var/run; \
- $(INSTALL) -d --mode='u=rwx,og=rx' $(DESTDIR)@GUILE_SITE@; \
- $(INSTALL) -d --mode='u=rwx,og=rx' $(DESTDIR)@GUILE_SITE@/mcron; \
- elif [ "x@NO_VIXIE_CLOBBER@" = "xyes" ]; then \
- echo "Not installing Vixie-style programs"; \
- else \
- echo "+++ WARNING: NON-ROOT INSTALL: ONLY mcron WILL BE INSTALLED, NOT ANY OF THE VIXIE REPLACEMENT PROGRAMS"; \
- fi
-
-
-uninstall-hook:
- if [ "`id -u`" -eq "0" ]; then \
- rm -f $(fpp){cron,crontab}$(EXEEXT); \
- fi
-
-
-# Not part of formal package building, but a rule for manual use to get the
-# elemental man page. Will only work once the mcron program is installed.
-mcron.1 : mcron.c
- $(HELP2MAN) -n 'a program to run tasks at regular (or not) intervals' \
- ./mcron > mcron.1
diff --git a/makefile.ed b/makefile.ed
deleted file mode 100644
index 7047ec7..0000000
--- a/makefile.ed
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2003 Dale Mellor
-#
-# This file is part of GNU mcron.
-#
-# GNU mcron 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.
-#
-# GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-#
-#
-#
-e scm/mcron/main.scm
-/\(load "crontab.scm"\)/d
--1r scm/mcron/crontab.scm
-%s/\\/\\\\/g
-%s/"/\\"/g
-%s/ *;;.*$/ /g
-g/^ *$/d
-%s/^/\"/
-%s/$/\"/
-w mcron.escaped.scm
-e mcron.c.template
-/GUILE_PROGRAM_GOES_HERE/d
--1r mcron.escaped.scm
-w mcron.c
-q
diff --git a/mcron.c.template b/mcron.c.template
deleted file mode 100644
index e9a755d..0000000
--- a/mcron.c.template
+++ /dev/null
@@ -1,120 +0,0 @@
-/* -*-c-*- */
-/*
- * Copyright (C) 2003, 2014 Dale Mellor
- *
- * This file is part of GNU mcron.
- *
- * GNU mcron 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.
- *
- * GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-/*
- This C code represents the thinnest possible wrapper around the Guile code
- which constitutes all the functionality of the mcron program. There are two
- plus one reasons why we need to do this, and one very unfortunate
- consequence.
-
- Firstly, SUID does not work on an executable script. In the end, it is
- the execution of the translator, in our case guile, which determines the
- effective user, and it is not wise to make the system guile installation
- SUID root!
-
- Secondly, executable scripts show up in ugly ways in listings of the
- system process table. Guile in particular, with its multi-line
- #! ...\ \n -s ...!#
- idiosyncracies shows up in process listings in a way that is difficult
- to determine what program is actually running.
-
- A third reason for the C wrapper which might be mentioned is that a
- security-conscious system administrator can choose to only install a
- binary, thus removing the possibility of a user studying a guile script
- and working out ways of hacking it to his own ends, or worse still
- finding a way to modify it to his own ends.
-
- Unfortunately, running the guile script from inside a C program means
- that the sigaction function does not work. Instead, it is necessary to
- perform the signal processing in C.
-
- The guile code itself is substituted for the GU1LE_PROGRAM_GOES_HERE (sic)
- token by the makefile, which processes the scheme to make it look like one
- big string.
-*/
-
-
-
-#include <string.h>
-#include <signal.h>
-#include <libguile.h>
-
-
-
-/* This is a function designed to be installed as a signal handler, for signals
- which are supposed to initiate shutdown of this program. It calls the scheme
- procedure (see mcron.scm for details) to do all the work, and then exits. */
-
-void
-react_to_terminal_signal (int sig)
-{
- scm_c_eval_string ("(delete-run-file)");
- exit (1);
-}
-
-
-
-/* This is a function designed to be callable from scheme, and sets up all the
- signal handlers required by the cron personality. */
-
-SCM
-set_cron_signals ()
-{
- static struct sigaction sa;
- memset (&sa, 0, sizeof (sa));
- sa.sa_handler = react_to_terminal_signal;
- sigaction (SIGTERM, &sa, 0);
- sigaction (SIGINT, &sa, 0);
- sigaction (SIGQUIT, &sa, 0);
- sigaction (SIGHUP, &sa, 0);
-
- return SCM_BOOL_T;
-}
-
-
-
-/* The effective main function (i.e. the one that actually does some work). We
- register the function above with the guile system, and then execute the mcron
- guile program. */
-
-void
-inner_main ()
-{
- scm_c_define_gsubr ("c-set-cron-signals", 0, 0, 0, set_cron_signals);
-
- scm_c_eval_string (
- GUILE_PROGRAM_GOES_HERE
- );
-}
-
-
-
-/* The real main function. Does nothing but start up the guile subsystem. */
-
-int
-main (int argc, char **argv)
-{
- setenv ("GUILE_LOAD_PATH", GUILE_LOAD_PATH, 1);
-
- scm_boot_guile (argc, argv, inner_main, 0);
-
- return 0;
-}
diff --git a/scm/mcron/config.scm.in b/scm/mcron/config.scm.in
deleted file mode 100644
index 6a0a85d..0000000
--- a/scm/mcron/config.scm.in
+++ /dev/null
@@ -1,35 +0,0 @@
-;; -*-scheme-*-
-
-;; Copyright (C) 2003 Dale Mellor
-;;
-;; This file is part of GNU mcron.
-;;
-;; GNU mcron 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.
-;;
-;; GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
-
-;; Some constants set by the configuration process.
-
-(define-module (mcron config))
-
-(define-public config-debug @CONFIG_DEBUG@)
-(define-public config-package-string "@PACKAGE_STRING@")
-(define-public config-package-bugreport "@PACKAGE_BUGREPORT@")
-(define-public config-sendmail "@SENDMAIL@")
-
-(define-public config-spool-dir "@CONFIG_SPOOL_DIR@")
-(define-public config-socket-file "@CONFIG_SOCKET_FILE@")
-(define-public config-allow-file "@CONFIG_ALLOW_FILE@")
-(define-public config-deny-file "@CONFIG_DENY_FILE@")
-(define-public config-pid-file "@CONFIG_PID_FILE@")
-(define-public config-tmp-dir "@CONFIG_TMP_DIR@")
diff --git a/scm/mcron/crontab.scm b/scm/mcron/crontab.scm
deleted file mode 100644
index 30e5592..0000000
--- a/scm/mcron/crontab.scm
+++ /dev/null
@@ -1,228 +0,0 @@
-;; Copyright (C) 2003, 2014 Dale Mellor
-;;
-;; This file is part of GNU mcron.
-;;
-;; GNU mcron 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.
-;;
-;; GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
-
-;; Apart from the collecting of options and the handling of --help and --version
-;; (which are done in the main.scm file), this file provides all the
-;; functionality of the crontab personality. It is designed to be loaded and run
-;; once, and then the calling program can exit and the crontab program will have
-;; completed its function.
-
-
-
-;; Procedure to communicate with running cron daemon that a user has modified
-;; his crontab. The user name is written to the /var/cron/socket UNIX socket.
-
-(let ((hit-server
- (lambda (user-name)
- (catch #t (lambda ()
- (let ((socket (socket AF_UNIX SOCK_STREAM 0)))
- (connect socket AF_UNIX config-socket-file)
- (display user-name socket)
- (close socket)))
- (lambda (key . args)
- (display "Warning: a cron daemon is not running.\n")))))
-
-
-
-;; Procedure to scan a file containing one user name per line (such as
-;; /var/cron/allow and /var/cron/deny), and determine if the given name is in
-;; there. The procedure returns #t, #f, or '() if the file does not exist.
-
- (in-access-file?
- (lambda (file name)
- (catch #t (lambda ()
- (with-input-from-file
- file
- (lambda ()
- (let loop ((input (read-line)))
- (if (eof-object? input)
- #f
- (if (string=? input name)
- #t
- (loop (read-line))))))))
- (lambda (key . args) '()))))
-
-
-
- ;; This program should have been installed SUID root. Here we get the
- ;; passwd entry for the real user who is running this program.
-
- (crontab-real-user (passwd:name (getpw (getuid)))))
-
-
-
- ;; If the real user is not allowed to use crontab due to the /var/cron/allow
- ;; and/or /var/cron/deny files, bomb out now.
-
- (if (or (eq? (in-access-file? config-allow-file crontab-real-user) #f)
- (eq? (in-access-file? config-deny-file crontab-real-user) #t))
- (mcron-error 6 "Access denied by system operator."))
-
-
-
- ;; Check that no more than one of the mutually exclusive options are being
- ;; used.
-
- (if (> (+ (if (option-ref options 'edit #f) 1 0)
- (if (option-ref options 'list #f) 1 0)
- (if (option-ref options 'remove #f) 1 0))
- 1)
- (mcron-error 7 "Only one of options -e, -l or -r can be used."))
-
-
-
- ;; Check that a non-root user is trying to read someone else's files.
-
- (if (and (not (eqv? (getuid) 0))
- (option-ref options 'user #f))
- (mcron-error 8 "Only root can use the -u option."))
-
-
-
- (let (
-
-
- ;; Iff the --user option is given, the crontab-user may be different
- ;; from the real user.
-
- (crontab-user (option-ref options 'user crontab-real-user))
-
-
- ;; So now we know which crontab file we will be manipulating.
-
- (crontab-file (string-append config-spool-dir "/" crontab-user))
-
-
-
- ;; Display the prompt and wait for user to type his choice. Return #t if
- ;; the answer begins with 'y' or 'Y', return #f if it begins with 'n' or
- ;; 'N', otherwise ask again.
-
- (get-yes-no (lambda (prompt . re-prompt)
- (if (not (null? re-prompt))
- (display "Please answer y or n.\n"))
- (display (string-append prompt " "))
- (let ((r (read-line)))
- (if (not (string-null? r))
- (case (string-ref r 0)
- ((#\y #\Y) #t)
- ((#\n #\N) #f)
- (else (get-yes-no prompt #t)))
- (get-yes-no prompt #t))))))
-
-
-
- ;; There are four possible sub-personalities to the crontab personality:
- ;; list, remove, edit and replace (when the user uses no options but
- ;; supplies file names on the command line).
-
- (cond
-
-
- ;; In the list personality, we simply open the crontab and copy it
- ;; character-by-character to the standard output. If anything goes wrong, it
- ;; can only mean that this user does not have a crontab file.
-
- ((option-ref options 'list #f)
- (catch #t (lambda ()
- (with-input-from-file crontab-file (lambda ()
- (do ((input (read-char) (read-char)))
- ((eof-object? input))
- (display input)))))
- (lambda (key . args)
- (display (string-append "No crontab for "
- crontab-user
- " exists.\n")))))
-
-
- ;; In the edit personality, we determine the name of a temporary file and an
- ;; editor command, copy an existing crontab file (if it is there) to the
- ;; temporary file, making sure the ownership is set so the real user can edit
- ;; it; once the editor returns we try to read the file to check that it is
- ;; parseable (but do nothing more with the configuration), and if it is okay
- ;; (this program is still running!) we move the temporary file to the real
- ;; crontab, wake the cron daemon up, and remove the temporary file. If the
- ;; parse fails, we give user a choice of editing the file again or quitting
- ;; the program and losing all changes made.
-
- ((option-ref options 'edit #f)
- (let ((temp-file (string-append config-tmp-dir
- "/crontab."
- (number->string (getpid)))))
- (catch #t (lambda () (copy-file crontab-file temp-file))
- (lambda (key . args) (with-output-to-file temp-file noop)))
- (chown temp-file (getuid) (getgid))
- (let retry ()
- (system (string-append
- (or (getenv "VISUAL") (getenv "EDITOR") "vi")
- " "
- temp-file))
- (catch 'mcron-error
- (lambda () (read-vixie-file temp-file))
- (lambda (key exit-code . msg)
- (apply mcron-error 0 msg)
- (if (get-yes-no "Edit again?")
- (retry)
- (begin
- (mcron-error 0 "Crontab not changed")
- (primitive-exit 0))))))
- (copy-file temp-file crontab-file)
- (delete-file temp-file)
- (hit-server crontab-user)))
-
-
- ;; In the remove personality we simply make an effort to delete the crontab and
- ;; wake the daemon. No worries if this fails.
-
- ((option-ref options 'remove #f)
- (catch #t (lambda () (delete-file crontab-file)
- (hit-server crontab-user))
- noop))
-
-
- ;; !!!! This comment is wrong.
-
- ;; In the case of the replace personality we loop over all the arguments on the
- ;; command line, and for each one parse the file to make sure it is parseable
- ;; (but subsequently ignore the configuration), and all being well we copy it
- ;; to the crontab location; we deal with the standard input in the same way but
- ;; different. :-) In either case the server is woken so that it will read the
- ;; newly installed crontab.
-
- ((not (null? (option-ref options '() '())))
- (let ((input-file (car (option-ref options '() '()))))
- (catch-mcron-error
- (if (string=? input-file "-")
- (let ((input-string (stdin->string)))
- (read-vixie-port (open-input-string input-string))
- (with-output-to-file crontab-file (lambda ()
- (display input-string))))
- (begin
- (read-vixie-file input-file)
- (copy-file input-file crontab-file))))
- (hit-server crontab-user)))
-
-
- ;; The user is being silly. The message here is identical to the one Vixie cron
- ;; used to put out, for total compatibility.
-
- (else
- (mcron-error 15 "usage error: file name must be specified for replace.")))
-
-
-)) ;; End of file-level let-scopes.
diff --git a/scm/mcron/environment.scm b/scm/mcron/environment.scm
deleted file mode 100644
index 9f694f1..0000000
--- a/scm/mcron/environment.scm
+++ /dev/null
@@ -1,105 +0,0 @@
-;; Copyright (C) 2003 Dale Mellor
-;;
-;; This file is part of GNU mcron.
-;;
-;; GNU mcron 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.
-;;
-;; GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
-
-
-;; This file defines the variable current-environment-mods, and the procedures
-;; append-environment-mods (which is available to user configuration files),
-;; clear-environment-mods and modify-environment. The idea is that the
-;; current-environment-mods is a list of pairs of environment names and values,
-;; and represents the cumulated environment settings in a configuration
-;; file. When a job definition is seen in a configuration file, the
-;; current-environment-mods are copied into the internal job description, and
-;; when the job actually runs these environment modifications are applied to
-;; the UNIX environment in which the job runs.
-
-
-
-
-(define-module (mcron environment)
- #:export (modify-environment
- clear-environment-mods
- append-environment-mods
- get-current-environment-mods-copy))
-
-
-
-
-;; The env-alist is an association list of variable names and values. Variables
-;; later in the list will take precedence over variables before. We return a
-;; fixed-up version in which some variables are given specific default values
-;; (which the user can override), and two variables which the user is not
-;; allowed to control are added at the end of the list.
-
-(define (impose-default-environment env-alist passwd-entry)
- (append `(("HOME" . ,(passwd:dir passwd-entry))
- ("CWD" . ,(passwd:dir passwd-entry))
- ("SHELL" . ,(passwd:shell passwd-entry))
- ("TERM" . #f)
- ("TERMCAP" . #f))
- env-alist
- `(("LOGNAME" . ,(passwd:name passwd-entry))
- ("USER" . ,(passwd:name passwd-entry)))))
-
-
-
-
-;; Modify the UNIX environment for the current process according to the given
-;; association list of variables, with the default variable values imposed.
-
-(define (modify-environment env-alist passwd-entry)
- (for-each (lambda (variable)
- (setenv (car variable) (cdr variable)))
- (impose-default-environment env-alist passwd-entry)))
-
-
-
-
-;; As we parse configuration files, we build up an alist of environment
-;; variables here.
-
-(define current-environment-mods '())
-
-
-
-;; Each time a job is added to the system, we take a snapshot of the current
-;; set of environment modifiers.
-
-(define (get-current-environment-mods-copy)
- (list-copy current-environment-mods))
-
-
-
-;; When we start to parse a new configuration file, we want to start with a
-;; fresh environment (actually an umodified version of the pervading mcron
-;; environment).
-
-(define (clear-environment-mods)
- (set! current-environment-mods '()))
-
-
-
-;; Procedure to add another environment setting to the alist above. This is
-;; used both implicitly by the Vixie parser, and can be used directly by users
-;; in scheme configuration files. The return value is purely for the
-;; convenience of the parse-vixie-environment in the vixie-specification module
-;; (yuk).
-
-(define (append-environment-mods name value)
- (set! current-environment-mods (append current-environment-mods
- (list (cons name value))))
- #t)
diff --git a/scm/mcron/job-specifier.scm b/scm/mcron/job-specifier.scm
deleted file mode 100644
index cce948c..0000000
--- a/scm/mcron/job-specifier.scm
+++ /dev/null
@@ -1,272 +0,0 @@
-;; Copyright (C) 2003 Dale Mellor
-;;
-;; This file is part of GNU mcron.
-;;
-;; GNU mcron 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.
-;;
-;; GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
-
-
-;; This module defines all the functions that can be used by scheme mcron
-;; configuration files, namely the procedures for working out next times, the
-;; job procedure for registering new jobs (actually a wrapper around the core
-;; add-job function), and the procedure for declaring environment modifications.
-
-(define-module (mcron job-specifier)
- #:export (range
- next-year-from next-year
- next-month-from next-month
- next-day-from next-day
- next-hour-from next-hour
- next-minute-from next-minute
- next-second-from next-second
- set-configuration-user
- set-configuration-time
- job
- find-best-next)
- #:use-module (mcron core)
- #:use-module (mcron environment)
- #:use-module (mcron vixie-time)
- #:re-export (append-environment-mods))
-
-
-
-;; Function (available to user configuration files) which produces a list of
-;; values from start up to (but not including) end. An optional step may be
-;; supplied, and (if positive) only every step'th value will go into the
-;; list. For example, (range 1 6 2) returns '(1 3 5).
-
-(define (range start end . step)
- (let ((step (if (or (null? step)
- (<= (car step) 0))
- 1
- (car step))))
- (let loop ((start start))
- (if (>= start end) '()
- (cons start
- (loop (+ start step)))))))
-
-
-
-;; Internal function (not supposed to be used directly in configuration files;
-;; it is exported from the module for the convenience of other parts of the
-;; mcron implementation) which takes a value and a list of possible next values
-;; (all assumed less than 9999). It returns a pair consisting of the smallest
-;; element of the list, and the smallest element larger than the current
-;; value. If an example of the latter cannot be found, 9999 will be returned.
-
-(define (find-best-next current next-list)
- (let ((current-best (cons 9999 9999)))
- (for-each (lambda (allowed-time)
- (if (< allowed-time (car current-best))
- (set-car! current-best allowed-time))
- (if (and (> allowed-time current)
- (< allowed-time (cdr current-best)))
- (set-cdr! current-best allowed-time)))
- next-list)
- current-best))
-
-
-
-;; Internal function to return the time corresponding to some near future
-;; hour. If hour-list is not supplied, the time returned corresponds to the
-;; start of the next hour of the day.
-;;
-;; If the hour-list is supplied the time returned corresponds to the first hour
-;; of the day in the future which is contained in the list. If all the values in
-;; the list are less than the current hour, then the time returned will
-;; correspond to the first hour in the list *on the following day*.
-;;
-;; ... except that the function is actually generalized to deal with seconds,
-;; minutes, etc., in an obvious way :-)
-;;
-;; Note that value-list always comes from an optional argument to a procedure,
-;; so is wrapped up as the first element of a list (i.e. it is a list inside a
-;; list).
-
-(define (bump-time time value-list component higher-component
- set-component! set-higher-component!)
- (if (null? value-list)
- (set-component! time (+ (component time) 1))
- (let ((best-next (find-best-next (component time) (car value-list))))
- (if (eqv? 9999 (cdr best-next))
- (begin
- (set-higher-component! time (+ (higher-component time) 1))
- (set-component! time (car best-next)))
- (set-component! time (cdr best-next)))))
- (car (mktime time)))
-
-
-
-
-;; Set of configuration methods which use the above general function to bump
-;; specific components of time to the next legitimate value. In each case, all
-;; the components smaller than that of interest are taken to zero, so that for
-;; example the time of the next year will be the time at which the next year
-;; actually starts.
-
-(define (next-year-from current-time . year-list)
- (let ((time (localtime current-time)))
- (set-tm:mon time 0)
- (set-tm:mday time 1)
- (set-tm:hour time 0)
- (set-tm:min time 0)
- (set-tm:sec time 0)
- (bump-time time year-list tm:year tm:year set-tm:year set-tm:year)))
-
-(define (next-month-from current-time . month-list)
- (let ((time (localtime current-time)))
- (set-tm:mday time 1)
- (set-tm:hour time 0)
- (set-tm:min time 0)
- (set-tm:sec time 0)
- (bump-time time month-list tm:mon tm:year set-tm:mon set-tm:year)))
-
-(define (next-day-from current-time . day-list)
- (let ((time (localtime current-time)))
- (set-tm:hour time 0)
- (set-tm:min time 0)
- (set-tm:sec time 0)
- (bump-time time day-list tm:mday tm:mon set-tm:mday set-tm:mon)))
-
-(define (next-hour-from current-time . hour-list)
- (let ((time (localtime current-time)))
- (set-tm:min time 0)
- (set-tm:sec time 0)
- (bump-time time hour-list tm:hour tm:mday set-tm:hour set-tm:mday)))
-
-(define (next-minute-from current-time . minute-list)
- (let ((time (localtime current-time)))
- (set-tm:sec time 0)
- (bump-time time minute-list tm:min tm:hour set-tm:min set-tm:hour)))
-
-(define (next-second-from current-time . second-list)
- (let ((time (localtime current-time)))
- (bump-time time second-list tm:sec tm:min set-tm:sec set-tm:min)))
-
-
-
-;; The current-action-time is the time a job was last run, the time from which
-;; the next time to run a job must be computed. (When the program is first run,
-;; this time is set to the configuration time so that jobs run from that moment
-;; forwards.) Once we have this, we supply versions of the time computation
-;; commands above which implicitly assume this value.
-
-(define current-action-time 0)
-
-
-
-;; We want to provide functions which take a single optional argument (as well
-;; as implicitly the current action time), but unlike usual scheme behaviour if
-;; the argument is missing we want to act like it is really missing, and if it
-;; is there we want to act like it is a genuine argument, not a list of
-;; optionals.
-
-(define (maybe-args function args)
- (if (null? args)
- (function current-action-time)
- (function current-action-time (car args))))
-
-
-
-;; These are the convenience functions we were striving to define for the
-;; configuration files. They are wrappers for the next-X-from functions above,
-;; but implicitly use the current-action-time for the time argument.
-
-(define (next-year . args) (maybe-args next-year-from args))
-(define (next-month . args) (maybe-args next-month-from args))
-(define (next-day . args) (maybe-args next-day-from args))
-(define (next-hour . args) (maybe-args next-hour-from args))
-(define (next-minute . args) (maybe-args next-minute-from args))
-(define (next-second . args) (maybe-args next-second-from args))
-
-
-
-;; The default user for running jobs is the current one (who invoked this
-;; program). There are exceptions: when cron parses /etc/crontab the user is
-;; specified on each individual line; when cron parses /var/cron/tabs/* the user
-;; is derived from the filename of the crontab. These cases are dealt with by
-;; mutating this variable. Note that the variable is only used at configuration
-;; time; a UID is stored with each job and it is that which takes effect when
-;; the job actually runs.
-
-(define configuration-user (getpw (getuid)))
-(define configuration-time (current-time))
-
-(define (set-configuration-user user)
- (set! configuration-user (if (or (string? user)
- (integer? user))
- (getpw user)
- user)))
-(define (set-configuration-time time) (set! configuration-time time))
-
-
-
-;; The job function, available to configuration files for adding a job rule to
-;; the system.
-;;
-;; Here we must 'normalize' the next-time-function so that it is always a lambda
-;; function which takes one argument (the last time the job ran) and returns a
-;; single value (the next time the job should run). If the input value is a
-;; string this is parsed as a Vixie-style time specification, and if it is a
-;; list then we arrange to eval it (but note that such lists are expected to
-;; ignore the function parameter - the last run time is always read from the
-;; current-action-time global variable). A similar normalization is applied to
-;; the action.
-;;
-;; Here we also compute the first time that the job is supposed to run, by
-;; finding the next legitimate time from the current configuration time (set
-;; right at the top of this program).
-
-(define (job time-proc action . displayable)
- (let ((action (cond ((procedure? action) action)
- ((list? action) (lambda () (primitive-eval action)))
- ((string? action) (lambda () (system action)))
- (else
- (throw 'mcron-error
- 2
- "job: invalid second argument (action; should be lambda"
- " function, string or list)"))))
-
- (time-proc
- (cond ((procedure? time-proc) time-proc)
- ((string? time-proc) (parse-vixie-time time-proc))
- ((list? time-proc) (lambda (current-time)
- (primitive-eval time-proc)))
- (else
- (throw 'mcron-error
- 3
- "job: invalid first argument (next-time-function; should ")
- "be function, string or list)")))
- (displayable
- (cond ((not (null? displayable)) (car displayable))
- ((procedure? action) "Lambda function")
- ((string? action) action)
- ((list? action) (with-output-to-string
- (lambda () (display action)))))))
- (add-job (lambda (current-time)
- (set! current-action-time current-time) ;; ?? !!!! Code
-
- ;; Contributed by Sergey Poznyakoff to allow for daylight savings
- ;; time changes.
- (let* ((next (time-proc current-time))
- (gmtoff (tm:gmtoff (localtime next)))
- (d (+ next (- gmtoff
- (tm:gmtoff (localtime current-time))))))
- (if (eqv? (tm:gmtoff (localtime d)) gmtoff)
- d
- next)))
- action
- displayable
- configuration-time
- configuration-user)))
diff --git a/scm/mcron/main.scm b/scm/mcron/main.scm
deleted file mode 100644
index 36adef9..0000000
--- a/scm/mcron/main.scm
+++ /dev/null
@@ -1,503 +0,0 @@
-;; Copyright (C) 2003, 2012 Dale Mellor
-;;
-;; This file is part of GNU mcron.
-;;
-;; GNU mcron 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.
-;;
-;; GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
-
-
-;; This is the 'main' routine for the whole system; the top of this file is the
-;; global entry point (after the minimal C wrapper, mcron.c.template); to all
-;; intents and purposes the program is pure Guile and starts here.
-;;
-;; This file is built into mcron.c.template by the makefile, which stringifies
-;; the whole lot, and escapes quotation marks and escape characters
-;; accordingly. Bear this in mind when considering literal multi-line strings.
-;;
-;; (l0ad "crontab.scm") (sic) is inlined by the makefile. All other
-;; functionality comes through modules in .../share/guile/site/mcron/*.scm.
-
-
-
-;; Pull in some constants set by the builder (via autoconf) at configuration
-;; time. Turn debugging on if indicated.
-
-(use-modules (mcron config))
-(if config-debug (begin (debug-enable 'debug)
- (debug-enable 'backtrace)))
-
-
-
-;; To determine the name of the program, scan the first item of the command line
-;; backwards for the first non-alphabetic character. This allows names like
-;; in.cron to be accepted as an invocation of the cron command.
-
-(use-modules (ice-9 regex) (ice-9 rdelim))
-
-(define command-name (match:substring (regexp-exec (make-regexp "[[:alpha:]]*$")
- (car (command-line)))))
-
-
-
-;; Code contributed by Sergey Poznyakoff. Print an error message (made up from
-;; the parts of rest), and if the error is fatal (present and non-zero) then
-;; exit to the system with this code.
-
-(define (mcron-error exit-code . rest)
- (with-output-to-port (current-error-port)
- (lambda ()
- (for-each display (append (list command-name ": ") rest))
- (newline)))
- (if (and exit-code (not (eq? exit-code 0)))
- (primitive-exit exit-code)))
-
-
-
-;; Code contributed by Sergey Poznyakoff. Execute body. If an 'mcron-error
-;; exception occurs, print its diagnostics and exit with its error code.
-
-(defmacro catch-mcron-error (. body)
- `(catch 'mcron-error
- (lambda ()
- ,@body)
- (lambda (key exit-code . msg)
- (apply mcron-error exit-code msg))))
-
-
-
-;; We will be doing a lot of testing of the command name, so it makes sense to
-;; perform the string comparisons once and for all here.
-
-(define command-type (cond ((string=? command-name "mcron") 'mcron)
- ((or (string=? command-name "cron")
- (string=? command-name "crond")) 'cron)
- ((string=? command-name "crontab") 'crontab)
- (else
- (mcron-error 12 "The command name is invalid."))))
-
-
-
-;; There are a different set of options for the crontab personality compared to
-;; all the others, with the --help and --version options common to all the
-;; personalities.
-
-(use-modules (ice-9 getopt-long))
-
-(define options
- (catch
- 'misc-error
- (lambda ()
- (getopt-long (command-line)
- (append
- (case command-type
- ((crontab)
- '((user (single-char #\u) (value #t))
- (edit (single-char #\e) (value #f))
- (list (single-char #\l) (value #f))
- (remove (single-char #\r) (value #f))))
- (else `((schedule (single-char #\s) (value #t)
- (predicate
- ,(lambda (value)
- (string->number value))))
- (daemon (single-char #\d) (value #f))
- (noetc (single-char #\n) (value #f))
- (stdin (single-char #\i) (value #t)
- (predicate
- ,(lambda (value)
- (or (string=? "vixie" value)
- (string=? "guile" value))))))))
- '((version (single-char #\v) (value #f))
- (help (single-char #\h) (value #f))))))
- (lambda (key func fmt args . rest)
- (mcron-error 1 (apply format (append (list #f fmt) args))))))
-
-;; If the user asked for the version of this program, give it to him and get
-;; out.
-
-(if (option-ref options 'version #f)
- (begin
- (display (string-append "\n
-" command-name " (" config-package-string ")\n
-Written by Dale Mellor\n
-\n
-Copyright (C) 2003, 2006, 2014 Dale Mellor\n
-This is free software; see the source for copying conditions. There is NO\n
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n
-"))
- (quit)))
-
-
-
-;; Likewise if the user requested the help text.
-
-(if (option-ref options 'help #f)
- (begin
- (display (string-append "
-Usage: " (car (command-line))
-(case command-type
-
- ((mcron)
-" [OPTIONS] [FILES]\n
-Run an mcron process according to the specifications in the FILES (`-' for\n
-standard input), or use all the files in ~/.config/cron (or the \n
-deprecated ~/.cron) with .guile or .vixie extensions.\n
-\n
- -v, --version Display version\n
- -h, --help Display this help message\n
- -sN, --schedule[=]N Display the next N jobs that will be run by mcron\n
- -d, --daemon Immediately detach the program from the terminal\n
- and run as a daemon process\n
- -i, --stdin=(guile|vixie) Format of data passed as standard input or\n
- file arguments (default guile)")
-
- ((cron)
-" [OPTIONS]\n
-Unless an option is specified, run a cron daemon as a detached process, \n
-reading all the information in the users' crontabs and in /etc/crontab.\n
-\n
- -v, --version Display version\n
- -h, --help Display this help message\n
- -sN, --schedule[=]N Display the next N jobs that will be run by cron\n
- -n, --noetc Do not check /etc/crontab for updates (HIGHLY\n
- RECOMMENDED).")
-
- ((crontab)
- (string-append " [-u user] file\n"
- " " (car (command-line)) " [-u user] { -e | -l | -r }\n"
- " (default operation is replace, per 1003.2)\n"
- " -e (edit user's crontab)\n"
- " -l (list user's crontab)\n"
- " -r (delete user's crontab)\n"))
-
- (else "rubbish"))
-
-"\n\n
-Report bugs to " config-package-bugreport ".\n
-"))
- (quit)))
-
-
-
-;; This is called from the C front-end whenever a terminal signal is
-;; received. We remove the /var/run/cron.pid file so that crontab and other
-;; invocations of cron don't get the wrong idea that a daemon is currently
-;; running.
-
-(define (delete-run-file)
- (catch #t (lambda () (delete-file config-pid-file)
- (delete-file config-socket-file))
- noop)
- (quit))
-
-
-
-;; Setup the cron process, if appropriate. If there is already a
-;; /var/run/cron.pid file, then we must assume a cron daemon is already running
-;; and refuse to start another one.
-;;
-;; Otherwise, clear the MAILTO environment variable so that output from cron
-;; jobs is sent to the various users (this may still be overridden in the
-;; configuration files), and call the function in the C wrapper to set up
-;; terminal signal responses to vector to the procedure above. The PID file will
-;; be filled in properly later when we have forked our daemon process (but not
-;; done if we are only viewing the schedules).
-
-(if (eq? command-type 'cron)
- (begin
- (if (not (eqv? (getuid) 0))
- (mcron-error 16
- "This program must be run by the root user (and should "
- "have been installed as such)."))
- (if (access? config-pid-file F_OK)
- (mcron-error 1
- "A cron daemon is already running.\n"
- " (If you are sure this is not true, remove the file\n"
- " "
- config-pid-file
- ".)"))
- (if (not (option-ref options 'schedule #f))
- (with-output-to-file config-pid-file noop))
- (setenv "MAILTO" #f)
- (c-set-cron-signals)))
-
-
-
-;; Define the functions available to the configuration files. While we're here,
-;; we'll get the core loaded as well.
-
-(use-modules (mcron core)
- (mcron job-specifier)
- (mcron vixie-specification))
-
-
-
-;; Procedure to slurp the standard input into a string.
-
-(define (stdin->string)
- (with-output-to-string (lambda () (do ((in (read-char) (read-char)))
- ((eof-object? in))
- (display in)))))
-
-
-
-;; Now we have the procedures in place for dealing with the contents of
-;; configuration files, the crontab personality is able to validate such
-;; files. If the user requested the crontab personality, we load and run the
-;; code here and then get out.
-
-(if (eq? command-type 'crontab)
- (begin
- (load "crontab.scm")
- (quit)))
-
-
-
-;; Code contributed by Sergey Poznyakoff. Determine if the given file is a
-;; regular file or not.
-
-(define (regular-file? file)
- (catch 'system-error
- (lambda ()
- (eq? (stat:type (stat file)) 'regular))
- (lambda (key call fmt args . rest)
- (mcron-error 0 (apply format (append (list #f fmt) args)))
- #f)))
-
-
-
-;; Procedure which processes any configuration file according to the
-;; extension. If a file is not recognized, it is silently ignored (this deals
-;; properly with most editors' backup files, for instance).
-
-(define guile-file-regexp (make-regexp "\\.gui(le)?$"))
-(define vixie-file-regexp (make-regexp "\\.vix(ie)?$"))
-
-(define (process-user-file file-path . assume-guile)
- (cond ((string=? file-path "-")
- (if (string=? (option-ref options 'stdin "guile") "vixie")
- (read-vixie-port (current-input-port))
- (eval-string (stdin->string))))
- ((or (not (null? assume-guile))
- (regexp-exec guile-file-regexp file-path))
- (load file-path))
- ((regexp-exec vixie-file-regexp file-path)
- (read-vixie-file file-path))))
-
-
-
-;; Procedure to run through all the files in a user's ~/.cron and/or
-;; $XDG_CONFIG_HOME/cron or ~/.config/cron directories (only happens under the
-;; mcron personality).
-
-(define (process-files-in-user-directory)
- (let ((errors 0)
- (home-directory (passwd:dir (getpw (getuid)))))
- (map (lambda (config-directory)
- (catch #t
- (lambda ()
- (let ((directory (opendir config-directory)))
- (do ((file-name (readdir directory) (readdir directory)))
- ((eof-object? file-name) (closedir directory))
- (process-user-file (string-append config-directory
- "/"
- file-name)))))
- (lambda (key . args)
- (set! errors (1+ errors)))))
- (list (string-append home-directory "/.cron")
- (string-append (or (getenv "XDG_CONFIG_HOME")
- (string-append home-directory "/.config"))
- "/cron")))
- (if (eq? 2 errors)
- (mcron-error 13
- "Cannot read files in your ~/.config/cron (or ~/.cron) "
- "directory."))))
-
-
-
-;; Procedure to check that a user name is in the passwd database (it may happen
-;; that a user is removed after creating a crontab). If the user name is valid,
-;; the full passwd entry for that user is returned to the caller.
-
-(define (valid-user user-name)
- (setpwent)
- (do ((entry (getpw) (getpw)))
- ((or (not entry)
- (string=? (passwd:name entry) user-name))
- (endpwent)
- entry)))
-
-
-
-;; Procedure to process all the files in the crontab directory, making sure that
-;; each file is for a legitimate user and setting the configuration-user to that
-;; user. In this way, when the job procedure is run on behalf of the
-;; configuration files, the jobs are registered with the system with the
-;; appropriate user. Note that only the root user should be able to perform this
-;; operation, but we leave it to the permissions on the /var/cron/tabs directory
-;; to enforce this.
-
-(use-modules (srfi srfi-2)) ;; For and-let*.
-
-(define (process-files-in-system-directory)
- (catch #t
- (lambda ()
- (let ((directory (opendir config-spool-dir)))
- (do ((file-name (readdir directory) (readdir directory)))
- ((eof-object? file-name))
- (and-let* ((user (valid-user file-name)))
- (set-configuration-user user) ;; / ?? !!!!
- (catch-mcron-error
- (read-vixie-file (string-append config-spool-dir
- "/"
- file-name)))))))
- (lambda (key . args)
- (mcron-error
- 4
- "You do not have permission to access the system crontabs."))))
-
-
-
-;; Having defined all the necessary procedures for scanning various sets of
-;; files, we perform the actual configuration of the program depending on the
-;; personality we are running as. If it is mcron, we either scan the files
-;; passed on the command line, or else all the ones in the user's .config/cron
-;; (or .cron) directory. If we are running under the cron personality, we read
-;; the /var/cron/tabs directory and also the /etc/crontab file.
-
-(case command-type
- ((mcron) (if (null? (option-ref options '() '()))
- (process-files-in-user-directory)
- (for-each (lambda (file-path)
- (process-user-file file-path #t))
- (option-ref options '() '()))))
-
- ((cron) (process-files-in-system-directory)
- (use-system-job-list)
- (catch-mcron-error
- (read-vixie-file "/etc/crontab" parse-system-vixie-line))
- (use-user-job-list)
- (if (not (option-ref options 'noetc #f))
- (begin
- (display
-"WARNING: cron will check for updates to /etc/crontab EVERY MINUTE. If you do\n
-not use this file, or you are prepared to manually restart cron whenever you\n
-make a change, then it is HIGHLY RECOMMENDED that you use the --noetc\n
-option.\n")
- (set-configuration-user "root")
- (job '(- (next-minute-from (next-minute)) 6)
- check-system-crontab
- "/etc/crontab update checker.")))))
-
-
-
-;; If the user has requested a schedule of jobs that will run, we provide the
-;; information here and then get out.
-;;
-;; Start by determining the number of time points in the future that output is
-;; required for. This may be provided on the command line as a parameter to the
-;; --schedule option, or else we assume a default of 8. Finally, ensure that the
-;; count is some positive integer.
-
-(and-let* ((count (option-ref options 'schedule #f)))
- (set! count (string->number count))
- (display (get-schedule (if (<= count 0) 1 count)))
- (quit))
-
-
-
-;; If we are supposed to run as a daemon process (either a --daemon option has
-;; been explicitly used, or we are running as cron or crond), detach from the
-;; terminal now. If we are running as cron, we can now write the PID file.
-
-(if (option-ref options 'daemon (eq? command-type 'cron))
- (begin
- (if (not (eqv? (primitive-fork) 0))
- (quit))
- (setsid)
- (if (eq? command-type 'cron)
- (with-output-to-file config-pid-file
- (lambda () (display (getpid)) (newline))))))
-
-
-
-;; If we are running as cron or crond, we establish a socket to listen for
-;; updates from a crontab program. This is put into fd-list so that we can
-;; inform the main wait-run-wait execution loop to listen for incoming messages
-;; on this socket.
-
-(define fd-list '())
-
-(if (eq? command-type 'cron)
- (catch #t
- (lambda ()
- (let ((socket (socket AF_UNIX SOCK_STREAM 0)))
- (bind socket AF_UNIX config-socket-file)
- (listen socket 5)
- (set! fd-list (list socket))))
- (lambda (key . args)
- (delete-file config-pid-file)
- (mcron-error 1
- "Cannot bind to UNIX socket "
- config-socket-file))))
-
-
-
-
-;; This function is called whenever a message comes in on the above socket. We
-;; read a user name from the socket, dealing with the "/etc/crontab" special
-;; case, remove all the user's jobs from the job list, and then re-read the
-;; user's updated file. In the special case we drop all the system jobs and
-;; re-read the /etc/crontab file.
-
-(define (process-update-request)
- (let* ((socket (car (accept (car fd-list))))
- (user-name (read-line socket)))
- (close socket)
- (set-configuration-time (current-time))
- (catch-mcron-error
- (if (string=? user-name "/etc/crontab")
- (begin
- (clear-system-jobs)
- (use-system-job-list)
- (read-vixie-file "/etc/crontab" parse-system-vixie-line)
- (use-user-job-list))
- (let ((user (getpw user-name)))
- (remove-user-jobs user)
- (set-configuration-user user)
- (read-vixie-file (string-append config-spool-dir "/" user-name)))))))
-
-
-
-;; Added by Sergey Poznyakoff. This no-op will collect zombie child processes
-;; as soon as they die. This is a big improvement as previously they stayed
-;; around the system until the next time mcron wakes to fire a new job off.
-
-;; Unfortunately it seems to interact badly with the select system call,
-;; wreaking havoc...
-
-;; (sigaction SIGCHLD (lambda (sig) noop) SA_RESTART)
-
-
-
-;; Now the main loop. Forever execute the run-job-loop procedure in the mcron
-;; core, and when it drops out (can only be because a message has come in on the
-;; socket) we process the socket request before restarting the loop again.
-;; Sergey Poznyakoff: we can also drop out of run-job-loop because of a SIGCHLD,
-;; so must test fd-list.
-
-(catch-mcron-error
- (while #t
- (run-job-loop fd-list)
- (if (not (null? fd-list))
- (process-update-request))))
diff --git a/scm/mcron/mcron-core.scm b/scm/mcron/mcron-core.scm
deleted file mode 100644
index 518bcac..0000000
--- a/scm/mcron/mcron-core.scm
+++ /dev/null
@@ -1,271 +0,0 @@
-;; Copyright (C) 2003 Dale Mellor
-;;
-;; This file is part of GNU mcron.
-;;
-;; GNU mcron 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.
-;;
-;; GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
-
-
-(define-module (mcron core)
- #:use-module (mcron environment)
- #:export (add-job
- remove-user-jobs
- get-schedule
- run-job-loop
- ;; These three are deprecated and not documented.
- use-system-job-list
- use-user-job-list
- clear-system-jobs)
- #:re-export (clear-environment-mods
- append-environment-mods))
-
-
-(use-modules (srfi srfi-1) ;; For remove.
- (srfi srfi-2)) ;; For and-let*.
-
-
-
-;; The list of all jobs known to the system. Each element of the list is
-;;
-;; (vector user next-time-function action environment displayable next-time)
-;;
-;; where action must be a procedure, and the environment is an alist of
-;; modifications that need making to the UNIX environment before the action is
-;; run. The next-time element is the only one that is modified during the
-;; running of a cron process (i.e. all the others are set once and for all at
-;; configuration time).
-;;
-;; The reason we maintain two lists is that jobs in /etc/crontab may be placed
-;; in one, and all other jobs go in the other. This makes it possible to remove
-;; all the jobs in the first list in one go, and separately we can remove all
-;; jobs from the second list which belong to a particular user. This behaviour
-;; is required for full vixie compatibility.
-
-(define system-job-list '())
-(define user-job-list '())
-
-(define configuration-source 'user)
-
-(define (use-system-job-list) (set! configuration-source 'system))
-(define (use-user-job-list) (set! configuration-source 'user))
-
-
-
-;; Convenience functions for getting and setting the elements of a job object.
-
-(define (job:user job) (vector-ref job 0))
-(define (job:next-time-function job) (vector-ref job 1))
-(define (job:action job) (vector-ref job 2))
-(define (job:environment job) (vector-ref job 3))
-(define (job:displayable job) (vector-ref job 4))
-(define (job:next-time job) (vector-ref job 5))
-
-
-
-;; Remove jobs from the user-job-list belonging to this user.
-
-(define (remove-user-jobs user)
- (if (or (string? user)
- (integer? user))
- (set! user (getpw user)))
- (set! user-job-list
- (remove (lambda (job) (eqv? (passwd:uid user)
- (passwd:uid (job:user job))))
- user-job-list)))
-
-
-
-;; Remove all the jobs on the system job list.
-
-(define (clear-system-jobs) (set! system-job-list '()))
-
-
-
-;; Add a new job with the given specifications to the head of the appropriate
-;; jobs list.
-
-(define (add-job time-proc action displayable configuration-time
- configuration-user)
- (let ((entry (vector configuration-user
- time-proc
- action
- (get-current-environment-mods-copy)
- displayable
- (time-proc configuration-time))))
- (if (eq? configuration-source 'user)
- (set! user-job-list (cons entry user-job-list))
- (set! system-job-list (cons entry system-job-list)))))
-
-
-
-;; Procedure to locate the jobs in the global job-list with the lowest
-;; (soonest) next-times. These are the jobs for which we must schedule the mcron
-;; program (under any personality) to next wake up. The return value is a cons
-;; cell consisting of the next time (maintained in the next-time variable) and a
-;; list of the job entries that are to run at this time (maintained in the
-;; next-jobs-list variable).
-;;
-;; The procedure works by first obtaining the time of the first job on the list,
-;; and setting this job in the next-jobs-list. Then for each other entry on the
-;; job-list, either the job runs earlier than any other that have been scanned,
-;; in which case the next-time and next-jobs-list are re-initialized to
-;; accomodate, or the job runs at the same time as the next job, in which case
-;; the next-jobs-list is simply augmented with the new job, or else the job runs
-;; later than others noted in which case we ignore it for now and continue to
-;; recurse the list.
-
-(define (find-next-jobs)
- (let ((job-list (append system-job-list user-job-list)))
-
- (if (null? job-list)
-
- '(#f . '())
-
- (let ((next-time 2000000000)
- (next-jobs-list '()))
-
- (for-each
- (lambda (job)
- (let ((this-time (job:next-time job)))
- (cond ((< this-time next-time)
- (set! next-time this-time)
- (set! next-jobs-list (list job)))
- ((eqv? this-time next-time)
- (set! next-jobs-list (cons job next-jobs-list))))))
- job-list)
-
- (cons next-time next-jobs-list)))))
-
-
-
-;; Create a string containing a textual list of the next count jobs to run.
-;;
-;; Enter a loop of displaying the next set of jobs to run, artificially
-;; forwarding the time to the next time point (instead of waiting for it to
-;; occur as we would do in a normal run of mcron), and recurse around the loop
-;; count times.
-;;
-;; Note that this has the effect of mutating the job timings. Thus the program
-;; must exit after calling this function; the internal data state will be left
-;; unusable.
-
-(define (get-schedule count)
- (with-output-to-string
- (lambda ()
- (do ((count count (- count 1)))
- ((eqv? count 0))
- (and-let* ((next-jobs (find-next-jobs))
- (time (car next-jobs))
- (date-string (strftime "%c %z\n" (localtime time))))
- (for-each (lambda (job)
- (display date-string)
- (display (job:displayable job))
- (newline)(newline)
- (vector-set! job
- 5
- ((job:next-time-function job)
- (job:next-time job))))
- (cdr next-jobs)))))))
-
-
-
-;; For proper housekeeping, it is necessary to keep a record of the number of
-;; child processes we fork off to run the jobs.
-
-(define number-children 0)
-
-
-
-;; For every job on the list, fork a process to run it (noting the fact by
-;; increasing the number-children counter), and in the new process set up the
-;; run-time environment exactly as it should be before running the job proper.
-;;
-;; In the parent, update the job entry by computing the next time the job needs
-;; to run.
-
-(define (run-jobs jobs-list)
- (for-each (lambda (job)
- (if (eqv? (primitive-fork) 0)
- (begin
- (setgid (passwd:gid (job:user job)))
- (setuid (passwd:uid (job:user job)))
- (chdir (passwd:dir (job:user job)))
- (modify-environment (job:environment job) (job:user job))
- ((job:action job))
- (primitive-exit 0))
- (begin
- (set! number-children (+ number-children 1))
- (vector-set! job
- 5
- ((job:next-time-function job)
- (current-time))))))
- jobs-list))
-
-
-
-;; Give any zombie children a chance to die, and decrease the number known to
-;; exist.
-
-(define (child-cleanup)
- (do () ((or (<= number-children 0)
- (eqv? (car (waitpid WAIT_ANY WNOHANG)) 0)))
- (set! number-children (- number-children 1))))
-
-
-
-;; Now the main loop. Loop over all job specifications, get a list of the next
-;; ones to run (may be more than one). Set an alarm and go to sleep. When we
-;; wake, run the jobs and reap any children (old jobs) that have
-;; completed. Repeat ad infinitum.
-;;
-;; Note that, if we wake ahead of time, it can only mean that a signal has been
-;; sent by a crontab job to tell us to re-read a crontab file. In this case we
-;; break out of the loop here, and let the main procedure deal with the
-;; situation (it will eventually re-call this function, thus maintaining the
-;; loop).
-
-(define (run-job-loop . fd-list)
-
- (call-with-current-continuation
- (lambda (break)
-
- (let ((fd-list (if (null? fd-list) '() (car fd-list))))
-
- (let loop ()
-
- (let* ((next-jobs (find-next-jobs))
- (next-time (car next-jobs))
- (next-jobs-list (cdr next-jobs))
- (sleep-time (if next-time (- next-time (current-time))
- 2000000000)))
-
- (and (> sleep-time 0)
- (if (not (null?
- (catch 'system-error
- (lambda ()
- (car (select fd-list '() '() sleep-time)))
- (lambda (key . args) ;; Exception add by Sergey
- ;; Poznyakoff.
- (if (member (car (last args))
- (list EINTR EAGAIN))
- (begin
- (child-cleanup) '())
- (apply throw key args))))))
- (break)))
-
- (run-jobs next-jobs-list)
-
- (child-cleanup)
-
- (loop)))))))
diff --git a/src/mcron/base.scm b/src/mcron/base.scm
new file mode 100644
index 0000000..c100b4f
--- /dev/null
+++ b/src/mcron/base.scm
@@ -0,0 +1,247 @@
+;;;; base.scm -- core procedures
+;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+;;; Copyright © 2016 Ludovic Courtès <ludo@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (mcron base)
+ #:use-module (ice-9 match)
+ #:use-module (mcron environment)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-2)
+ #:use-module (srfi srfi-9)
+ #:export (add-job
+ remove-user-jobs
+ get-schedule
+ run-job-loop
+ ;; Deprecated and undocumented procedures.
+ use-system-job-list
+ use-user-job-list
+ clear-system-jobs)
+ #:re-export (clear-environment-mods
+ append-environment-mods))
+
+;; The list of all jobs known to the system. Each element of the list is
+;;
+;; (make-job user next-time-function action environment displayable next-time)
+;;
+;; where action must be a procedure, and the environment is an alist of
+;; modifications that need making to the UNIX environment before the action is
+;; run. The next-time element is the only one that is modified during the
+;; running of a cron process (i.e. all the others are set once and for all at
+;; configuration time).
+;;
+;; The reason we maintain two lists is that jobs in /etc/crontab may be placed
+;; in one, and all other jobs go in the other. This makes it possible to remove
+;; all the jobs in the first list in one go, and separately we can remove all
+;; jobs from the second list which belong to a particular user. This behaviour
+;; is required for full vixie compatibility.
+
+(define system-job-list '())
+(define user-job-list '())
+
+(define configuration-source 'user)
+
+(define (use-system-job-list) (set! configuration-source 'system))
+(define (use-user-job-list) (set! configuration-source 'user))
+
+;; A cron job.
+(define-record-type <job>
+ (make-job user time-proc action environment displayable next-time)
+ job?
+ (user job:user) ;object : passwd entry
+ (time-proc job:next-time-function) ;proc : with one 'time' parameter
+ (action job:action) ;thunk : user's code
+ (environment job:environment) ;alist : environment variables
+ (displayable job:displayable) ;string : visible in schedule
+ (next-time job:next-time ;number : time in UNIX format
+ job:next-time-set!))
+
+;; Remove jobs from the user-job-list belonging to this user.
+
+(define (remove-user-jobs user)
+ (if (or (string? user)
+ (integer? user))
+ (set! user (getpw user)))
+ (set! user-job-list
+ (remove (lambda (job) (eqv? (passwd:uid user)
+ (passwd:uid (job:user job))))
+ user-job-list)))
+
+
+
+;; Remove all the jobs on the system job list.
+
+(define (clear-system-jobs) (set! system-job-list '()))
+
+
+
+;; Add a new job with the given specifications to the head of the appropriate
+;; jobs list.
+
+(define (add-job time-proc action displayable configuration-time
+ configuration-user)
+ (let ((entry (make-job configuration-user
+ time-proc
+ action
+ (get-current-environment-mods-copy)
+ displayable
+ (time-proc configuration-time))))
+ (if (eq? configuration-source 'user)
+ (set! user-job-list (cons entry user-job-list))
+ (set! system-job-list (cons entry system-job-list)))))
+
+(define (find-next-jobs)
+ "Procedure to locate the jobs in the global job-list with the
+lowest (soonest) next-times. These are the jobs for which we must schedule
+the mcron program (under any personality) to next wake up. The return value
+is a cons cell consisting of the next time (maintained in the next-time
+variable) and a list of the job entries that are to run at this
+time (maintained in the next-jobs-list variable).
+
+The procedure works by first obtaining the time of the first job on the list,
+and setting this job in the next-jobs-list. Then for each other entry on the
+job-list, either the job runs earlier than any other that have been scanned,
+in which case the next-time and next-jobs-list are re-initialized to
+accomodate, or the job runs at the same time as the next job, in which case
+the next-jobs-list is simply augmented with the new job, or else the job runs
+later than others noted in which case we ignore it for now and continue to
+recurse the list."
+ (let loop ((jobs (append system-job-list user-job-list))
+ (next-time (inf))
+ (next-jobs '()))
+ (match jobs
+ (()
+ (cons (and (finite? next-time) next-time) next-jobs))
+ ((job . rest)
+ (let ((this-time (job:next-time job)))
+ (cond ((< this-time next-time)
+ (loop rest this-time (list job)))
+ ((= this-time next-time)
+ (loop rest next-time (cons job next-jobs)))
+ (else
+ (loop rest next-time next-jobs))))))))
+
+;; Create a string containing a textual list of the next count jobs to run.
+;;
+;; Enter a loop of displaying the next set of jobs to run, artificially
+;; forwarding the time to the next time point (instead of waiting for it to
+;; occur as we would do in a normal run of mcron), and recurse around the loop
+;; count times.
+;;
+;; Note that this has the effect of mutating the job timings. Thus the program
+;; must exit after calling this function; the internal data state will be left
+;; unusable.
+
+(define (get-schedule count)
+ (with-output-to-string
+ (lambda ()
+ (do ((count count (- count 1)))
+ ((eqv? count 0))
+ (and-let*
+ ((next-jobs (find-next-jobs))
+ (time (car next-jobs))
+ (date-string (strftime "%c %z\n" (localtime time))))
+ (for-each (lambda (job)
+ (display date-string)
+ (display (job:displayable job))
+ (newline)(newline)
+ (job:next-time-set! job ((job:next-time-function job)
+ (job:next-time job))))
+ (cdr next-jobs)))))))
+
+
+
+;; For proper housekeeping, it is necessary to keep a record of the number of
+;; child processes we fork off to run the jobs.
+
+(define number-children 0)
+
+
+
+;; For every job on the list, fork a process to run it (noting the fact by
+;; increasing the number-children counter), and in the new process set up the
+;; run-time environment exactly as it should be before running the job proper.
+;;
+;; In the parent, update the job entry by computing the next time the job needs
+;; to run.
+
+(define (run-jobs jobs-list)
+ (for-each
+ (lambda (job)
+ (if (eqv? (primitive-fork) 0)
+ (dynamic-wind
+ (const #t)
+ (lambda ()
+ (setgid (passwd:gid (job:user job)))
+ (setuid (passwd:uid (job:user job)))
+ (chdir (passwd:dir (job:user job)))
+ (modify-environment (job:environment job) (job:user job))
+ ((job:action job)))
+ (lambda ()
+ (primitive-exit 0)))
+ (begin
+ (set! number-children (+ number-children 1))
+ (job:next-time-set! job ((job:next-time-function job)
+ (current-time))))))
+ jobs-list))
+
+
+
+;; Give any zombie children a chance to die, and decrease the number known to
+;; exist.
+
+(define (child-cleanup)
+ (do () ((or (<= number-children 0)
+ (eqv? (car (waitpid WAIT_ANY WNOHANG)) 0)))
+ (set! number-children (- number-children 1))))
+
+(define* (run-job-loop #:optional fd-list)
+ ;; Loop over all job specifications, get a list of the next ones to run (may
+ ;; be more than one). Set an alarm and go to sleep. When we wake, run the
+ ;; jobs and reap any children (old jobs) that have completed. Repeat ad
+ ;; infinitum.
+ ;;
+ ;; Note that, if we wake ahead of time, it can only mean that a signal has
+ ;; been sent by a crontab job to tell us to re-read a crontab file. In this
+ ;; case we break out of the loop here, and let the main procedure deal with
+ ;; the situation (it will eventually re-call this function, thus maintaining
+ ;; the loop).
+ (call-with-current-continuation
+ (lambda (break)
+ (let loop ()
+ (match (find-next-jobs)
+ ((next-time . next-jobs-lst)
+ (let ((sleep-time (if next-time
+ (- next-time (current-time))
+ 2000000000)))
+ (when (and
+ (> sleep-time 0)
+ (not (null? (catch 'system-error
+ (λ ()
+ (car (select fd-list '() '() sleep-time)))
+ (λ (key . args)
+ (let ((err (car (last args))))
+ (cond ((member err (list EINTR EAGAIN))
+ (child-cleanup)
+ '())
+ (else
+ (apply throw key args)))))))))
+ (break))
+ (run-jobs next-jobs-lst)
+ (child-cleanup)
+ (loop))))))))
diff --git a/src/mcron/config.scm.in b/src/mcron/config.scm.in
new file mode 100644
index 0000000..2b0bc7f
--- /dev/null
+++ b/src/mcron/config.scm.in
@@ -0,0 +1,35 @@
+;;;; config.scm -- variables defined at configure time
+;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (mcron config))
+
+(define-public config-debug @CONFIG_DEBUG@)
+(define-public config-package-name "@PACKAGE_NAME@")
+(define-public config-package-version "@PACKAGE_VERSION@")
+(define-public config-package-string "@PACKAGE_STRING@")
+(define-public config-package-bugreport "@PACKAGE_BUGREPORT@")
+(define-public config-package-url "@PACKAGE_URL@")
+(define-public config-sendmail "@SENDMAIL@")
+
+(define-public config-spool-dir "@CONFIG_SPOOL_DIR@")
+(define-public config-socket-file "@CONFIG_SOCKET_FILE@")
+(define-public config-allow-file "@CONFIG_ALLOW_FILE@")
+(define-public config-deny-file "@CONFIG_DENY_FILE@")
+(define-public config-pid-file "@CONFIG_PID_FILE@")
+(define-public config-tmp-dir "@CONFIG_TMP_DIR@")
diff --git a/src/mcron/environment.scm b/src/mcron/environment.scm
new file mode 100644
index 0000000..f6b9637
--- /dev/null
+++ b/src/mcron/environment.scm
@@ -0,0 +1,97 @@
+;;;; environment.scm -- interact with the job process environment
+;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+;;;; Commentary:
+;;;
+;;; Define the variable current-environment-mods, and the procedures
+;;; append-environment-mods (which is available to user configuration files),
+;;; clear-environment-mods and modify-environment. The idea is that the
+;;; current-environment-mods is a list of pairs of environment names and
+;;; values, and represents the cumulated environment settings in a
+;;; configuration file. When a job definition is seen in a configuration file,
+;;; the current-environment-mods are copied into the internal job description,
+;;; and when the job actually runs these environment modifications are applied
+;;; to the UNIX environment in which the job runs.
+;;;
+;;;; Code:
+
+(define-module (mcron environment)
+ #:export (modify-environment
+ clear-environment-mods
+ append-environment-mods
+ get-current-environment-mods-copy))
+
+
+
+;; As we parse configuration files, we build up an alist of environment
+;; variables here.
+
+(define current-environment-mods '())
+
+
+
+;; Each time a job is added to the system, we take a snapshot of the current
+;; set of environment modifiers.
+
+(define (get-current-environment-mods-copy)
+ (list-copy current-environment-mods))
+
+
+
+;; When we start to parse a new configuration file, we want to start with a
+;; fresh environment (actually an umodified version of the pervading mcron
+;; environment).
+
+(define (clear-environment-mods)
+ (set! current-environment-mods '()))
+
+
+
+;; Procedure to add another environment setting to the alist above. This is
+;; used both implicitly by the Vixie parser, and can be used directly by users
+;; in scheme configuration files. The return value is purely for the
+;; convenience of the parse-vixie-environment in the vixie-specification module
+;; (yuk).
+
+(define (append-environment-mods name value)
+ (set! current-environment-mods (append current-environment-mods
+ (list (cons name value))))
+ #t)
+
+(define (modify-environment env passwd-entry)
+ "Modify the environment (in the UNIX sense) by setting the variables from
+ENV and some default ones which are modulated by PASSWD-ENTRY. \"LOGNAME\"
+and \"USER\" environment variables can't be overided by ENV. ENV must be an
+alist which associate environment variables to their value. PASSWD-ENTRY must
+be an object representing user information which corresponds to a valid entry
+in /etc/passwd. The return value is not specified."
+ (for-each (lambda (pair) (setenv (car pair) (cdr pair)))
+ (let ((home-dir (passwd:dir passwd-entry))
+ (user-name (passwd:name passwd-entry)))
+ (append
+ ;; Default environment variables which can be overided by ENV.
+ `(("HOME" . ,home-dir)
+ ("CWD" . ,home-dir)
+ ("SHELL" . ,(passwd:shell passwd-entry))
+ ("TERM" . #f)
+ ("TERMCAP" . #f))
+ env
+ ;; Environment variables with imposed values.
+ `(("LOGNAME" . ,user-name)
+ ("USER" . ,user-name))))))
diff --git a/src/mcron/job-specifier.scm b/src/mcron/job-specifier.scm
new file mode 100644
index 0000000..d4c05bd
--- /dev/null
+++ b/src/mcron/job-specifier.scm
@@ -0,0 +1,256 @@
+;;;; job-specifier.scm -- public interface for defining jobs
+;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+;;;; Commentary:
+;;;
+;;; Define all the functions that can be used by scheme Mcron configuration
+;;; files, namely the procedures for working out next times, the job procedure
+;;; for registering new jobs (actually a wrapper around the base add-job
+;;; function), and the procedure for declaring environment modifications.
+;;;
+;;;; Code:
+
+(define-module (mcron job-specifier)
+ #:use-module (ice-9 match)
+ #:use-module (mcron base)
+ #:use-module (mcron environment)
+ #:use-module (mcron vixie-time)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
+ #:re-export (append-environment-mods)
+ #:export (range
+ next-year-from next-year
+ next-month-from next-month
+ next-day-from next-day
+ next-hour-from next-hour
+ next-minute-from next-minute
+ next-second-from next-second
+ set-configuration-user
+ set-configuration-time
+ job))
+
+(define* (range start end #:optional (step 1))
+ "Produces a list of values from START up to (but not including) END. An
+optional STEP may be supplied, and (if positive) only every step'th value will
+go into the list. For example, (range 1 6 2) returns '(1 3 5)."
+ (unfold (cut >= <> end) identity (cute + <> (max step 1)) start))
+
+(define (%find-best-next current next-list)
+ ;; Takes a value and a list of possible next values. It returns a pair
+ ;; consisting of the smallest element of the NEXT-LIST, and the smallest
+ ;; element larger than the CURRENT value. If an example of the latter
+ ;; cannot be found, +INF.0 will be returned.
+ (let loop ((smallest (inf)) (closest+ (inf)) (lst next-list))
+ (match lst
+ (() (cons smallest closest+))
+ ((time . rest)
+ (loop (min time smallest)
+ (if (> time current) (min time closest+) closest+)
+ rest)))))
+
+(define (bump-time time value-list component higher-component
+ set-component! set-higher-component!)
+ ;; Return the time corresponding to some near future hour. If hour-list is
+ ;; not supplied, the time returned corresponds to the start of the next hour
+ ;; of the day.
+ ;;
+ ;; If the hour-list is supplied the time returned corresponds to the first
+ ;; hour of the day in the future which is contained in the list. If all the
+ ;; values in the list are less than the current hour, then the time returned
+ ;; will correspond to the first hour in the list *on the following day*.
+ ;;
+ ;; ... except that the function is actually generalized to deal with
+ ;; seconds, minutes, etc., in an obvious way :-)
+ ;;
+ ;; Note that value-list always comes from an optional argument to a
+ ;; procedure, so is wrapped up as the first element of a list (i.e. it is a
+ ;; list inside a list).
+ (match value-list
+ (()
+ (set-component! time (1+ (component time))))
+ ((val . rest)
+ (match (%find-best-next (component time) val)
+ ((smallest . closest+)
+ (cond ((inf? closest+)
+ (set-higher-component! time (1+ (higher-component time)))
+ (set-component! time smallest))
+ (else
+ (set-component! time closest+)))))))
+ (first (mktime time)))
+
+;; Set of configuration methods which use the above general function to bump
+;; specific components of time to the next legitimate value. In each case, all
+;; the components smaller than that of interest are taken to zero, so that for
+;; example the time of the next year will be the time at which the next year
+;; actually starts.
+
+(define* (next-year-from current-time #:optional (year-list '()))
+ (let ((time (localtime current-time)))
+ (set-tm:mon time 0)
+ (set-tm:mday time 1)
+ (set-tm:hour time 0)
+ (set-tm:min time 0)
+ (set-tm:sec time 0)
+ (bump-time time year-list tm:year tm:year set-tm:year set-tm:year)))
+
+(define* (next-month-from current-time #:optional (month-list '()))
+ (let ((time (localtime current-time)))
+ (set-tm:mday time 1)
+ (set-tm:hour time 0)
+ (set-tm:min time 0)
+ (set-tm:sec time 0)
+ (bump-time time month-list tm:mon tm:year set-tm:mon set-tm:year)))
+
+(define* (next-day-from current-time #:optional (day-list '()))
+ (let ((time (localtime current-time)))
+ (set-tm:hour time 0)
+ (set-tm:min time 0)
+ (set-tm:sec time 0)
+ (bump-time time day-list tm:mday tm:mon set-tm:mday set-tm:mon)))
+
+(define* (next-hour-from current-time #:optional (hour-list '()))
+ (let ((time (localtime current-time)))
+ (set-tm:min time 0)
+ (set-tm:sec time 0)
+ (bump-time time hour-list tm:hour tm:mday set-tm:hour set-tm:mday)))
+
+(define* (next-minute-from current-time #:optional (minute-list '()))
+ (let ((time (localtime current-time)))
+ (set-tm:sec time 0)
+ (bump-time time minute-list tm:min tm:hour set-tm:min set-tm:hour)))
+
+(define* (next-second-from current-time #:optional (second-list '()))
+ (let ((time (localtime current-time)))
+ (bump-time time second-list tm:sec tm:min set-tm:sec set-tm:min)))
+
+;;; The following procedures are convenient for configuration files. They are
+;;; wrappers for the next-X-from functions above, by implicitly using
+;;; %CURRENT-ACTION-TIME as the time argument.
+
+(define %current-action-time
+ ;; The time a job was last run, the time from which the next time to run a
+ ;; job must be computed. (When the program is first run, this time is set to
+ ;; the configuration time so that jobs run from that moment forwards.) Once
+ ;; we have this, we supply versions of the time computation commands above
+ ;; which implicitly assume this value.
+ (make-parameter 0))
+
+(define* (next-year #:optional (args '()))
+ "Compute the next year from %CURRENT-ACTION-TIME parameter object."
+ (next-year-from (%current-action-time) args))
+
+(define* (next-month #:optional (args '()))
+ "Compute the next month from %CURRENT-ACTION-TIME parameter object."
+ (next-month-from (%current-action-time) args))
+
+(define* (next-day #:optional (args '()))
+ "Compute the next day from %CURRENT-ACTION-TIME parameter object."
+ (next-day-from (%current-action-time) args))
+
+(define* (next-hour #:optional (args '()))
+ "Compute the next hour from %CURRENT-ACTION-TIME parameter object."
+ (next-hour-from (%current-action-time) args))
+
+(define* (next-minute #:optional (args '()))
+ "Compute the next minute from %CURRENT-ACTION-TIME parameter object."
+ (next-minute-from (%current-action-time) args))
+
+(define* (next-second #:optional (args '()))
+ "Compute the next second from %CURRENT-ACTION-TIME parameter object."
+ (next-second-from (%current-action-time) args))
+
+;; The default user for running jobs is the current one (who invoked this
+;; program). There are exceptions: when cron parses /etc/crontab the user is
+;; specified on each individual line; when cron parses /var/cron/tabs/* the user
+;; is derived from the filename of the crontab. These cases are dealt with by
+;; mutating this variable. Note that the variable is only used at configuration
+;; time; a UID is stored with each job and it is that which takes effect when
+;; the job actually runs.
+
+(define configuration-user (getpw (getuid)))
+(define configuration-time (current-time))
+
+(define (set-configuration-user user)
+ (set! configuration-user (if (or (string? user)
+ (integer? user))
+ (getpw user)
+ user)))
+(define (set-configuration-time time) (set! configuration-time time))
+
+
+
+;; The job function, available to configuration files for adding a job rule to
+;; the system.
+;;
+;; Here we must 'normalize' the next-time-function so that it is always a lambda
+;; function which takes one argument (the last time the job ran) and returns a
+;; single value (the next time the job should run). If the input value is a
+;; string this is parsed as a Vixie-style time specification, and if it is a
+;; list then we arrange to eval it (but note that such lists are expected to
+;; ignore the function parameter - the last run time is always read from the
+;; %CURRENT-ACTION-TIME parameter object). A similar normalization is applied to
+;; the action.
+;;
+;; Here we also compute the first time that the job is supposed to run, by
+;; finding the next legitimate time from the current configuration time (set
+;; right at the top of this program).
+
+(define* (job time-proc action #:optional displayable
+ #:key (user configuration-user))
+ (let ((action (cond ((procedure? action) action)
+ ((list? action) (lambda () (primitive-eval action)))
+ ((string? action) (lambda () (system action)))
+ (else
+ (throw 'mcron-error 2
+ "job: invalid second argument (action; should be lambda "
+ "function, string or list)"))))
+
+ (time-proc
+ (cond ((procedure? time-proc) time-proc)
+ ((string? time-proc) (parse-vixie-time time-proc))
+ ((list? time-proc) (lambda (current-time)
+ (primitive-eval time-proc)))
+ (else
+ (throw 'mcron-error 3
+ "job: invalid first argument (next-time-function; "
+ "should be function, string or list)"))))
+ (displayable
+ (cond (displayable displayable)
+ ((procedure? action) "Lambda function")
+ ((string? action) action)
+ ((list? action) (with-output-to-string
+ (lambda () (display action))))))
+ (user* (if (or (string? user) (integer? user))
+ (getpw user)
+ user)))
+ (add-job (lambda (current-time)
+ (parameterize ((%current-action-time current-time))
+ ;; Allow for daylight savings time changes.
+ (let* ((next (time-proc current-time))
+ (gmtoff (tm:gmtoff (localtime next)))
+ (d (+ next
+ (- gmtoff
+ (tm:gmtoff (localtime current-time))))))
+ (if (eqv? (tm:gmtoff (localtime d)) gmtoff)
+ d
+ next))))
+ action
+ displayable
+ configuration-time
+ user*)))
diff --git a/scm/mcron/redirect.scm b/src/mcron/redirect.scm
index 312b768..6711407 100644
--- a/scm/mcron/redirect.scm
+++ b/src/mcron/redirect.scm
@@ -1,40 +1,43 @@
-;; Copyright (C) 2003 Dale Mellor
-;;
-;; This file is part of GNU mcron.
-;;
-;; GNU mcron 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.
-;;
-;; GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
-
-
-;; This module provides the (with-mail-out action . user) procedure. This
-;; procedure runs the action in a child process, allowing the user control over
-;; the input and output (including standard error). The input is governed (only
-;; in the case of a string action) by the placing of percentage signs in the
-;; string; the first delimits the true action from the standard input, and
-;; subsequent ones denote newlines to be placed into the input. The output (if
-;; there actually is any) is controlled by the MAILTO environment variable. If
-;; this is not defined, output is e-mailed to the user passed as argument, if
-;; any, or else the owner of the action; if defined but empty then any output is
-;; sunk to /dev/null; otherwise output is e-mailed to the address held in the
-;; MAILTO variable.
+;;;; redirect.scm -- modify job outputs
+;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+;;;; Commentary:
+;;;
+;;; Provide the (with-mail-out action . user) procedure. This procedure runs
+;;; the action in a child process, allowing the user control over the input
+;;; and output (including standard error). The input is governed (only in the
+;;; case of a string action) by the placing of percentage signs in the string;
+;;; the first delimits the true action from the standard input, and subsequent
+;;; ones denote newlines to be placed into the input. The output (if there
+;;; actually is any) is controlled by the MAILTO environment variable. If
+;;; this is not defined, output is e-mailed to the user passed as argument, if
+;;; any, or else the owner of the action; if defined but empty then any output
+;;; is sunk to /dev/null; otherwise output is e-mailed to the address held in
+;;; the MAILTO variable.
+;;;
+;;;; Code:
(define-module (mcron redirect)
- #:export (with-mail-out)
- #:use-module ((mcron config) :select (config-sendmail))
- #:use-module (mcron vixie-time))
-
-
+ #:use-module (ice-9 popen)
+ #:use-module (ice-9 regex)
+ #:use-module (mcron config)
+ #:use-module (mcron vixie-time)
+ #:export (with-mail-out))
;; An action string consists of a sequence of characters forming a command
;; executable by the shell, possibly followed by an non-escaped percentage
@@ -59,8 +62,6 @@
;; the string, and output (including the error output) being sent to a pipe
;; opened on a mail transport.
-(use-modules (ice-9 popen))
-
(define (with-mail-out action . user)
;; Determine the name of the user who is to recieve the mail, looking for a
diff --git a/src/mcron/scripts/cron.scm b/src/mcron/scripts/cron.scm
new file mode 100644
index 0000000..d043d79
--- /dev/null
+++ b/src/mcron/scripts/cron.scm
@@ -0,0 +1,177 @@
+;;;; cron -- daemon for running jobs at scheduled times
+;;; Copyright © 2003, 2012 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (mcron scripts cron)
+ #:use-module (mcron base)
+ #:use-module (mcron config)
+ #:use-module (mcron job-specifier)
+ #:use-module (mcron utils)
+ #:use-module (mcron vixie-specification)
+ #:use-module (srfi srfi-2)
+ #:export (main))
+
+(define (show-help)
+ (display "Usage: cron [OPTIONS]
+Unless an option is specified, run a cron daemon as a detached process,
+reading all the information in the users' crontabs and in /etc/crontab.
+
+ -v, --version Display version
+ -h, --help Display this help message
+ -sN, --schedule[=]N Display the next N jobs that will be run by cron
+ -n, --noetc Do not check /etc/crontab for updates (HIGHLY
+ RECOMMENDED).")
+ (newline)
+ (show-package-information))
+
+(define %options
+ `((schedule (single-char #\s) (value #t)
+ (predicate ,(λ (str) (string->number str))))
+ (noetc (single-char #\n) (value #f))
+ (version (single-char #\v) (value #f))
+ (help (single-char #\h) (value #f))))
+
+(define (delete-run-file)
+ "Remove the /var/run/cron.pid file so that crontab and other invocations of
+cron don't get the wrong idea that a daemon is currently running. This
+procedure is called from the C front-end whenever a terminal signal is
+received."
+ (catch #t
+ (λ ()
+ (delete-file config-pid-file)
+ (delete-file config-socket-file))
+ noop)
+ (quit))
+
+(define (cron-file-descriptors)
+ "Establish a socket to listen for updates from a crontab program, and return
+a list containing the file descriptors correponding to the files read by
+crontab. This requires that command-type is 'cron."
+ (catch #t
+ (λ ()
+ (let ((sock (socket AF_UNIX SOCK_STREAM 0)))
+ (bind sock AF_UNIX config-socket-file)
+ (listen sock 5)
+ (list sock)))
+ (λ (key . args)
+ (delete-file config-pid-file)
+ (mcron-error 1 "Cannot bind to UNIX socket " config-socket-file))))
+
+(define (process-files-in-system-directory)
+ "Process all the files in the crontab directory. When the job procedure is
+run on behalf of the configuration files, the jobs are registered on the
+system with the appropriate user. Only root should be able to perform this
+operation. The permissions on the /var/cron/tabs directory enforce this."
+
+ (define (user-entry name)
+ ;; Return the user database entry if NAME is valid, otherwise #f.
+ (false-if-exception (getpwnam name)))
+
+ (catch #t
+ (λ ()
+ (for-each-file
+ (λ (user)
+ (and-let* ((entry (user-entry user))) ;crontab without user?
+ (set-configuration-user entry)
+ (catch-mcron-error
+ (read-vixie-file (string-append config-spool-dir "/" user)))))
+ config-spool-dir))
+ (λ (key . args)
+ (mcron-error 4
+ "You do not have permission to access the system crontabs."))))
+
+(define (%process-files schedule? noetc?)
+ ;; XXX: What is this supposed to do?
+ (when schedule?
+ (with-output-to-file config-pid-file noop))
+ ;; Clear MAILTO so that outputs are sent to the various users.
+ (setenv "MAILTO" #f)
+ ;; XXX: At compile time, this yields a "possibly unbound variable" warning,
+ ;; but this is OK since it is bound in the C wrapper.
+ (c-set-cron-signals)
+ ;; Having defined all the necessary procedures for scanning various sets of
+ ;; files, we perform the actual configuration of the program depending on
+ ;; the personality we are running as. If it is mcron, we either scan the
+ ;; files passed on the command line, or else all the ones in the user's
+ ;; .config/cron (or .cron) directory. If we are running under the cron
+ ;; personality, we read the /var/cron/tabs directory and also the
+ ;; /etc/crontab file.
+ (process-files-in-system-directory)
+ (use-system-job-list)
+ (catch-mcron-error
+ (read-vixie-file "/etc/crontab" parse-system-vixie-line))
+ (use-user-job-list)
+ (unless noetc?
+ (display "\
+WARNING: cron will check for updates to /etc/crontab EVERY MINUTE. If you do
+not use this file, or you are prepared to manually restart cron whenever you
+make a change, then it is HIGHLY RECOMMENDED that you use the --noetc
+option.\n")
+ (set-configuration-user "root")
+ (job '(- (next-minute-from (next-minute)) 6)
+ check-system-crontab
+ "/etc/crontab update checker.")))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define* (main #:optional (args (command-line)))
+ (let ((opts (parse-args args %options)))
+ (when config-debug
+ (debug-enable 'backtrace))
+ (cond
+ ((option-ref opts 'help #f)
+ (show-help)
+ (exit 0))
+ ((option-ref opts 'version #f)
+ (show-version "cron")
+ (exit 0))
+ ((not (zero? (getuid)))
+ (mcron-error 16
+ "This program must be run by the root user (and should"
+ " have been installed as such)."))
+ ((access? config-pid-file F_OK)
+ (mcron-error 1
+ "A cron daemon is already running.\n (If you are sure"
+ " this is not true, remove the file\n "
+ config-pid-file ".)"))
+ (else
+ (%process-files (option-ref opts 'schedule #f)
+ (option-ref opts 'noetc #f))
+ (cond ((option-ref opts 'schedule #f) ;display jobs schedule
+ => (λ (count)
+ (display (get-schedule (max 1 (string->number count))))
+ (exit 0)))
+ (else (case (primitive-fork) ;run the daemon
+ ((0)
+ (setsid)
+ ;; we can now write the PID file.
+ (with-output-to-file config-pid-file
+ (λ () (display (getpid)) (newline))))
+ (else (exit 0)))))
+ ;; Forever execute the 'run-job-loop', and when it drops out (can
+ ;; only be because a message has come in on the socket) we
+ ;; process the socket request before restarting the loop again.
+ (catch-mcron-error
+ (let ((fdes-list (cron-file-descriptors)))
+ (while #t
+ (run-job-loop fdes-list)
+ (unless (null? fdes-list)
+ (process-update-request fdes-list)))))))))
diff --git a/src/mcron/scripts/crontab.scm b/src/mcron/scripts/crontab.scm
new file mode 100644
index 0000000..502fec6
--- /dev/null
+++ b/src/mcron/scripts/crontab.scm
@@ -0,0 +1,225 @@
+;;;; crontab -- edit user's cron tabs
+;;; Copyright © 2003, 2004 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (mcron scripts crontab)
+ #:use-module (ice-9 rdelim)
+ #:use-module (mcron config)
+ #:use-module (mcron utils)
+ #:use-module (mcron vixie-specification)
+ #:export (main))
+
+(define* (show-help)
+ (display "Usage: crontab [-u user] file
+ crontab [-u user] { -e | -l | -r }
+ (default operation is replace, per 1003.2)
+ -e (edit user's crontab)
+ -l (list user's crontab)
+ -r (delete user's crontab")
+ (newline)
+ (show-package-information))
+
+(define %options
+ '((user (single-char #\u) (value #t))
+ (edit (single-char #\e) (value #f))
+ (list (single-char #\l) (value #f))
+ (remove (single-char #\r) (value #f))
+ (version (single-char #\v) (value #f))
+ (help (single-char #\h) (value #f))))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define* (main #:optional (args (command-line)))
+ (let ((opts (parse-args args %options)))
+ (when config-debug
+ (debug-enable 'backtrace))
+ (cond ((option-ref opts 'help #f)
+ (show-help)
+ (exit 0))
+ ((option-ref opts 'version #f)
+ (show-version "crontab")
+ (exit 0)))
+ (let ((hit-server
+ (λ (user-name)
+ ;; Procedure to communicate with running cron daemon that a user
+ ;; has modified his crontab. The user name is written to the
+ ;; /var/cron/socket UNIX socket.
+ (catch #t
+ (λ ()
+ (let ((socket (socket AF_UNIX SOCK_STREAM 0)))
+ (connect socket AF_UNIX config-socket-file)
+ (display user-name socket)
+ (close socket)))
+ (λ (key . args)
+ (display "Warning: a cron daemon is not running.\n")))))
+
+ ;; Procedure to scan a file containing one user name per line (such
+ ;; as /var/cron/allow and /var/cron/deny), and determine if the
+ ;; given name is in there. The procedure returns #t, #f, or '() if
+ ;; the file does not exist.
+ (in-access-file?
+ (λ (file name)
+ (catch #t
+ (λ ()
+ (with-input-from-file file
+ (λ ()
+ (let loop ((input (read-line)))
+ (if (eof-object? input)
+ #f
+ (if (string=? input name)
+ #t
+ (loop (read-line))))))))
+ (λ (key . args) '()))))
+
+ ;; This program should have been installed SUID root. Here we get
+ ;; the passwd entry for the real user who is running this program.
+ (crontab-real-user (passwd:name (getpw (getuid)))))
+
+ ;; If the real user is not allowed to use crontab due to the
+ ;; /var/cron/allow and/or /var/cron/deny files, bomb out now.
+ (if (or (eq? (in-access-file? config-allow-file crontab-real-user) #f)
+ (eq? (in-access-file? config-deny-file crontab-real-user) #t))
+ (mcron-error 6 "Access denied by system operator."))
+
+ ;; Check that no more than one of the mutually exclusive options are
+ ;; being used.
+ (when (> (+ (if (option-ref opts 'edit #f) 1 0)
+ (if (option-ref opts 'list #f) 1 0)
+ (if (option-ref opts 'remove #f) 1 0))
+ 1)
+ (mcron-error 7 "Only one of options -e, -l or -r can be used."))
+
+ ;; Check that a non-root user is trying to read someone else's files.
+ (when (and (not (zero? (getuid)))
+ (option-ref opts 'user #f))
+ (mcron-error 8 "Only root can use the -u option."))
+
+ (letrec* (;; Iff the --user option is given, the crontab-user may be
+ ;; different from the real user.
+ (crontab-user (option-ref opts 'user crontab-real-user))
+ ;; So now we know which crontab file we will be manipulating.
+ (crontab-file (string-append config-spool-dir "/" crontab-user))
+ ;; Display the prompt and wait for user to type his
+ ;; choice. Return #t if the answer begins with 'y' or 'Y',
+ ;; return #f if it begins with 'n' or 'N', otherwise ask
+ ;; again.
+ (get-yes-no (λ (prompt . re-prompt)
+ (if (not (null? re-prompt))
+ (display "Please answer y or n.\n"))
+ (display (string-append prompt " "))
+ (let ((r (read-line)))
+ (if (not (string-null? r))
+ (case (string-ref r 0)
+ ((#\y #\Y) #t)
+ ((#\n #\N) #f)
+ (else (get-yes-no prompt #t)))
+ (get-yes-no prompt #t))))))
+ ;; There are four possible sub-personalities to the crontab
+ ;; personality: list, remove, edit and replace (when the user uses no
+ ;; options but supplies file names on the command line).
+ (cond
+ ;; In the list personality, we simply open the crontab and copy it
+ ;; character-by-character to the standard output. If anything goes
+ ;; wrong, it can only mean that this user does not have a crontab
+ ;; file.
+ ((option-ref opts 'list #f)
+ (catch #t
+ (λ ()
+ (with-input-from-file crontab-file
+ (λ ()
+ (do ((input (read-char) (read-char)))
+ ((eof-object? input))
+ (display input)))))
+ (λ (key . args)
+ (display (string-append "No crontab for "
+ crontab-user
+ " exists.\n")))))
+
+ ;; In the edit personality, we determine the name of a temporary file
+ ;; and an editor command, copy an existing crontab file (if it is
+ ;; there) to the temporary file, making sure the ownership is set so
+ ;; the real user can edit it; once the editor returns we try to read
+ ;; the file to check that it is parseable (but do nothing more with
+ ;; the configuration), and if it is okay (this program is still
+ ;; running!) we move the temporary file to the real crontab, wake the
+ ;; cron daemon up, and remove the temporary file. If the parse fails,
+ ;; we give user a choice of editing the file again or quitting the
+ ;; program and losing all changes made.
+ ((option-ref opts 'edit #f)
+ (let ((temp-file (string-append config-tmp-dir
+ "/crontab."
+ (number->string (getpid)))))
+ (catch #t
+ (λ () (copy-file crontab-file temp-file))
+ (λ (key . args) (with-output-to-file temp-file noop)))
+ (chown temp-file (getuid) (getgid))
+ (let retry ()
+ (system (string-append
+ (or (getenv "VISUAL") (getenv "EDITOR") "vi")
+ " "
+ temp-file))
+ (catch 'mcron-error
+ (λ () (read-vixie-file temp-file))
+ (λ (key exit-code . msg)
+ (apply mcron-error 0 msg)
+ (if (get-yes-no "Edit again?")
+ (retry)
+ (begin
+ (mcron-error 0 "Crontab not changed")
+ (primitive-exit 0))))))
+ (copy-file temp-file crontab-file)
+ (delete-file temp-file)
+ (hit-server crontab-user)))
+
+ ;; In the remove personality we simply make an effort to delete the
+ ;; crontab and wake the daemon. No worries if this fails.
+ ((option-ref opts 'remove #f)
+ (catch #t
+ (λ ()
+ (delete-file crontab-file)
+ (hit-server crontab-user))
+ noop))
+
+ ;; XXX: This comment is wrong.
+ ;; In the case of the replace personality we loop over all the
+ ;; arguments on the command line, and for each one parse the file to
+ ;; make sure it is parseable (but subsequently ignore the
+ ;; configuration), and all being well we copy it to the crontab
+ ;; location; we deal with the standard input in the same way but
+ ;; different. :-) In either case the server is woken so that it will
+ ;; read the newly installed crontab.
+ ((not (null? (option-ref opts '() '())))
+ (let ((input-file (car (option-ref opts '() '()))))
+ (catch-mcron-error
+ (if (string=? input-file "-")
+ (let ((input-string (read-string)))
+ (read-vixie-port (open-input-string input-string))
+ (with-output-to-file crontab-file
+ (λ () (display input-string))))
+ (begin
+ (read-vixie-file input-file)
+ (copy-file input-file crontab-file))))
+ (hit-server crontab-user)))
+
+ ;; The user is being silly. The message here is identical to the one
+ ;; Vixie cron used to put out, for total compatibility.
+ (else (mcron-error 15
+ "usage error: file name must be specified for replace.")))))))
diff --git a/src/mcron/scripts/mcron.scm b/src/mcron/scripts/mcron.scm
new file mode 100644
index 0000000..b6c7729
--- /dev/null
+++ b/src/mcron/scripts/mcron.scm
@@ -0,0 +1,136 @@
+;;;; mcron -- run jobs at scheduled times
+;;; Copyright © 2003, 2012 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (mcron scripts mcron)
+ #:use-module (mcron base)
+ #:use-module (mcron config)
+ #:use-module (mcron job-specifier) ;for user/system files
+ #:use-module (mcron utils)
+ #:use-module (mcron vixie-specification)
+ #:export (main))
+
+(define (show-help)
+ (display "Usage: mcron [OPTIONS] [FILES]
+Run an mcron process according to the specifications in the FILES (`-' for
+standard input), or use all the files in ~/.config/cron (or the
+deprecated ~/.cron) with .guile or .vixie extensions.
+
+ -v, --version Display version
+ -h, --help Display this help message
+ -sN, --schedule[=]N Display the next N jobs that will be run by mcron
+ -d, --daemon Immediately detach the program from the terminal
+ and run as a daemon process
+ -i, --stdin=(guile|vixie) Format of data passed as standard input or
+ file arguments (default guile)")
+ (newline)
+ (show-package-information))
+
+(define %options
+ `((schedule (single-char #\s) (value #t)
+ (predicate ,(λ (str) (string->number str))))
+ (daemon (single-char #\d) (value #f))
+ (noetc (single-char #\n) (value #f))
+ (stdin (single-char #\i) (value #t)
+ (predicate ,(λ (val)
+ (or (string=? val "guile")
+ (string=? val "vixie")))))
+ (version (single-char #\v) (value #f))
+ (help (single-char #\h) (value #f))))
+
+(define process-user-file
+ (let ((guile-regexp (make-regexp "\\.gui(le)?$"))
+ (vixie-regexp (make-regexp "\\.vix(ie)?$")))
+ (lambda* (file-name #:optional guile-syntax? #:key (input "guile"))
+ "Process FILE-NAME according its extension. When GUILE-SYNTAX? is TRUE,
+force guile syntax usage. If FILE-NAME format is not recognized, it is
+silently ignored."
+ (cond ((string=? "-" file-name)
+ (if (string=? input "vixie")
+ (read-vixie-port (current-input-port))
+ (eval-string (read-string))))
+ ((or guile-syntax? (regexp-exec guile-regexp file-name))
+ (load file-name))
+ ((regexp-exec vixie-regexp file-name)
+ (read-vixie-file file-name))))))
+
+(define (process-files-in-user-directory input-type)
+ "Process files in $XDG_CONFIG_HOME/cron and/or ~/.cron directories (if
+$XDG_CONFIG_HOME is not defined uses ~/.config/cron instead)."
+ (let ((errors 0)
+ (home-directory (passwd:dir (getpw (getuid)))))
+ (map (λ (dir)
+ (catch #t
+ (λ ()
+ (for-each-file
+ (λ (file)
+ (process-user-file (string-append dir "/" file)
+ #:input input-type))
+ dir))
+ (λ (key . args)
+ (set! errors (1+ errors)))))
+ (list (string-append home-directory "/.cron")
+ (string-append (or (getenv "XDG_CONFIG_HOME")
+ (string-append home-directory "/.config"))
+ "/cron")))
+ (when (eq? 2 errors)
+ (mcron-error 13
+ "Cannot read files in your ~/.config/cron (or ~/.cron) directory."))))
+
+(define (%process-files files input-type)
+ (if (null? files)
+ (process-files-in-user-directory input-type)
+ (for-each (λ (file) (process-user-file file #t)) files)))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define* (main #:optional (args (command-line)))
+ (let ((opts (parse-args args %options)))
+ (when config-debug
+ (debug-enable 'backtrace))
+ (cond ((option-ref opts 'help #f)
+ (show-help)
+ (exit 0))
+ ((option-ref opts 'version #f)
+ (show-version "mcron")
+ (exit 0))
+ (else
+ (%process-files (option-ref opts '() '())
+ (option-ref opts 'stdin "guile"))
+ (cond ((option-ref opts 'schedule #f) ;display jobs schedule
+ => (λ (count)
+ (display (get-schedule (max 1 (string->number count))))
+ (exit 0)))
+ ((option-ref opts 'daemon #f) ;run mcron as a daemon
+ (case (primitive-fork)
+ ((0) (setsid))
+ (else (exit 0)))))
+ ;; Forever execute the 'run-job-loop', and when it drops out (can
+ ;; only be because a message has come in on the socket) we process
+ ;; the socket request before restarting the loop again.
+ (catch-mcron-error
+ (let ((fdes-list '()))
+ (while #t
+ (run-job-loop fdes-list)
+ ;; we can also drop out of run-job-loop because of a SIGCHLD,
+ ;; so must test FDES-LIST.
+ (unless (null? fdes-list)
+ (process-update-request fdes-list)))))))))
diff --git a/src/mcron/utils.scm b/src/mcron/utils.scm
new file mode 100644
index 0000000..062e756
--- /dev/null
+++ b/src/mcron/utils.scm
@@ -0,0 +1,116 @@
+;;;; utils.scm -- helper procedures
+;;; Copyright © 2003, 2012 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (mcron utils)
+ #:use-module (ice-9 getopt-long)
+ #:use-module (ice-9 rdelim)
+ #:use-module (mcron config)
+ #:use-module (mcron base)
+ #:use-module (mcron job-specifier)
+ #:use-module (mcron vixie-specification)
+ #:export (catch-mcron-error
+ mcron-error
+ parse-args
+ show-version
+ show-package-information
+ for-each-file
+ process-update-request)
+ #:re-export (option-ref
+ read-string))
+
+(define (mcron-error exit-code . rest)
+ "Print an error message (made up from the parts of REST), and if the
+EXIT-CODE error is fatal (present and non-zero) then exit to the system with
+EXIT-CODE."
+ (with-output-to-port (current-error-port)
+ (lambda ()
+ (for-each display (cons "mcron: " rest))
+ (newline)))
+ (when (and exit-code (not (eq? exit-code 0)))
+ (primitive-exit exit-code)))
+
+(define-syntax-rule (catch-mcron-error exp ...)
+ "Evaluate EXP .... if an 'mcron-error exception occurs, print its diagnostics
+and exit with its error code."
+ (catch 'mcron-error
+ (lambda () exp ...)
+ (lambda (key exit-code . msg)
+ (apply mcron-error exit-code msg))))
+
+(define (parse-args args option-desc-list)
+ "Parse ARGS with OPTION-DESC-LIST specification."
+ (catch 'misc-error
+ (lambda () (getopt-long args option-desc-list))
+ (lambda (key func fmt args . rest)
+ (mcron-error 1 (apply format (append (list #f fmt) args))))))
+
+(define (show-version command)
+ "Display version information for COMMAND and quit."
+ (let* ((name config-package-name)
+ (short-name (cadr (string-split name #\space)))
+ (version config-package-version))
+ (simple-format #t "~a (~a) ~a
+Copyright (C) 2015 the ~a authors.
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.\n"
+ command name version short-name)))
+
+(define (show-package-information)
+ "Display where to get help and send bug reports."
+ (simple-format #t "\nReport bugs to: ~a.
+~a home page: <~a>
+General help using GNU software: <http://www.gnu.org/gethelp/>\n"
+ config-package-bugreport
+ config-package-name
+ config-package-url))
+
+(define (for-each-file proc directory)
+ "Apply PROC to each file in DIRECTORY. DIRECTORY must be a valid directory name.
+PROC must be a procedure that take one file name argument. The return value
+is not specified"
+ (let ((dir (opendir directory)))
+ (let loop ((file-name (readdir dir)))
+ (if (eof-object? file-name)
+ (closedir dir)
+ (begin
+ (proc file-name)
+ (loop (readdir dir)))))))
+
+(define (process-update-request fdes-list)
+ "Read a user name from the socket, dealing with the /etc/crontab special
+case, remove all the user's jobs from the job list, and then re-read the
+user's updated file. In the special case drop all the system jobs and re-read
+the /etc/crontab file. This function should be called whenever a message
+comes in on the above socket."
+ (let* ((sock (car (accept (car fdes-list))))
+ (user-name (read-line sock)))
+ (close sock)
+ (set-configuration-time (current-time))
+ (catch-mcron-error
+ (if (string=? user-name "/etc/crontab")
+ (begin
+ (clear-system-jobs)
+ (use-system-job-list)
+ (read-vixie-file "/etc/crontab" parse-system-vixie-line)
+ (use-user-job-list))
+ (let ((user (getpw user-name)))
+ (remove-user-jobs user)
+ (set-configuration-user user)
+ (read-vixie-file (string-append config-spool-dir "/" user-name)))))))
diff --git a/scm/mcron/vixie-specification.scm b/src/mcron/vixie-specification.scm
index ab002ba..cf2679a 100644
--- a/scm/mcron/vixie-specification.scm
+++ b/src/mcron/vixie-specification.scm
@@ -1,45 +1,45 @@
-;; Copyright (C) 2003 Dale Mellor
-;;
-;; This file is part of GNU mcron.
-;;
-;; GNU mcron 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.
-;;
-;; GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
-
-
-;; This file provides methods for reading a complete Vixie-style configuration
-;; file, either from a real file or an already opened port. It also exposes the
-;; method for parsing the time-specification part of a Vixie string, so that
-;; these can be used to form the next-time-function of a job in a Guile
-;; configuration file.
+;;;; vixie-specification.scm -- read Vixie-sytle configuration file
+;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+;;;; Commentary:
+;;;
+;;; Methods for reading a complete Vixie-style configuration file, either from
+;;; a real file or an already opened port. It also exposes the method for
+;;; parsing the time-specification part of a Vixie string, so that these can
+;;; be used to form the next-time-function of a job in a Guile configuration
+;;; file.
+;;;
+;;;; Code:
(define-module (mcron vixie-specification)
+ #:use-module (ice-9 regex)
+ #:use-module (ice-9 rdelim)
+ #:use-module (mcron base)
+ #:use-module (mcron config)
+ #:use-module (mcron job-specifier)
+ #:use-module (mcron redirect)
+ #:use-module (mcron vixie-time)
+ #:use-module (srfi srfi-1)
#:export (parse-user-vixie-line
parse-system-vixie-line
read-vixie-port
read-vixie-file
- check-system-crontab)
- #:use-module ((mcron config) :select (config-socket-file))
- #:use-module (mcron core)
- #:use-module (mcron job-specifier)
- #:use-module (mcron redirect)
- #:use-module (mcron vixie-time))
-
-
-(use-modules (ice-9 regex) (ice-9 rdelim)
- (srfi srfi-1) (srfi srfi-2) (srfi srfi-13) (srfi srfi-14))
-
-
+ check-system-crontab))
;; A line in a Vixie-style crontab file which gives a command specification
;; carries two pieces of information: a time specification consisting of five
@@ -108,11 +108,9 @@
(if match
(append-environment-mods (match:substring match 1)
(match:substring match 2))
- (and-let* ((match (regexp-exec parse-vixie-environment-regexp4 string)))
- (append-environment-mods (match:substring match 1) #f)))))
-
-
-
+ (and=> (regexp-exec parse-vixie-environment-regexp4 string)
+ (λ (match)
+ (append-environment-mods (match:substring match 1) #f))))))
;; The next procedure reads an entire Vixie-style file. For each line in the
;; file there are three possibilities (after continuation lines have been
@@ -162,13 +160,11 @@
(parse-vixie-environment line)
(parse-vixie-line line)))
(lambda (key exit-code . msg)
- (throw
- 'mcron-error
- exit-code
- (apply string-append
- (number->string report-line)
- ": "
- msg)))))))))
+ (throw 'mcron-error exit-code
+ (apply string-append
+ (number->string report-line)
+ ": "
+ msg)))))))))
diff --git a/scm/mcron/vixie-time.scm b/src/mcron/vixie-time.scm
index 2f26a6d..c4d6bd9 100644
--- a/scm/mcron/vixie-time.scm
+++ b/src/mcron/vixie-time.scm
@@ -1,29 +1,26 @@
-;; Copyright (C) 2003 Dale Mellor
-;;
-;; This file is part of GNU mcron.
-;;
-;; GNU mcron 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.
-;;
-;; GNU mcron 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 GNU mcron. If not, see <http://www.gnu.org/licenses/>.
-
+;;;; vixie-time.scm -- parse Vixie-style times
+;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
(define-module (mcron vixie-time)
- #:export (parse-vixie-time)
- #:use-module (mcron job-specifier))
-
-
-(use-modules (srfi srfi-1) (srfi srfi-13) (srfi srfi-14)
- (ice-9 regex))
-
+ #:use-module (ice-9 regex)
+ #:use-module (mcron job-specifier)
+ #:use-module (srfi srfi-1)
+ #:export (parse-vixie-time))
;; In Vixie-style time specifications three-letter symbols are allowed to stand
;; for the numbers corresponding to months and days of the week. We deal with
@@ -179,11 +176,12 @@
;; simply unreadable without all of these aliases.
(define (increment-time-component time time-spec)
- (let* ((time-list (time-spec:list time-spec))
- (getter (time-spec:getter time-spec))
- (setter (time-spec:setter time-spec))
- (next-best (find-best-next (getter time) time-list))
- (wrap-around (eqv? (cdr next-best) 9999)))
+ (let* ((time-list (time-spec:list time-spec))
+ (getter (time-spec:getter time-spec))
+ (setter (time-spec:setter time-spec))
+ (find-best-next (@@ (mcron job-specifier) %find-best-next))
+ (next-best (find-best-next (getter time) time-list))
+ (wrap-around (eqv? (cdr next-best) 9999)))
(setter time ((if wrap-around car cdr) next-best))
wrap-around))
diff --git a/src/wrapper.c b/src/wrapper.c
new file mode 100644
index 0000000..bb7932e
--- /dev/null
+++ b/src/wrapper.c
@@ -0,0 +1,85 @@
+/* wrapper.c -- C code booting Guile
+ Copyright © 2003, 2014 Dale Mellor <dale_mellor@users.sourceforge.net>
+ Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
+
+ This file is part of GNU Mcron.
+
+ GNU Mcron 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.
+
+ GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>. */
+
+/* This C code represents a thin wrapper around the Guile code of Mcron. It
+ is needed because the crontab personality requires SUID which is not
+ permitted for executable scripts. */
+
+#include <libguile.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Forward declarations. */
+static void inner_main (void *closure, int argc, char **argv);
+static void react_to_terminal_signal (int sig);
+static SCM set_cron_signals (void);
+
+int
+main (int argc, char **argv)
+{
+ scm_boot_guile (argc, argv, inner_main, 0);
+
+ return EXIT_SUCCESS;
+}
+
+/* Launch the Mcron Guile main program. */
+static void
+inner_main (void *closure, int argc, char **argv)
+{
+ /* Set Guile load paths to ensure that Mcron modules will be found. */
+ if (getenv ("MCRON_UNINSTALLED") == NULL)
+ {
+ scm_c_eval_string ("(set! %load-path (cons \""
+ PACKAGE_LOAD_PATH "\" %load-path))");
+ scm_c_eval_string ("(set! %load-compiled-path (cons \""
+ PACKAGE_LOAD_PATH "\" %load-compiled-path))");
+ }
+ scm_set_current_module (scm_c_resolve_module ("mcron scripts " PROGRAM));
+ /* Register set_cron_signals to be called from Guile. */
+ scm_c_define_gsubr ("c-set-cron-signals", 0, 0, 0, set_cron_signals);
+ scm_c_eval_string ("(main)");
+}
+
+/* Set up all the signal handlers as required by the cron personality. This
+ is necessary to perform the signal processing in C because the sigaction
+ function won't work when called from Guile. */
+static SCM
+set_cron_signals ()
+{
+ static struct sigaction sa;
+
+ memset (&sa, 0, sizeof (sa));
+ sa.sa_handler = react_to_terminal_signal;
+ sigaction (SIGTERM, &sa, 0);
+ sigaction (SIGINT, &sa, 0);
+ sigaction (SIGQUIT, &sa, 0);
+ sigaction (SIGHUP, &sa, 0);
+
+ return SCM_BOOL_T;
+}
+
+/* Handle signal SIG and exit. All signals that mcron handles will produce
+ the same behavior so we don't need to use SIG in the implementation. */
+static void
+react_to_terminal_signal (int sig)
+{
+ scm_c_eval_string ("(delete-run-file)");
+ exit (EXIT_FAILURE);
+}
diff --git a/tests/environment.scm b/tests/environment.scm
new file mode 100644
index 0000000..1bc34a6
--- /dev/null
+++ b/tests/environment.scm
@@ -0,0 +1,39 @@
+;;;; environment.scm -- tests for (mcron environment) module
+;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+(use-modules (srfi srfi-64)
+ (mcron environment))
+
+(test-begin "environment")
+
+(test-assert "modifiy-environment: basic"
+ (begin
+ (modify-environment '(("FOO" . "bar")) (getpw))
+ (equal? (getenv "FOO") "bar")))
+
+(test-assert "modifiy-environment: user & logname"
+ ;; Check that USER and LOGNAME environment variables can't be changed.
+ (let* ((user-entry (pk (getpw)))
+ (user-name (passwd:name user-entry)))
+ (modify-environment '(("USER" . "alice")) user-entry)
+ (modify-environment '(("LOGNAME" . "bob")) user-entry)
+ (equal? user-name
+ (pk (getenv "USER"))
+ (pk (getenv "LOGNAME")))))
+
+(test-end)
diff --git a/tests/job-specifier.scm b/tests/job-specifier.scm
new file mode 100644
index 0000000..889530b
--- /dev/null
+++ b/tests/job-specifier.scm
@@ -0,0 +1,43 @@
+;;;; job-specifier.scm -- tests for (mcron job-specifier) module
+;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org>
+;;;
+;;; This file is part of GNU Mcron.
+;;;
+;;; GNU Mcron 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.
+;;;
+;;; GNU Mcron 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 GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
+
+(use-modules (srfi srfi-64)
+ (mcron job-specifier))
+
+(test-begin "job-specifier")
+
+(test-equal "range: basic"
+ '(0 1 2 3 4 5 6 7 8 9)
+ (range 0 10))
+
+(test-equal "range: positive step"
+ '(0 2 4 6 8)
+ (range 0 10 2))
+
+(test-assert "range: zero step"
+ ;; Since this behavior is undefined, only check if range doesn't crash.
+ (range 0 5 0))
+
+(test-assert "range: negative step"
+ ;; Since this behavior is undefined, only check if range doesn't crash.
+ (range 0 5 -2))
+
+(test-assert "range: reverse boundaries"
+ (range 10 3))
+
+(test-end)