From f22de155b8e75918c9030897a01e070a8af02661 Mon Sep 17 00:00:00 2001 From: atsb Date: Tue, 14 Apr 2020 22:10:28 +0200 Subject: [PATCH 01/21] small fix for older gcc versions --- src/mcron.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mcron.c b/src/mcron.c index c0b151a..adb91aa 100644 --- a/src/mcron.c +++ b/src/mcron.c @@ -110,8 +110,9 @@ parse_opt (int key, char *arg, struct argp_state *state) SCM lst = SCM_EOL; int filec = state->argc - state->next; char **filev = state->argv + state->next; + int i; - for (int i = filec - 1; i >= 0; i--) + for (i = filec - 1; i >= 0; i--) lst = scm_cons (scm_from_locale_string (filev[i]), lst); assq_symbol_set_x (config, "files", lst); From a8d938c4ed93aa03fc87c9173aaee4e26ee67c5b Mon Sep 17 00:00:00 2001 From: Dale Mellor Date: Mon, 20 Apr 2020 11:27:53 +0000 Subject: [PATCH 02/21] test: make date tests reliable, i.e. independent of current time Some of the date tests depend both on the particular time of day and year at which the test is run, and also on the state of daylight-savings adjustments. (At the present time on my system there are four failing tests, but YMMV.) This patch puts all the tests to UTC time in the C locale, making the results consistent. *All* items in the test suite should be passing once again. * tests/job-schedule.scm: Fix up the environment before running the tests. --- tests/job-specifier.scm | 32 ++++++++++++++------------------ tests/schedule.sh | 3 +++ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/tests/job-specifier.scm b/tests/job-specifier.scm index baf96af..70dd518 100644 --- a/tests/job-specifier.scm +++ b/tests/job-specifier.scm @@ -56,36 +56,32 @@ ;;; TODO: Find more meaningful date examples. +(setenv "TZ" ":UTC") + (test-equal "next-year" - (list 59989762800 1546293600) - (list (next-year '(1971)) + (list 1893456000 1546300800) + (list (next-year '(130)) ;; This is the year 2030. (next-year-from 1522095469))) (test-equal "next-month" - (list 28854000 5094000) - (list (next-month '(11)) - (next-month-from 101 '(0 2 4)))) + 5097600 + (next-month-from 101 '(0 2 4))) (test-equal "next-day" - (list 2588400 342000) - (list (next-day '(31)) - (next-day-from 4337 '(0 5 10)))) + 345600 + (next-day-from 4337 '(0 5 10))) (test-equal "next-hour" - '(3600 82800 3600) - (list (next-hour) - (next-hour '(0)) - (next-hour-from 3 '(0 1 2 3 4)))) + 3600 + (next-hour-from 3 '(0 1 2 3 4))) (test-equal "next-minute" - '(240 60) - (list (next-minute '(4 9)) - (next-minute-from 8))) + 60 + (next-minute-from 8)) (test-equal "next-second" - '(52 15) - (list (next-second '(52 55)) - (next-second-from 14))) + 15 + (next-second-from 14)) ;;; ;;; Check 'configuration-user' manipulation diff --git a/tests/schedule.sh b/tests/schedule.sh index d403f1d..9a1836e 100644 --- a/tests/schedule.sh +++ b/tests/schedule.sh @@ -26,6 +26,9 @@ export SOURCE_DATE_EPOCH TZ=UTC0 export TZ +LC_ALL=C +export LC_ALL + # Use current working directory to store mcron files XDG_CONFIG_HOME=`pwd` export XDG_CONFIG_HOME From cb88cc9e423ce02c3e2806e8a7889c3048264a25 Mon Sep 17 00:00:00 2001 From: Dale Mellor Date: Fri, 10 Apr 2020 19:38:07 +0100 Subject: [PATCH 03/21] doc/mcron.texi: Make the manual gender-neutral. Replace his/hers with theirs, etc. *doc/mcron.text: light edits only. --- doc/mcron.texi | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/mcron.texi b/doc/mcron.texi index c124bc8..70ab38c 100644 --- a/doc/mcron.texi +++ b/doc/mcron.texi @@ -158,10 +158,10 @@ example, take the system load into consideration. Turns out to be easy to provide complete backwards compatibility with Vixie cron. @item -Each user looks after his own files in his own directory. He can use +Each user looks after their own files in their own directory. They can use more than one to break up complicated cron specifications. @item -Each user can run his own daemon. This removes the need for suid +Each user can run their own daemon. This removes the need for suid programs to manipulate the crontabs, and eliminates many security concerns that surround all existing cron programs. @item @@ -516,7 +516,7 @@ second-to-last day of every month. @emph{NOTE} that this section is definitive. If there is a difference in behaviour between the mcron program and this part of the manual, then there is a bug in the program. This section is also copied verbatim -from Paul Vixie's documentation for his cron program, and his +from Paul Vixie's documentation for their cron program, and their copyright notice is duly reproduced below. There are three problems with this specification. @@ -815,7 +815,7 @@ place in the part which implements the mcron personality. @cindex mcron arguments @cindex command line, mcron @cindex mcron command line -Mcron should be run by the user who wants to schedule his jobs. It +Mcron should be run by the user who wants to schedule their jobs. It may be made a background job using the facilities of the shell. The basic command is @code{mcron [OPTION ...] [file ...]} which has the effect of reading all the configuration files specified (subject to @@ -1026,7 +1026,7 @@ Delete the user's crontab file, and exit. @item -e @item --edit Using the editor specified in the user's VISUAL or EDITOR environment -variables, allow the user to edit his crontab. Once the user exits the +variables, allow the user to edit their crontab. Once the user exits the editor, the crontab is checked for parseability, and if it is okay then it is installed as the user's new crontab and the daemon is notified that a change has taken place, so that the new file will @@ -1231,7 +1231,7 @@ the mcron base. @deffn{Scheme procedure} remove-user-jobs user @ [#:schedule @var{%global-schedule}] -The argument @var{user} should be a string naming a user (his +The argument @var{user} should be a string naming a user (their login name), or an integer UID, or an object representing the user's passwd entry. All jobs on the current job list that are scheduled to be run under this personality are removed from the job list. From ad6e4e550512555328cfbc8f2777e71f5d0e28a4 Mon Sep 17 00:00:00 2001 From: Dale Mellor Date: Mon, 13 Apr 2020 19:45:45 +0100 Subject: [PATCH 04/21] test: Demonstration of failure to open local file. The mcron program goes looking for files specified on the command line in Guile's module path, inevitably resulting in failure to load said file. Running 'make check' will show at least one failure. * tests/basic.sh: Added new test. --- tests/basic.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/basic.sh b/tests/basic.sh index 07b664b..7b2ca55 100644 --- a/tests/basic.sh +++ b/tests/basic.sh @@ -27,6 +27,9 @@ cat > cron/foo.guile < "output$$" +grep -e "foo" "output$$" || fail_ "'foo.guile' job is not scheduled" + mcron --schedule=1 > "output$$" grep -e "foo" "output$$" || fail_ "'foo.guile' job is not scheduled" From deaa79a7c687ebc3eb3bb6c318a8e11de154efcb Mon Sep 17 00:00:00 2001 From: Dale Mellor Date: Mon, 13 Apr 2020 20:05:27 +0100 Subject: [PATCH 05/21] mcron: Look for local files in local directory. Previously were looking for files listed on the command line in Guile's modules directory. This is a bug-fix; running 'make check' will reveal one less failure than before. * src/mcron/scripts/mcron.scm (process-user-file): use read and eval instead of load. --- src/mcron/scripts/mcron.scm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mcron/scripts/mcron.scm b/src/mcron/scripts/mcron.scm index 6545ada..8ae61cf 100644 --- a/src/mcron/scripts/mcron.scm +++ b/src/mcron/scripts/mcron.scm @@ -19,6 +19,7 @@ (define-module (mcron scripts mcron) #:use-module (ice-9 ftw) + #:use-module (ice-9 rdelim) #:use-module (mcron base) #:use-module (mcron config) #:use-module (mcron job-specifier) ;for user/system files @@ -36,9 +37,11 @@ silently ignored." (cond ((string=? "-" file-name) (if (string=? input "vixie") (read-vixie-port (current-input-port)) - (eval-string (read-string)))) + (eval-string (read-string) + (resolve-module '(mcron job-specifier))))) ((or guile-syntax? (regexp-exec guile-regexp file-name)) - (load file-name)) + (eval-string (read-delimited "" (open-input-file file-name)) + (resolve-module '(mcron job-specifier)))) ((regexp-exec vixie-regexp file-name) (read-vixie-file file-name)))))) From 4a05a2e8612560cd7f3274d2930e86bfc9f1ec93 Mon Sep 17 00:00:00 2001 From: Dale Mellor Date: Mon, 20 Apr 2020 16:25:19 +0000 Subject: [PATCH 06/21] test: demonstrate incorrect -s option on mcron program The option is supposed to be able to take an optional argument, but if the argument is not supplied (should default to 8) then the test, rather than failing, is skipped with a friendly message in the log file. The proper fix will come with an upstream patch to GNU Guile, and a future version of Mcron. * tests/schedule-2.sh: new test, new file * Makefile.am: make sure to run the new test file --- Makefile.am | 1 + tests/schedule-2.sh | 82 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 tests/schedule-2.sh diff --git a/Makefile.am b/Makefile.am index 38b0a7b..972e2c6 100755 --- a/Makefile.am +++ b/Makefile.am @@ -136,6 +136,7 @@ SCM_LOG_DRIVER = \ TESTS = \ tests/basic.sh \ tests/schedule.sh \ + tests/schedule-2.sh \ tests/base.scm \ tests/environment.scm \ tests/job-specifier.scm \ diff --git a/tests/schedule-2.sh b/tests/schedule-2.sh new file mode 100644 index 0000000..7039123 --- /dev/null +++ b/tests/schedule-2.sh @@ -0,0 +1,82 @@ +# schedule-2.sh -- Check mcron schedule output +# Copyright © 2020 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 . + +source "${srcdir}/tests/init.sh" + +# Use UTC and SOURCE_DATE_EPOCH to get reproducible result. + +SOURCE_DATE_EPOCH=1 +export SOURCE_DATE_EPOCH + +TZ=UTC0 +export TZ + +LC_ALL=C +export LC_ALL + +# Use current working directory to store mcron files +XDG_CONFIG_HOME=`pwd` +export XDG_CONFIG_HOME + +mkdir cron +cat > cron/foo.guile < expected < output-A +diff expected output-A \ + || skip_ 'The -s option is not fully functional; +this will be fixed with a future version of GNU Guile and then +a future version of GNU Mcron.' + +Exit 0 From 1eedf3b6d24f421ccdb8798b053ee718d1051651 Mon Sep 17 00:00:00 2001 From: Dale Mellor Date: Mon, 13 Apr 2020 11:42:39 +0100 Subject: [PATCH 07/21] project: banish need for C compiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch gets rid of the thin veneer that we currently have around the three executables. This was done for historical reasons (circa 2003 Guile couldnʼt deal with process signals and forks). In fact these problems were fixed many moons ago, and there is now no need for it. The project becomes 100% Guile! Many files are affected; interested coders should use the GIT repository to understand the details of all the changes. --- Makefile.am | 75 ++++++++++---------- build-aux/guix.scm | 8 +-- configure.ac | 55 +++++++-------- src/cron.c | 80 ---------------------- src/cron.in | 8 +++ src/crontab.c | 48 ------------- src/crontab.in | 7 ++ src/mcron.c | 125 ---------------------------------- src/mcron.in | 8 +++ src/mcron/base.scm | 2 +- src/mcron/job-specifier.scm | 19 +++--- src/mcron/scripts/cron.scm | 114 ++++++++++++++++++------------- src/mcron/scripts/crontab.scm | 4 +- src/mcron/scripts/mcron.scm | 103 ++++++++++++++++++++-------- src/utils.c | 48 ------------- src/utils.h | 33 --------- tests/schedule-2.sh | 13 ++-- 17 files changed, 248 insertions(+), 502 deletions(-) delete mode 100644 src/cron.c create mode 100644 src/cron.in delete mode 100644 src/crontab.c create mode 100644 src/crontab.in delete mode 100644 src/mcron.c create mode 100644 src/mcron.in delete mode 100644 src/utils.c delete mode 100644 src/utils.h diff --git a/Makefile.am b/Makefile.am index 972e2c6..e8fe80c 100755 --- a/Makefile.am +++ b/Makefile.am @@ -21,40 +21,18 @@ ## Programs. ## ## ---------- ## -bin_PROGRAMS = bin/mcron +bin_SCRIPTS = bin/mcron +noinst_SCRIPTS = if MULTI_USER -bin_PROGRAMS += bin/crontab -sbin_PROGRAMS = bin/cron +bin_SCRIPTS += bin/crontab +sbin_SCRIPTS = bin/cron else -noinst_PROGRAMS = bin/cron bin/crontab +noinst_SCRIPTS += bin/cron bin/crontab endif -AM_CPPFLAGS = \ - -DPACKAGE_LOAD_PATH=\"$(guilesitedir)\" \ - -DPACKAGE_LOAD_COMPILED_PATH=\"$(guilesitegodir)\" \ - -D_GNU_SOURCE - -AM_CFLAGS = @GUILE_CFLAGS@ -LDADD = @GUILE_LIBS@ src/libmcron.a - -bin_mcron_SOURCES = src/mcron.c -bin_mcron_DEPENDENCIES = $(compiled_modules) $(noinst_LIBRARIES) - -bin_cron_SOURCES = src/cron.c -bin_cron_DEPENDENCIES = $(compiled_modules) $(noinst_LIBRARIES) - -bin_crontab_SOURCES = src/crontab.c -bin_crontab_DEPENDENCIES = $(compiled_modules) $(noinst_LIBRARIES) - # wrapper to be used in the build environment and for running tests. -noinst_SCRIPTS = pre-inst-env - -# local library. -noinst_LIBRARIES = src/libmcron.a -src_libmcron_a_SOURCES = \ - src/utils.c \ - src/utils.h +noinst_SCRIPTS += pre-inst-env ## --------------- ## ## Guile modules. ## @@ -97,7 +75,7 @@ compiled_modules = \ $(pkgmodulego_DATA) \ $(pkgscriptgo_DATA) -CLEANFILES = $(compiled_modules) +CLEANFILES = $(compiled_modules) bin/crontab bin/cron bin/mcron DISTCLEANFILES = src/mcron/config.scm # Unset 'GUILE_LOAD_COMPILED_PATH' altogether while compiling. Otherwise, if @@ -120,6 +98,24 @@ DISTCLEANFILES = src/mcron/config.scm --warn=format --warn=unbound-variable --warn=arity-mismatch \ --target="$(host)" --output="$@" "$<" $(devnull_verbose) + +bin/% : src/%.in Makefile + -@sed -e 's,%PREFIX%,${prefix},g' \ + -e 's,%modsrcdir%,${guilesitedir},g' \ + -e 's,%modbuilddir%,${guilesitegodir},g' \ + -e 's,%localstatedir%,${localstatedir},g' \ + -e 's,%pkglibdir%,${pkglibdir},g' \ + -e 's,%sysconfdir%,${sysconfdir},g' \ + -e 's,%localedir%,${localedir},g' \ + -e 's,%VERSION%,@VERSION@,g' \ + -e 's,%PACKAGE_BUGREPORT%,@PACKAGE_BUGREPORT@,g' \ + -e 's,%PACKAGE_NAME%,@PACKAGE_NAME@,g' \ + -e 's,%PACKAGE_URL%,@PACKAGE_URL@,g' \ + -e 's,%GUILE%,$(GUILE),g' \ + $< > $@; + -@chmod a+x $@ + + ## ------------ ## ## Test suite. ## ## ------------ ## @@ -161,25 +157,28 @@ EXTRA_DIST = \ # Sed command for Transforming program names. transform_exe = s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/ -if MULTI_USER install-exec-hook: - tcrontab=`echo crontab$(EXEEXT) | sed '$(transform_exe)'`; \ +if MULTI_USER + tcrontab=`echo crontab | sed '$(transform_exe)'`; \ chmod u+s $(DESTDIR)$(bindir)/$${tcrontab} + tcron=`echo cron | sed '$(transform_exe)'`; \ + chmod u+s $(DESTDIR)$(sbindir)/$${tcron} endif + tmcron=`echo mcron | sed '$(transform_exe)'`; installcheck-local: ## Check that only expected programs are installed and configured - tmcron=`echo mcron$(EXEEXT) | sed '$(transform_exe)'`; \ + tmcron=`echo mcron | sed '$(transform_exe)'`; \ test -e $(DESTDIR)$(bindir)/$${tmcron} if MULTI_USER - tcrontab=`echo crontab$(EXEEXT) | sed '$(transform_exe)'`; \ + tcrontab=`echo crontab | sed '$(transform_exe)'`; \ test -u $(DESTDIR)$(bindir)/$${tcrontab} - tcron=`echo cron$(EXEEXT) | sed '$(transform_exe)'`; \ + tcron=`echo cron | sed '$(transform_exe)'`; \ test -e $(DESTDIR)$(sbindir)/$${tcron} else !MULTI_USER - tcrontab=`echo crontab$(EXEEXT) | sed '$(transform_exe)'`; \ + tcrontab=`echo crontab | sed '$(transform_exe)'`; \ test ! -u $(DESTDIR)$(bindir)/$${tcrontab} - tcron=`echo cron$(EXEEXT) | sed '$(transform_exe)'`; \ + tcron=`echo cron | sed '$(transform_exe)'`; \ test ! -f $(DESTDIR)$(sbindir)/$${tcron} endif !MULTI_USER @@ -220,10 +219,10 @@ gen_man = \ esac $(srcdir)/doc/mcron.1: src/mcron/scripts/mcron.scm bin/mcron - -@prog="mcron"; man_section=1; $(gen_man) + -@prog="bin/mcron"; man_section=1; $(gen_man) $(srcdir)/doc/crontab.1: src/mcron/scripts/crontab.scm bin/crontab - -@prog="crontab"; man_section=1; $(gen_man) + -@prog="bin/crontab"; man_section=1; $(gen_man) $(srcdir)/doc/cron.8: src/mcron/scripts/cron.scm bin/cron -@prog="cron"; man_section=8; $(gen_man) diff --git a/build-aux/guix.scm b/build-aux/guix.scm index ae253ec..3c57bf1 100644 --- a/build-aux/guix.scm +++ b/build-aux/guix.scm @@ -32,16 +32,16 @@ '(".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"))))) + '("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) ".")) (package (inherit (specification->package "mcron")) - (version "1.1.4") + (version "1.1.4+") (source (local-file (dirname %srcdir) #:recursive? #t #:select? keep-mcron-file?)) (inputs diff --git a/configure.ac b/configure.ac index 768a94c..fb19229 100755 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ ## Process this file with autoconf to produce a configure script. -# Copyright © 2003, 2005, 2012, 2014 Dale Mellor -# +# +# Copyright © 2003, 2005, 2012, 2014 Dale Mellor # Copyright © 2015, 2016, 2017, 2018 Mathieu Lirzin # Copyright © 2018 宋文武 # @@ -20,19 +20,18 @@ # along with GNU Mcron. If not, see . AC_PREREQ(2.61) -AC_INIT([GNU Mcron], [1.1.4], [bug-mcron@gnu.org]) -AC_CONFIG_SRCDIR([src/mcron.c]) +AC_INIT([GNU Mcron], [1.1.4+], [bug-mcron@gnu.org]) +AC_CONFIG_SRCDIR([src/mcron/scripts/mcron.scm]) AC_CONFIG_AUX_DIR([build-aux]) AC_REQUIRE_AUX_FILE([test-driver.scm]) -AM_INIT_AUTOMAKE([subdir-objects -Wall -Wno-override std-options]) -AM_SILENT_RULES([yes]) # enables silent rules by default + +dnl We're fine with GNU make constructs, hence '-Wno-portability'. +AM_INIT_AUTOMAKE([1.11 gnu silent-rules subdir-objects color-tests + -Wall -Wno-override -Wno-portability std-options]) + +AM_SILENT_RULES([yes]) # Enables silent rules by default. AC_CANONICAL_HOST -AC_PROG_AWK -AC_PROG_EGREP -AM_PROG_CC_C_O -AC_PROG_RANLIB -AM_PROG_AR # Check for Guile development files. GUILE_PKG([3.0 2.2 2.0]) @@ -41,12 +40,6 @@ GUILE_PKG([3.0 2.2 2.0]) # 'config.rpath' script. PKG_CHECK_MODULES(GUILE, [guile-$GUILE_EFFECTIVE_VERSION]) -# Check for 'argp' program arguments parser. -AC_CHECK_FUNC(argp_parse, [], AC_MSG_ERROR([argp not found])) - -# Check for non-POSIX string formatting function. -AC_CHECK_FUNC(asprintf, [], AC_MSG_ERROR([asprintf not found])) - # Checks for programs. GUILE_PROGS @@ -76,8 +69,8 @@ AC_MSG_CHECKING([which spool directory to use]) AC_ARG_WITH(spool-dir, AC_HELP_STRING([--with-spool-dir], [the crontab spool directory (/var/cron/tabs)]), - CONFIG_SPOOL_DIR=$withval, - CONFIG_SPOOL_DIR=[/var/cron/tabs]) + CONFIG_SPOOL_DIR=$withval, + CONFIG_SPOOL_DIR=[/var/cron/tabs]) AC_MSG_RESULT($CONFIG_SPOOL_DIR) AC_SUBST(CONFIG_SPOOL_DIR) @@ -85,8 +78,8 @@ AC_MSG_CHECKING([name of socket]) AC_ARG_WITH(socket-file, AC_HELP_STRING([--with-socket-file], [unix pathname for cron socket (/var/cron/socket)]), - CONFIG_SOCKET_FILE=$withval, - CONFIG_SOCKET_FILE=[/var/cron/socket]) + CONFIG_SOCKET_FILE=$withval, + CONFIG_SOCKET_FILE=[/var/cron/socket]) AC_MSG_RESULT($CONFIG_SOCKET_FILE) AC_SUBST(CONFIG_SOCKET_FILE) @@ -94,8 +87,8 @@ AC_MSG_CHECKING([name of allow file]) AC_ARG_WITH(allow-file, AC_HELP_STRING([--with-allow-file], [the file of allowed users (/var/cron/allow)]), - CONFIG_ALLOW_FILE=$withval, - CONFIG_ALLOW_FILE=[/var/cron/allow]) + CONFIG_ALLOW_FILE=$withval, + CONFIG_ALLOW_FILE=[/var/cron/allow]) AC_MSG_RESULT($CONFIG_ALLOW_FILE) AC_SUBST(CONFIG_ALLOW_FILE) @@ -103,8 +96,8 @@ AC_MSG_CHECKING([name of deny file]) AC_ARG_WITH(deny-file, AC_HELP_STRING([--with-deny-file], [the file of barred users (/var/cron/deny)]), - CONFIG_DENY_FILE=$withval, - CONFIG_DENY_FILE=[/var/cron/deny]) + CONFIG_DENY_FILE=$withval, + CONFIG_DENY_FILE=[/var/cron/deny]) AC_MSG_RESULT($CONFIG_DENY_FILE) AC_SUBST(CONFIG_DENY_FILE) @@ -112,8 +105,8 @@ AC_MSG_CHECKING([name of PID file]) AC_ARG_WITH(pid-file, AC_HELP_STRING([--with-pid-file], [the file to record cron's PID (/var/run/cron.pid)]), - CONFIG_PID_FILE=$withval, - CONFIG_PID_FILE=[/var/run/cron.pid]) + CONFIG_PID_FILE=$withval, + CONFIG_PID_FILE=[/var/run/cron.pid]) AC_MSG_RESULT($CONFIG_PID_FILE) AC_SUBST(CONFIG_PID_FILE) @@ -121,19 +114,19 @@ AC_MSG_CHECKING([directory to hold temporary files]) AC_ARG_WITH(tmp-dir, AC_HELP_STRING([--with-tmp-dir], [directory to hold temporary files (/tmp)]), - CONFIG_TMP_DIR=$withval, - CONFIG_TMP_DIR=[/tmp]) + CONFIG_TMP_DIR=$withval, + CONFIG_TMP_DIR=[/tmp]) AC_MSG_RESULT($CONFIG_TMP_DIR) AC_SUBST(CONFIG_TMP_DIR) # Include the Maintainer's Makefile fragment, if it's here. MAINT_MAKEFILE=/dev/null AS_IF([test -r "$srcdir/maint.mk"], - [MAINT_MAKEFILE="$srcdir/maint.mk"]) + [MAINT_MAKEFILE="$srcdir/maint.mk"]) AC_SUBST_FILE([MAINT_MAKEFILE]) AC_CONFIG_FILES([pre-inst-env:build-aux/pre-inst-env.in], - [chmod +x pre-inst-env]) + [chmod +x pre-inst-env]) AC_CONFIG_FILES([doc/config.texi Makefile src/mcron/config.scm]) diff --git a/src/cron.c b/src/cron.c deleted file mode 100644 index 074f882..0000000 --- a/src/cron.c +++ /dev/null @@ -1,80 +0,0 @@ -/* cron.c -- run jobs at scheduled times - Copyright © 2003, 2014 Dale Mellor - Copyright © 2015, 2016, 2017 Mathieu Lirzin - - 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 . */ - -#include "utils.h" -#include -#include - -/* 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[]) -{ - /* Set Guile load paths to ensure that Mcron modules will be found after - installation. In a build environment let the 'pre-inst-env' script set - the correct paths. */ - if (getenv ("MCRON_UNINSTALLED") == NULL) - { - wrap_env_path ("GUILE_LOAD_PATH", PACKAGE_LOAD_PATH); - wrap_env_path ("GUILE_LOAD_COMPILED_PATH", PACKAGE_LOAD_COMPILED_PATH); - } - - scm_boot_guile (argc, argv, inner_main, NULL); - - return EXIT_SUCCESS; -} - -/* Launch the Mcron Guile main program. */ -static void -inner_main (void *closure, int argc, char *argv[]) -{ - scm_set_current_module (scm_c_resolve_module ("mcron scripts cron")); - scm_c_define_gsubr ("c-set-cron-signals", 0, 0, 0, set_cron_signals); - scm_call_0 (scm_variable_ref (scm_c_lookup ("main"))); -} - -/* Set up all the signal handlers. 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 cron 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/src/cron.in b/src/cron.in new file mode 100644 index 0000000..97d49b4 --- /dev/null +++ b/src/cron.in @@ -0,0 +1,8 @@ +#!%GUILE% --no-auto-compile +-*- scheme -*- +!# + +(set! %load-path (cons "%modsrcdir%" %load-path)) +(set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path)) +(use-modules (mcron scripts cron)) +(main) diff --git a/src/crontab.c b/src/crontab.c deleted file mode 100644 index 16c0be6..0000000 --- a/src/crontab.c +++ /dev/null @@ -1,48 +0,0 @@ -/* crontab.c -- edit users' crontab files - Copyright © 2003, 2014 Dale Mellor - Copyright © 2015, 2016, 2017 Mathieu Lirzin - - 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 . */ - -#include "utils.h" - -/* Forward declarations. */ -static void inner_main (void *closure, int argc, char *argv[]); - -int -main (int argc, char *argv[]) -{ - /* Set Guile load paths to ensure that Mcron modules will be found after - installation. In a build environment let the 'pre-inst-env' script set - the correct paths. */ - if (getenv ("MCRON_UNINSTALLED") == NULL) - { - wrap_env_path ("GUILE_LOAD_PATH", PACKAGE_LOAD_PATH); - wrap_env_path ("GUILE_LOAD_COMPILED_PATH", PACKAGE_LOAD_COMPILED_PATH); - } - - scm_boot_guile (argc, argv, inner_main, NULL); - - return EXIT_SUCCESS; -} - -/* Launch the Mcron Guile main program. */ -static void -inner_main (void *closure, int argc, char *argv[]) -{ - scm_set_current_module (scm_c_resolve_module ("mcron scripts crontab")); - scm_call_0 (scm_variable_ref (scm_c_lookup ("main"))); -} diff --git a/src/crontab.in b/src/crontab.in new file mode 100644 index 0000000..fa31878 --- /dev/null +++ b/src/crontab.in @@ -0,0 +1,7 @@ +#!%GUILE% --no-auto-compile +-*- scheme -*- +!# + +(set! %load-path (cons "%modsrcdir%" %load-path)) +(set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path)) +((@ (mcron scripts crontab) main)) diff --git a/src/mcron.c b/src/mcron.c deleted file mode 100644 index c0b151a..0000000 --- a/src/mcron.c +++ /dev/null @@ -1,125 +0,0 @@ -/* mcron.c -- run jobs at scheduled times - Copyright © 2003, 2014 Dale Mellor - Copyright © 2015, 2016, 2017 Mathieu Lirzin - - 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 . */ - -#include "utils.h" -#include -#include - -/* Forward declarations. */ -static void inner_main (void *closure, int argc, char *argv[]); -static SCM parse_args (int argc, char *argv[]); -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -int -main (int argc, char *argv[]) -{ - /* Set Guile load paths to ensure that Mcron modules will be found after - installation. In a build environment let the 'pre-inst-env' script set - the correct paths. */ - if (getenv ("MCRON_UNINSTALLED") == NULL) - { - wrap_env_path ("GUILE_LOAD_PATH", PACKAGE_LOAD_PATH); - wrap_env_path ("GUILE_LOAD_COMPILED_PATH", PACKAGE_LOAD_COMPILED_PATH); - } - - scm_boot_guile (argc, argv, inner_main, NULL); - - return EXIT_SUCCESS; -} - -/* Launch the Mcron Guile main program. */ -static void -inner_main (void *closure, int argc, char *argv[]) -{ - SCM config = parse_args (argc, argv); - scm_set_current_module (scm_c_resolve_module ("mcron scripts mcron")); - scm_call_1 (scm_variable_ref (scm_c_lookup ("main")), config); -} - -/* Handle command line arguments. */ -static SCM -parse_args (int argc, char *argv[]) -{ - static struct argp_option options[] = { - {"schedule", 's', "N", 0, - "Display the next N jobs that will be run"}, - {"daemon", 'd', 0, 0, - "Run as a daemon process"}, - {"stdin", 'i', "FORMAT", 0, - "Format of data passed as standard input or file arguments (default guile)"}, - {0, 0, 0, 0, 0} - }; - - static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = "[FILE...]", - .doc = "Run an mcron process according to the specifications in the " - "FILE... (`-' for standard input), or use all the files in " - "~/.config/cron (or the deprecated ~/.cron) with .guile or " - ".vixie extensions." - }; - - SCM config = SCM_EOL; - argp_program_version = PACKAGE_STRING; - argp_program_bug_address = PACKAGE_BUGREPORT; - argp_parse (&argp, argc, argv, 0, NULL, &config); - - return config; -} - -static error_t -parse_opt (int key, char *arg, struct argp_state *state) -{ - SCM *config = state->input; - - switch (key) - { - case 's': - assq_symbol_set_x (config, "schedule", - scm_from_int (atoi (arg))); - break; - case 'd': - assq_symbol_set_x (config, "daemon", SCM_BOOL_T); - break; - case 'i': - if (strncmp (arg, "vixie", 6) == 0) - assq_symbol_set_x (config, "vixie", SCM_BOOL_T); - break; - case ARGP_KEY_NO_ARGS: - assq_symbol_set_x (config, "files", SCM_EOL); - break; - case ARGP_KEY_ARGS: - { - SCM lst = SCM_EOL; - int filec = state->argc - state->next; - char **filev = state->argv + state->next; - - for (int i = filec - 1; i >= 0; i--) - lst = scm_cons (scm_from_locale_string (filev[i]), lst); - - assq_symbol_set_x (config, "files", lst); - break; - } - case ARGP_KEY_ARG: - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} diff --git a/src/mcron.in b/src/mcron.in new file mode 100644 index 0000000..4519ad0 --- /dev/null +++ b/src/mcron.in @@ -0,0 +1,8 @@ +#!%GUILE% --no-auto-compile +-*- scheme -*- +!# + +(set! %load-path (cons "%modsrcdir%" %load-path)) +(set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path)) +(use-modules (mcron scripts mcron)) +(main) diff --git a/src/mcron/base.scm b/src/mcron/base.scm index edcf1bc..f7b727d 100644 --- a/src/mcron/base.scm +++ b/src/mcron/base.scm @@ -139,7 +139,7 @@ entries that are to run at this time. When SCHEDULE is empty next time is (define* (display-schedule count #:optional (port (current-output-port)) #:key (schedule %global-schedule)) "Display on PORT a textual list of the next COUNT jobs to run. This -simulates the run of the job loop to display the resquested information. +simulates the run of the job loop to display the requested information. Since calling this procedure has the effect of mutating the job timings, the program must exit after. Otherwise the internal data state will be left unusable." diff --git a/src/mcron/job-specifier.scm b/src/mcron/job-specifier.scm index 120bf99..7eb2304 100644 --- a/src/mcron/job-specifier.scm +++ b/src/mcron/job-specifier.scm @@ -202,14 +202,14 @@ go into the list. For example, (range 1 6 2) returns '(1 3 5)." ;; 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 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 @@ -229,7 +229,8 @@ go into the list. For example, (range 1 6 2) returns '(1 3 5)." (cond ((procedure? time-proc) time-proc) ((string? time-proc) (parse-vixie-time time-proc)) ((list? time-proc) (lambda (current-time) - (primitive-eval time-proc))) + (eval time-proc + (resolve-module '(mcron job-specifier))))) (else (throw 'mcron-error 3 "job: invalid first argument (next-time-function; " diff --git a/src/mcron/scripts/cron.scm b/src/mcron/scripts/cron.scm index 1a97fdf..25c8a1a 100644 --- a/src/mcron/scripts/cron.scm +++ b/src/mcron/scripts/cron.scm @@ -17,6 +17,7 @@ ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Mcron. If not, see . + (define-module (mcron scripts cron) #:use-module (ice-9 getopt-long) #:use-module (ice-9 ftw) @@ -28,6 +29,8 @@ #: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, @@ -41,12 +44,15 @@ reading all the information in the users' crontabs and in /etc/crontab. (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 %options `((schedule (single-char #\s) (value #t) + (predicate ,string->number)) + (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 @@ -60,6 +66,8 @@ received." 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 @@ -74,6 +82,8 @@ crontab. This requires that command-type is 'cron." (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 @@ -103,9 +113,6 @@ operation. The permissions on the /var/cron/tabs directory enforce this." (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 @@ -138,42 +145,53 @@ option.\n") (let ((opts (getopt-long 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-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))))))))) + (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) + => (λ (count) + (display-schedule (max 1 (string->number count))) + (exit 0))))))) + + ;; Daemonize ourself. + (unless (eq? 0 (primitive-fork)) (exit 0)) + (setsid) + + ;; Set up process signal handlers, as signals are the only way to terminate + ;; the daemon and we MUST be graceful in defeat. + (for-each (λ (x) (sigaction x + (λ (sig) (catch #t + (λ () + (delete-file config-pid-file) + (delete-file config-socket-file)) + noop) + (exit EXIT_FAILURE)))) + '(SIGTERM SIGINT SIGQUIT SIGHUP)) + + ;; We can now write the PID file. + (with-output-to-file config-pid-file + (λ () (display (getpid)) (newline))) + + ;; 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 index 902d3fc..480eadc 100644 --- a/src/mcron/scripts/crontab.scm +++ b/src/mcron/scripts/crontab.scm @@ -25,13 +25,13 @@ #:use-module (mcron vixie-specification) #:export (main)) -(define* (show-help) +(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") + -r (delete user's crontab)") (newline) (show-package-information)) diff --git a/src/mcron/scripts/mcron.scm b/src/mcron/scripts/mcron.scm index 8ae61cf..0da1cdf 100644 --- a/src/mcron/scripts/mcron.scm +++ b/src/mcron/scripts/mcron.scm @@ -19,14 +19,40 @@ (define-module (mcron scripts mcron) #:use-module (ice-9 ftw) + #:use-module (ice-9 getopt-long) + #:use-module (ice-9 local-eval) #:use-module (ice-9 rdelim) #:use-module (mcron base) #:use-module (mcron config) - #:use-module (mcron job-specifier) ;for user/system files + #: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 [OPTION...] [FILE...] +Run an mcron process according to the specifications in the FILE... (`-' for +standard input), or use all the files in ~/.config/cron (or the deprecated +~/.cron) with .guile or .vixie extensions. + + -d, --daemon Run as a daemon process + -i, --stdin=(guile|vixie) Format of data passed as standard input or file + arguments (default guile) + -s, --schedule[=N] Display the next N (or 8) jobs that will be run + -?, --help Give this help list + -V, --version Print program version + +Mandatory or optional arguments to long options are also mandatory or optional +for any corresponding short options. + +Report bugs to bug-mcron@gnu.org. + +")) + + + (define process-user-file (let ((guile-regexp (make-regexp "\\.gui(le)?$")) (vixie-regexp (make-regexp "\\.vix(ie)?$"))) @@ -35,15 +61,17 @@ 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)) + (if (string=? input "vixie") + (read-vixie-port (current-input-port)) (eval-string (read-string) (resolve-module '(mcron job-specifier))))) ((or guile-syntax? (regexp-exec guile-regexp file-name)) (eval-string (read-delimited "" (open-input-file file-name)) (resolve-module '(mcron job-specifier)))) ((regexp-exec vixie-regexp file-name) - (read-vixie-file 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 @@ -67,6 +95,8 @@ $XDG_CONFIG_HOME is not defined uses ~/.config/cron instead)." (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) @@ -77,30 +107,47 @@ $XDG_CONFIG_HOME is not defined uses ~/.config/cron instead)." ;;; Entry point. ;;; -(define* (main #:optional (opts '())) - (when config-debug - (debug-enable 'backtrace)) +(define (main) - (%process-files (or (assq-ref opts 'files) '()) - (if (assq-ref opts 'vixie) "vixie" "guile")) + (let ((options + (getopt-long + (command-line) + `((daemon (single-char #\d) (value #f)) + (stdin (single-char #\i) (value #t) + (predicate ,(λ (in) (or (string=? in "guile") + (string=? in "vixie"))))) + (schedule (single-char #\s) (value optional) + (predicate ,string->number)) + (help (single-char #\?)) + (version (single-char #\V)))))) - (cond ((assq-ref opts 'schedule) ;display jobs schedule - => (λ (count) - (display-schedule (max 1 count)) - (exit 0))) - ((assq-ref opts 'daemon) ;run mcron as a daemon - (case (primitive-fork) - ((0) (setsid)) - (else (exit 0))))) + (cond ((option-ref options 'help #f) (show-help) (exit 0)) + ((option-ref options 'version #f) (show-version "mcron") (exit 0))) + + (when config-debug + (debug-enable 'backtrace)) - ;; 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)))))) + (%process-files (option-ref options '() '()) + (option-ref options 'stdin "guile")) + + (cond ((option-ref options 'schedule #f) + => (λ (count) + (let ((c (if (string? count) (string->number count) 8))) + (display-schedule (if (exact-integer? c) (max 1 c) 8))) + (exit 0))) + ((option-ref options 'daemon #f) + (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/utils.c b/src/utils.c deleted file mode 100644 index 00a4150..0000000 --- a/src/utils.c +++ /dev/null @@ -1,48 +0,0 @@ -/* utils.c -- Utility functions. - Copyright © 2017 Mathieu Lirzin - - 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 . */ - -#include "utils.h" -#include -#include - -void -wrap_env_path (const char *envar, const char *dir) -{ - const char *path = getenv (envar); - if (path == NULL) - setenv (envar, dir, true); - else - { - char *new_path; - int ret = asprintf (&new_path, "%s:%s", dir, path); - if (ret >= 0) - setenv (envar, new_path, true); - else - { - perror (envar); - exit (EXIT_FAILURE); - } - free (new_path); - } -} - -void -assq_symbol_set_x (SCM *alst, const char *symbol, SCM val) -{ - *alst = scm_assq_set_x (*alst, scm_from_utf8_symbol (symbol), val); -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 910483f..0000000 --- a/src/utils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* utils.h -- Utility functions. - Copyright © 2017 Mathieu Lirzin - - 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 . */ - -#ifndef MCRON_UTILS_H -#define MCRON_UTILS_H - -#include - -/** - Append DIR in front of ENVAR environment variable value. If ENVAR is not - defined, then define it with DIR. Bail out if something went wrong. */ -extern void wrap_env_path (const char *envar, const char *dir); - -/** - In the association list pointed by ALST, set SYMBOL to VAL. */ -extern void assq_symbol_set_x (SCM *alst, const char *symbol, SCM val); - -#endif /* MCRON_UTILS_H */ diff --git a/tests/schedule-2.sh b/tests/schedule-2.sh index 7039123..531af54 100644 --- a/tests/schedule-2.sh +++ b/tests/schedule-2.sh @@ -26,13 +26,13 @@ export SOURCE_DATE_EPOCH TZ=UTC0 export TZ -LC_ALL=C -export LC_ALL - # Use current working directory to store mcron files XDG_CONFIG_HOME=`pwd` export XDG_CONFIG_HOME +LC_ALL=C +export LC_ALL + mkdir cron cat > cron/foo.guile < output-A -diff expected output-A \ +mcron -s cron/foo.guile > output +diff expected output \ || skip_ 'The -s option is not fully functional; -this will be fixed with a future version of GNU Guile and then -a future version of GNU Mcron.' +this will be fixed with a future version of GNU Guile.' Exit 0 From b596461e4229784f2a34992d183158c50e7d5192 Mon Sep 17 00:00:00 2001 From: atsb Date: Wed, 22 Apr 2020 20:42:17 +0200 Subject: [PATCH 08/21] push new NEWS file --- NEWS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NEWS b/NEWS index f8d0d73..c5dc2b9 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,14 @@ GNU Mcron NEWS -*- outline -*- +* Noteworthy changes in release 1.2.0 (2020-04-22) [stable] + +** Improvements + C code removed, mcron becomes 100% Guile. + Make doc/mcron.texi gender neutral. + Have src/mcron/scripts/mcron.scm (process-user-file): use read and eval + instead of load. + New tests added for extra checks. + * Noteworthy changes in release 1.1.4 (2020-04-12) [stable] ** Improvements From 42fae5880eb53b48a2ea9bb4c8d07bbe23201ec9 Mon Sep 17 00:00:00 2001 From: atsb Date: Wed, 22 Apr 2020 20:45:35 +0200 Subject: [PATCH 09/21] prepare version 1.2.0 --- build-aux/guix.scm | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-aux/guix.scm b/build-aux/guix.scm index 3c57bf1..25a281f 100644 --- a/build-aux/guix.scm +++ b/build-aux/guix.scm @@ -41,7 +41,7 @@ (package (inherit (specification->package "mcron")) - (version "1.1.4+") + (version "1.2.0") (source (local-file (dirname %srcdir) #:recursive? #t #:select? keep-mcron-file?)) (inputs diff --git a/configure.ac b/configure.ac index fb19229..f92619b 100755 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ # along with GNU Mcron. If not, see . AC_PREREQ(2.61) -AC_INIT([GNU Mcron], [1.1.4+], [bug-mcron@gnu.org]) +AC_INIT([GNU Mcron], [1.2.0], [bug-mcron@gnu.org]) AC_CONFIG_SRCDIR([src/mcron/scripts/mcron.scm]) AC_CONFIG_AUX_DIR([build-aux]) AC_REQUIRE_AUX_FILE([test-driver.scm]) From b0151cad38a7f71b5888f393db9b7d2673b67b52 Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Fri, 8 May 2020 17:19:28 +0200 Subject: [PATCH 10/21] build: Handle missing "bin" directory This fixes the generation of scripts when "bin" directory does not exist. * Makefile.am (bin/%): Invoke $(MKDIR_P) first. --- Makefile.am | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index e8fe80c..f63ff8f 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. # Copyright © 2003 Dale Mellor -# Copyright © 2015, 2016, 2017, 2018 Mathieu Lirzin +# Copyright © 2015, 2016, 2017, 2018, 2020 Mathieu Lirzin # # This file is part of GNU Mcron. # @@ -100,7 +100,8 @@ DISTCLEANFILES = src/mcron/config.scm bin/% : src/%.in Makefile - -@sed -e 's,%PREFIX%,${prefix},g' \ + $(AM_V_GEN)$(MKDIR_P) bin ; \ + sed -e 's,%PREFIX%,${prefix},g' \ -e 's,%modsrcdir%,${guilesitedir},g' \ -e 's,%modbuilddir%,${guilesitegodir},g' \ -e 's,%localstatedir%,${localstatedir},g' \ @@ -112,8 +113,8 @@ bin/% : src/%.in Makefile -e 's,%PACKAGE_NAME%,@PACKAGE_NAME@,g' \ -e 's,%PACKAGE_URL%,@PACKAGE_URL@,g' \ -e 's,%GUILE%,$(GUILE),g' \ - $< > $@; - -@chmod a+x $@ + $< > $@ ; \ + chmod a+x $@ ## ------------ ## From 39857ae8442853e4f7d89116da400ca24d58ba7b Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Fri, 8 May 2020 17:20:06 +0200 Subject: [PATCH 11/21] build: Distribute script source files This allows 'make distcheck' to succeed. * Makefile.am (EXTRA_DIST): Add script source files. --- Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.am b/Makefile.am index f63ff8f..b5c6260 100755 --- a/Makefile.am +++ b/Makefile.am @@ -148,6 +148,9 @@ EXTRA_DIST = \ bootstrap \ build-aux/guix.scm \ HACKING \ + src/cron.in \ + src/crontab.in \ + src/mcron.in \ tests/init.sh \ $(TESTS) From 9781507defd8ddd652653386eddbc5fc371e4742 Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Fri, 8 May 2020 18:43:41 +0200 Subject: [PATCH 12/21] build: Handle missing "bin" directory This fixes the generation of scripts when "bin" directory does not exist. * Makefile.am (bin/%): Invoke $(MKDIR_P) first. --- Makefile.am | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index e8fe80c..f63ff8f 100755 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in. # Copyright © 2003 Dale Mellor -# Copyright © 2015, 2016, 2017, 2018 Mathieu Lirzin +# Copyright © 2015, 2016, 2017, 2018, 2020 Mathieu Lirzin # # This file is part of GNU Mcron. # @@ -100,7 +100,8 @@ DISTCLEANFILES = src/mcron/config.scm bin/% : src/%.in Makefile - -@sed -e 's,%PREFIX%,${prefix},g' \ + $(AM_V_GEN)$(MKDIR_P) bin ; \ + sed -e 's,%PREFIX%,${prefix},g' \ -e 's,%modsrcdir%,${guilesitedir},g' \ -e 's,%modbuilddir%,${guilesitegodir},g' \ -e 's,%localstatedir%,${localstatedir},g' \ @@ -112,8 +113,8 @@ bin/% : src/%.in Makefile -e 's,%PACKAGE_NAME%,@PACKAGE_NAME@,g' \ -e 's,%PACKAGE_URL%,@PACKAGE_URL@,g' \ -e 's,%GUILE%,$(GUILE),g' \ - $< > $@; - -@chmod a+x $@ + $< > $@ ; \ + chmod a+x $@ ## ------------ ## From bc18db895064d9d0516b784ecac5270282c21d30 Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Fri, 8 May 2020 18:43:42 +0200 Subject: [PATCH 13/21] build: Distribute script source files This allows 'make distcheck' to succeed. * Makefile.am (EXTRA_DIST): Add script source files. --- Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.am b/Makefile.am index f63ff8f..b5c6260 100755 --- a/Makefile.am +++ b/Makefile.am @@ -148,6 +148,9 @@ EXTRA_DIST = \ bootstrap \ build-aux/guix.scm \ HACKING \ + src/cron.in \ + src/crontab.in \ + src/mcron.in \ tests/init.sh \ $(TESTS) From 6ae322468832759c1950779d6faff0fa550580ce Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Fri, 8 May 2020 18:06:20 +0200 Subject: [PATCH 14/21] scripts: Separate build/install directory context This prevents installed modules to interfere with the ones from the build directory. * src/cron.in: Augment Guile load paths with install directories only when MCRON_UNINSTALLED environment variable is not set. * src/crontab.in: Likewise. * src/mcron.in: Likewise. --- src/cron.in | 8 +++++--- src/crontab.in | 6 ++++-- src/mcron.in | 8 +++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/cron.in b/src/cron.in index 97d49b4..25ad273 100644 --- a/src/cron.in +++ b/src/cron.in @@ -2,7 +2,9 @@ -*- scheme -*- !# -(set! %load-path (cons "%modsrcdir%" %load-path)) -(set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path)) -(use-modules (mcron scripts cron)) +(unless (getenv "MCRON_UNINSTALLED") + (set! %load-path (cons "%modsrcdir%" %load-path)) + (set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path))) + +(use-modules (mcron scripts cron)) (main) diff --git a/src/crontab.in b/src/crontab.in index fa31878..dad0dd2 100644 --- a/src/crontab.in +++ b/src/crontab.in @@ -2,6 +2,8 @@ -*- scheme -*- !# -(set! %load-path (cons "%modsrcdir%" %load-path)) -(set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path)) +(unless (getenv "MCRON_UNINSTALLED") + (set! %load-path (cons "%modsrcdir%" %load-path)) + (set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path))) + ((@ (mcron scripts crontab) main)) diff --git a/src/mcron.in b/src/mcron.in index 4519ad0..268743c 100644 --- a/src/mcron.in +++ b/src/mcron.in @@ -2,7 +2,9 @@ -*- scheme -*- !# -(set! %load-path (cons "%modsrcdir%" %load-path)) -(set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path)) -(use-modules (mcron scripts mcron)) +(unless (getenv "MCRON_UNINSTALLED") + (set! %load-path (cons "%modsrcdir%" %load-path)) + (set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path))) + +(use-modules (mcron scripts mcron)) (main) From bfe2a8921226cc7ccb54357d4f0c07534b763671 Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Fri, 8 May 2020 19:24:00 +0200 Subject: [PATCH 15/21] build: Detect guile M4 macro expansion errors This ensures that the absence of 'pkg-config' or 'guile' M4 macros expansion do not pass the bootstrap step. * configure.ac: Allow or forbid some M4 macros patterns in the generated 'configure' script. --- configure.ac | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f92619b..d27e12e 100755 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ ## Process this file with autoconf to produce a configure script. # # Copyright © 2003, 2005, 2012, 2014 Dale Mellor -# Copyright © 2015, 2016, 2017, 2018 Mathieu Lirzin +# Copyright © 2015, 2016, 2017, 2018, 2020 Mathieu Lirzin # Copyright © 2018 宋文武 # # This file is part of GNU Mcron. @@ -33,6 +33,13 @@ AM_SILENT_RULES([yes]) # Enables silent rules by default. AC_CANONICAL_HOST +dnl We require pkg.m4 (from pkg-config) and guile.m4 (from Guile.) +dnl Make sure they are available when generating the configure script. +m4_pattern_forbid([^PKG_PROG]) +m4_pattern_forbid([^PKG_CHECK]) +m4_pattern_forbid([^GUILE_P]) +m4_pattern_allow([^GUILE_PKG_ERRORS]) + # Check for Guile development files. GUILE_PKG([3.0 2.2 2.0]) From 289e4c505e52402e5dfba08d57f2ba1d61a90bf3 Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Sat, 9 May 2020 00:30:23 +0200 Subject: [PATCH 16/21] build: Remove C specific Guile configuration step * configure.ac: Remove unecessary PKG_CHECK_MODULES invocation. --- configure.ac | 5 ----- 1 file changed, 5 deletions(-) diff --git a/configure.ac b/configure.ac index d27e12e..97e9cff 100755 --- a/configure.ac +++ b/configure.ac @@ -43,12 +43,7 @@ m4_pattern_allow([^GUILE_PKG_ERRORS]) # Check for Guile development files. GUILE_PKG([3.0 2.2 2.0]) -# Set Guile flags without using GUILE_FLAGS which is requiring the unused -# 'config.rpath' script. -PKG_CHECK_MODULES(GUILE, [guile-$GUILE_EFFECTIVE_VERSION]) - # Checks for programs. - GUILE_PROGS AM_MISSING_PROG(HELP2MAN, help2man, $missing_dir) From 6a9bfcea4080390186be859c721e35f438d1272d Mon Sep 17 00:00:00 2001 From: Dale Mellor Date: Fri, 8 May 2020 20:43:50 +0100 Subject: [PATCH 17/21] Using new Guile command-line-processor. --- src/cron.in | 47 ++++++++++++- src/crontab.in | 38 ++++++++++- src/mcron.in | 50 +++++++++++++- src/mcron/scripts/cron.scm | 51 +++----------- src/mcron/scripts/crontab.scm | 121 +++++++++++++--------------------- src/mcron/scripts/mcron.scm | 66 ++++--------------- 6 files changed, 195 insertions(+), 178 deletions(-) diff --git a/src/cron.in b/src/cron.in index 25ad273..c22b701 100644 --- a/src/cron.in +++ b/src/cron.in @@ -2,9 +2,52 @@ -*- scheme -*- !# +;;;; cron -- run jobs at scheduled times +;;; Copyright © 2003, 2012, 2020 Dale Mellor +;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin +;;; +;;; 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 . + + (unless (getenv "MCRON_UNINSTALLED") (set! %load-path (cons "%modsrcdir%" %load-path)) (set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path))) -(use-modules (mcron scripts cron)) -(main) +(use-modules (mcron scripts cron) + (ice-9 command-line-processor)) + +(process-command-line (command-line) + application "cron" + version "%VERSION%" + usage "[OPTIONS]" + help-preamble + "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." + option (--schedule=8 -s string->number + "display the next N (or 8) jobs that will be" + "run, and exit") + option (--noetc -n "do not check /etc/crontab for updates (use" + "of this option is HIGHLY RECOMMENDED)") + help-postamble + "Mandatory or optional arguments to long options are also mandatory or " + "optional for any corresponding short options." + bug-address "%PACKAGE_BUGREPORT%" + copyright + "2003, 2012, 2015, 2016, 2018, 2020 Free Software Foundation, Inc." + license GPLv3) + + +(main --schedule --noetc) diff --git a/src/crontab.in b/src/crontab.in index dad0dd2..f203a98 100644 --- a/src/crontab.in +++ b/src/crontab.in @@ -2,8 +2,44 @@ -*- scheme -*- !# +;;;; crontab -- run jobs at scheduled times +;;; Copyright © 2003, 2020 Dale Mellor +;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin +;;; +;;; 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 . + + (unless (getenv "MCRON_UNINSTALLED") (set! %load-path (cons "%modsrcdir%" %load-path)) (set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path))) -((@ (mcron scripts crontab) main)) +(use-modules (mcron scripts crontab) + (ice-9 command-line-processor)) + +(process-command-line (command-line) + application "crontab" + version "%VERSION%" + usage "[-u user] { -e | -l | -r }" + help-preamble "the default operation is to replace, per 1003.2" + option (--user= -u "the user whose files are to be manipulated") + option (--edit -e "edit this userʼs crontab") + option (--list -l "list this userʼs crontab") + option (--remove -r "delete the userʼs crontab") + bug-address "%PACKAGE_BUGREPORT%" + copyright "2003, 2016, 2020 Free Software Foundation, Inc." + license GPLv3) + +((@ (mcron scripts crontab) main) --user --edit --list --remove --!) diff --git a/src/mcron.in b/src/mcron.in index 268743c..43dc326 100644 --- a/src/mcron.in +++ b/src/mcron.in @@ -2,9 +2,55 @@ -*- scheme -*- !# +;;;; mcron -- run jobs at scheduled times +;;; Copyright © 2003, 2012, 2020 Dale Mellor +;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin +;;; +;;; 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 . + + (unless (getenv "MCRON_UNINSTALLED") (set! %load-path (cons "%modsrcdir%" %load-path)) (set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path))) -(use-modules (mcron scripts mcron)) -(main) +(use-modules (mcron scripts mcron) + (ice-9 command-line-processor)) + +(process-command-line (command-line) + application "mcron" + version "%VERSION%" + usage "[OPTIONS ...] [FILES ...]" + help-preamble + "Run unattended jobs according to instructions in the FILES... " + "(`-' for standard input), or use all the files in ~/.config/cron " + "(or the deprecated ~/.cron) with .guile or .vixie extensions.\n" + "Note that --daemon and --schedule are mutually exclusive." + option (--daemon -d "run as a daemon process") + option (--schedule=8 -s string->number + "display the next N (or 8) jobs that will be run," + "and then exit") + option (--stdin=guile short-i (λ (in) (or (string=? in "guile") + (string=? in "vixie"))) + "format of data passed as standard input or file " + "arguments, 'guile' or 'vixie' (default guile)") + help-postamble + "Mandatory or optional arguments to long options are also mandatory or " + "optional for any corresponding short options." + bug-address "%PACKAGE_BUGREPORT%" + copyright "2003, 2006, 2014, 2020 Free Software Foundation, Inc." + license GPLv3) + +(main --daemon --schedule --stdin --!) diff --git a/src/mcron/scripts/cron.scm b/src/mcron/scripts/cron.scm index 25c8a1a..b8463f6 100644 --- a/src/mcron/scripts/cron.scm +++ b/src/mcron/scripts/cron.scm @@ -19,7 +19,6 @@ (define-module (mcron scripts cron) - #:use-module (ice-9 getopt-long) #:use-module (ice-9 ftw) #:use-module (mcron base) #:use-module (mcron config) @@ -31,29 +30,6 @@ -(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 ,string->number)) - (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 @@ -107,10 +83,7 @@ operation. The permissions on the /var/cron/tabs directory enforce this." (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)) +(define (%process-files noetc?) ;; Clear MAILTO so that outputs are sent to the various users. (setenv "MAILTO" #f) ;; Having defined all the necessary procedures for scanning various sets of @@ -141,17 +114,10 @@ option.\n") ;;; Entry point. ;;; -(define* (main #:optional (args (command-line))) - (let ((opts (getopt-long 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))) +(define (main --schedule --noetc) + (when config-debug (debug-enable 'backtrace)) + + (cond ((not (zero? (getuid))) (mcron-error 16 "This program must be run by the root user (and should" " have been installed as such).")) @@ -161,12 +127,11 @@ option.\n") " 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) + (cond (--schedule => (λ (count) (display-schedule (max 1 (string->number count))) - (exit 0))))))) + (exit 0)))) + (%process-files --noetc))) ;; Daemonize ourself. (unless (eq? 0 (primitive-fork)) (exit 0)) diff --git a/src/mcron/scripts/crontab.scm b/src/mcron/scripts/crontab.scm index 480eadc..0c5c47f 100644 --- a/src/mcron/scripts/crontab.scm +++ b/src/mcron/scripts/crontab.scm @@ -1,5 +1,5 @@ ;;;; crontab -- edit user's cron tabs -;;; Copyright © 2003, 2004 Dale Mellor +;;; Copyright © 2003, 2004 Dale Mellor <> ;;; Copyright © 2016 Mathieu Lirzin ;;; ;;; This file is part of GNU Mcron. @@ -18,31 +18,12 @@ ;;; along with GNU Mcron. If not, see . (define-module (mcron scripts crontab) - #:use-module (ice-9 getopt-long) #: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)))) - (define (hit-server user-name) "Tell the running cron daemon that the user corresponding to USER-NAME has modified his crontab. USER-NAME is written to the @@ -56,6 +37,25 @@ USER-NAME has modified his crontab. USER-NAME is written to the (lambda (key . args) (display "Warning: a cron daemon is not running.\n")))) + + +;; 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. +(define (get-yes-no prompt . re-prompt) + (unless (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)))) + + + (define (in-access-file? file name) "Scan FILE which should contain one user name per line (such as '/var/cron/allow' and '/var/cron/deny'). Return #t if NAME is in there, and @@ -78,60 +78,34 @@ USER-NAME has modified his crontab. USER-NAME is written to the ;;; Entry point. ;;; -(define* (main #:optional (args (command-line))) - (let ((opts (getopt-long 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 ((crontab-real-user - ;; This program should have been installed SUID root. Here we get - ;; the passwd entry for the real user who is running this program. - (passwd:name (getpw (getuid))))) +(define (main --user --edit --list --remove files) + (when config-debug (debug-enable 'backtrace)) + (let ((crontab-real-user + ;; This program should have been installed SUID root. Here we get + ;; the passwd entry for the real user who is running this program. + (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.")) + ;; 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) + ;; Check that no more than one of the mutually exclusive options are + ;; being used. + (when (< 1 (+ (if --edit 1 0) (if --list 1 0) (if --remove 1 0))) (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)) + (when (and (not (zero? (getuid))) --user) (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)) + (crontab-user (or --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)))))) + (crontab-file + (string-append config-spool-dir "/" crontab-user))) ;; 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). @@ -140,7 +114,7 @@ USER-NAME has modified his crontab. USER-NAME is written to the ;; 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) + (--list (catch #t (λ () (with-input-from-file crontab-file @@ -163,7 +137,7 @@ USER-NAME has modified his crontab. USER-NAME is written to 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) + (--edit (let ((temp-file (string-append config-tmp-dir "/crontab." (number->string (getpid))))) @@ -191,12 +165,9 @@ USER-NAME has modified his crontab. USER-NAME is written to the ;; 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)) + (--remove (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 @@ -206,8 +177,8 @@ USER-NAME has modified his crontab. USER-NAME is written to the ;; 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 '() '())))) + ((not (null? files)) + (let ((input-file (car files))) (catch-mcron-error (if (string=? input-file "-") (let ((input-string (read-string))) @@ -222,4 +193,4 @@ USER-NAME has modified his crontab. USER-NAME is written to the ;; 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."))))))) + "usage error: file name must be specified for replace.")))))) diff --git a/src/mcron/scripts/mcron.scm b/src/mcron/scripts/mcron.scm index 0da1cdf..48efae5 100644 --- a/src/mcron/scripts/mcron.scm +++ b/src/mcron/scripts/mcron.scm @@ -1,6 +1,6 @@ ;;;; mcron -- run jobs at scheduled times -;;; Copyright © 2003, 2012 Dale Mellor -;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin +;;; Copyright © 2003, 2012, 2020 Dale Mellor <> +;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin ;;; ;;; This file is part of GNU Mcron. ;;; @@ -19,7 +19,6 @@ (define-module (mcron scripts mcron) #:use-module (ice-9 ftw) - #:use-module (ice-9 getopt-long) #:use-module (ice-9 local-eval) #:use-module (ice-9 rdelim) #:use-module (mcron base) @@ -31,28 +30,6 @@ -(define (show-help) - (display "Usage: mcron [OPTION...] [FILE...] -Run an mcron process according to the specifications in the FILE... (`-' for -standard input), or use all the files in ~/.config/cron (or the deprecated -~/.cron) with .guile or .vixie extensions. - - -d, --daemon Run as a daemon process - -i, --stdin=(guile|vixie) Format of data passed as standard input or file - arguments (default guile) - -s, --schedule[=N] Display the next N (or 8) jobs that will be run - -?, --help Give this help list - -V, --version Print program version - -Mandatory or optional arguments to long options are also mandatory or optional -for any corresponding short options. - -Report bugs to bug-mcron@gnu.org. - -")) - - - (define process-user-file (let ((guile-regexp (make-regexp "\\.gui(le)?$")) (vixie-regexp (make-regexp "\\.vix(ie)?$"))) @@ -107,38 +84,17 @@ $XDG_CONFIG_HOME is not defined uses ~/.config/cron instead)." ;;; Entry point. ;;; -(define (main) +(define (main --daemon --schedule --stdin file-list) - (let ((options - (getopt-long - (command-line) - `((daemon (single-char #\d) (value #f)) - (stdin (single-char #\i) (value #t) - (predicate ,(λ (in) (or (string=? in "guile") - (string=? in "vixie"))))) - (schedule (single-char #\s) (value optional) - (predicate ,string->number)) - (help (single-char #\?)) - (version (single-char #\V)))))) - - (cond ((option-ref options 'help #f) (show-help) (exit 0)) - ((option-ref options 'version #f) (show-version "mcron") (exit 0))) - - (when config-debug - (debug-enable 'backtrace)) - - (%process-files (option-ref options '() '()) - (option-ref options 'stdin "guile")) - - (cond ((option-ref options 'schedule #f) + (when config-debug (debug-enable 'backtrace)) + (%process-files file-list (or --stdin "guile")) + (cond (--schedule => (λ (count) - (let ((c (if (string? count) (string->number count) 8))) - (display-schedule (if (exact-integer? c) (max 1 c) 8))) + (display-schedule + (max 1 (inexact->exact (floor (string->number count))))) (exit 0))) - ((option-ref options 'daemon #f) - (case (primitive-fork) - ((0) (setsid)) - (else (exit 0))))) + (--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 @@ -150,4 +106,4 @@ $XDG_CONFIG_HOME is not defined uses ~/.config/cron instead)." ;; 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))))))) + (process-update-request fdes-list)))))) From d2143dea3f3d933b4c7f19b738827d6ae2bcfe53 Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Mon, 18 May 2020 12:54:50 +0200 Subject: [PATCH 18/21] vixie-time: Remove calls to 'pk' debugging facility * src/mcron/vixie-time.scm (parse-vixie-time): Remove pk usage --- src/mcron/vixie-time.scm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mcron/vixie-time.scm b/src/mcron/vixie-time.scm index 407ff60..0301434 100644 --- a/src/mcron/vixie-time.scm +++ b/src/mcron/vixie-time.scm @@ -1,6 +1,6 @@ ;;;; vixie-time.scm -- parse Vixie-style times ;;; Copyright © 2003 Dale Mellor -;;; Copyright © 2018 Mathieu Lirzin +;;; Copyright © 2018, 2020 Mathieu Lirzin ;;; ;;; This file is part of GNU Mcron. ;;; @@ -360,7 +360,7 @@ accounted for." (time-spec:list wday) (tm:mon time) (tm:year time))))) - (nudge-day! (pk time) (pk (cddr time-spec-list))) + (nudge-day! time (cddr time-spec-list)) (set-tm:hour time -1)) (unless (member (tm:hour time) (time-spec:list hour)) From 92a940cca55a07efb67c7588bbca99a9fe305025 Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Mon, 18 May 2020 12:54:51 +0200 Subject: [PATCH 19/21] tests: Check (mcron vixie-specification) * tests/vixie-specification.scm: New file. * Makefile.am (TESTS): Register it. --- Makefile.am | 1 + tests/vixie-specification.scm | 144 ++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 tests/vixie-specification.scm diff --git a/Makefile.am b/Makefile.am index b5c6260..c7562c5 100755 --- a/Makefile.am +++ b/Makefile.am @@ -138,6 +138,7 @@ TESTS = \ tests/environment.scm \ tests/job-specifier.scm \ tests/utils.scm \ + tests/vixie-specification.scm \ tests/vixie-time.scm ## -------------- ## diff --git a/tests/vixie-specification.scm b/tests/vixie-specification.scm new file mode 100644 index 0000000..78c1dad --- /dev/null +++ b/tests/vixie-specification.scm @@ -0,0 +1,144 @@ +;;;; vixie-specification.scm -- tests for (mcron vixie-specificaion) module +;;; Copyright © 2020 Mathieu Lirzin +;;; +;;; 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 . + +(use-modules (srfi srfi-1) + (srfi srfi-64) + (mcron vixie-specification)) + +(setenv "TZ" "UTC0") + +;;; Do not send mail +(setenv "MAILTO" "") + +(define (create-file! content) + "Construct a temporary file port containing CONTENT which must be a string." + (let ((port (mkstemp! (string-copy "file-XXXXXX")))) + (display content port) + (force-output port) + port)) + +(define (clean-temp port) + "Close and Delete a temporary file port" + (let ((fname (port-filename port))) + (close port) + (delete-file fname))) + +(define schedule (@@ (mcron base) %global-schedule)) +(define schedule-user (@@ (mcron base) schedule-user)) +(define set-schedule-user! (@@ (mcron base) set-schedule-user!)) +(define job:environment (@@ (mcron base) job:environment)) +(define job:displayable (@@ (mcron base) job:displayable)) +(define job:user (@@ (mcron base) job:user)) + +(test-begin "vixie-specification") + +;;; Parse user crontab file + +(define user-crontab-example + "# Example crontab +FOO=x +BAR=y + +# Example of job definitions: +17 * * * * cd / && run baz +47 6 * * 7 foo -x /tmp/example || bar +") + +(define user-crontab (create-file! user-crontab-example)) + +(dynamic-wind + (const #t) + (lambda () + (set-schedule-user! schedule '()) + (read-vixie-file (port-filename user-crontab)) + + (test-assert "User schedule has exactly 2 matching jobs" + (lset= string=? + '("cd / && run baz" + "foo -x /tmp/example || bar") + (map job:displayable (schedule-user schedule)))) + + (test-assert "Job environment matches configuration" + (every (lambda (j) + (lset= equal? + '(("FOO" . "x") ("BAR" . "y")) + (job:environment j))) + (schedule-user schedule)))) + + (lambda () + (clean-temp user-crontab))) + +;;; Parse system crontab file + +;;; Get two existing users from the test environment. +(setpwent) +(define user0 (getpwent)) +(define user1 (or (getpwent) user0)) +(define system-crontab-example + (string-append + "# Example crontab +BAZ=z + +17 * * * * " (passwd:name user0) " cd / && run baz +47 6 * * 7 " (passwd:name user1) " foo -x /tmp/example || bar")) + +(define sys-crontab (create-file! system-crontab-example)) + +(dynamic-wind + (const #t) + (lambda () + (set-schedule-user! schedule '()) + (read-vixie-file (port-filename sys-crontab) parse-system-vixie-line) + + (test-assert "System schedule has exactly 2 matching jobs" + (lset= equal? + `((,user0 . "cd / && run baz") + (,user1 . "foo -x /tmp/example || bar")) + (map (lambda (j) + (cons (job:user j) (job:displayable j))) + (schedule-user schedule)))) + + (test-assert "Job environment matches configuration" + (every (lambda (j) + (lset= equal? '(("BAZ" . "z")) (job:environment j))) + (schedule-user schedule)))) + + (lambda () + (clean-temp sys-crontab))) + +;;; Try to parse a user crontab in a system context + +(define wrong-system-crontab-example + " +# Example of job definitions: +17 * * * * ls") + +(define wrong-sys-crontab (create-file! wrong-system-crontab-example)) + +(dynamic-wind + (const #t) + (lambda () + (test-error "missing user" + 'mcron-error + (read-vixie-file (port-filename wrong-sys-crontab) + parse-system-vixie-line))) + + (lambda () + (clean-temp wrong-sys-crontab))) + +(test-end) From 765bfbf4d9e4cd22371313f653cd6431034798f0 Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Mon, 18 May 2020 12:54:52 +0200 Subject: [PATCH 20/21] tests: Check (mcron redirect) * tests/redirect.scm: New file. * Makefile.am (TESTS): Register it. * src/mcron/redirect.scm (with-mail-out): Adapt to facilitate testing. --- Makefile.am | 1 + src/mcron/redirect.scm | 12 ++++++---- tests/redirect.scm | 53 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 tests/redirect.scm diff --git a/Makefile.am b/Makefile.am index c7562c5..ddfad07 100755 --- a/Makefile.am +++ b/Makefile.am @@ -137,6 +137,7 @@ TESTS = \ tests/base.scm \ tests/environment.scm \ tests/job-specifier.scm \ + tests/redirect.scm \ tests/utils.scm \ tests/vixie-specification.scm \ tests/vixie-time.scm diff --git a/src/mcron/redirect.scm b/src/mcron/redirect.scm index b7df42c..8374552 100644 --- a/src/mcron/redirect.scm +++ b/src/mcron/redirect.scm @@ -1,5 +1,6 @@ ;;;; redirect.scm -- modify job outputs ;;; Copyright © 2003 Dale Mellor +;;; Copyright © 2020 Mathieu Lirzin ;;; Copyright © 2018 宋文武 ;;; ;;; This file is part of GNU Mcron. @@ -63,7 +64,10 @@ ;; the string, and output (including the error output) being sent to a pipe ;; opened on a mail transport. -(define (with-mail-out action . user) +(define* (with-mail-out action #:optional user #:key + (hostname (gethostname)) + (out (lambda () + (open-output-pipe config-sendmail)))) ;; Determine the name of the user who is to recieve the mail, looking for a ;; name in the optional user argument, then in the MAILTO environment @@ -72,7 +76,7 @@ (let* ((mailto (getenv "MAILTO")) (user (cond (mailto mailto) - ((not (null? user)) (car user)) + (user user) (else (getenv "LOGNAME")))) (parent->child (pipe)) (child->parent (pipe)) @@ -173,11 +177,11 @@ (open-output-file "/dev/null") ;; The sendmail command should read ;; recipients from the message header. - (open-output-pipe config-sendmail))) + (out))) (set-current-input-port (car child->parent)) (display "To: ") (display user) (newline) (display "From: mcron") (newline) - (display (string-append "Subject: " user "@" (gethostname))) + (display (string-append "Subject: " user "@" hostname)) (newline) (newline) diff --git a/tests/redirect.scm b/tests/redirect.scm new file mode 100644 index 0000000..700bfb4 --- /dev/null +++ b/tests/redirect.scm @@ -0,0 +1,53 @@ +;;;; redirect.scm -- tests for (mcron redirect) module +;;; Copyright © 2020 Mathieu Lirzin +;;; +;;; 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 . + +(use-modules (ice-9 textual-ports) + (srfi srfi-1) + (srfi srfi-64) + (mcron redirect)) + +(setenv "TZ" "UTC0") + +(test-begin "redirect") + +(define out (mkstemp! (string-copy "foo-XXXXXX"))) + +(dynamic-wind + (const #t) + (lambda () + (with-mail-out "echo 'foo'" "user0" + #:out (lambda () out) + #:hostname "localhost") + + (flush-all-ports) + + (test-equal "mail output" + "To: user0 +From: mcron +Subject: user0@localhost + +foo +" + (call-with-input-file (port-filename out) get-string-all))) + + (lambda () + (let ((fname (port-filename out))) + (close out) + (delete-file fname)))) + +(test-end) From 833ae20c31d9951e7721bb55441df2630aae4765 Mon Sep 17 00:00:00 2001 From: Dale Mellor Date: Mon, 8 Jun 2020 08:54:35 +0100 Subject: [PATCH 21/21] Version to 1.2.0+dmbcs. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 97e9cff..8948c9b 100755 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ # along with GNU Mcron. If not, see . AC_PREREQ(2.61) -AC_INIT([GNU Mcron], [1.2.0], [bug-mcron@gnu.org]) +AC_INIT([GNU Mcron], [1.2.0+dmbcs], [bug-mcron@gnu.org]) AC_CONFIG_SRCDIR([src/mcron/scripts/mcron.scm]) AC_CONFIG_AUX_DIR([build-aux]) AC_REQUIRE_AUX_FILE([test-driver.scm])