From a0d493fa9be910187a00c6be1ccbf5ee50ffd040 Mon Sep 17 00:00:00 2001 From: Dale Mellor Date: Fri, 8 May 2020 20:43:50 +0100 Subject: Using proposed new Guile command-line-processor. This is a pre-emptive delta which will make use of new facilities in a future Guile for command-line option processing---a fuller description will appear with later patches. * src/{cron,crontab,mcron}.in: use new facility * src/mcron/scripts/{cron,crontab,mcron}.scm: remove old option-scanning code --- src/cron.in | 47 +++++++++++++++- src/crontab.in | 38 ++++++++++++- src/mcron.in | 56 ++++++++++++++++++- src/mcron/scripts/cron.scm | 80 +++++++-------------------- src/mcron/scripts/crontab.scm | 125 ++++++++++++++++-------------------------- src/mcron/scripts/mcron.scm | 75 +++++-------------------- 6 files changed, 215 insertions(+), 206 deletions(-) (limited to 'src') diff --git a/src/cron.in b/src/cron.in index be624e0..fc4725e 100644 --- a/src/cron.in +++ b/src/cron.in @@ -2,10 +2,51 @@ -*- scheme -*- !# +;;;; cron -- run jobs at scheduled times +;;; Copyright © 2003, 2012, 2020, 2022 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)) -(use-modules (mcron utils)) -(catch-mcron-error (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 - 2022 Free Software Foundation, Inc." + license GPLv3) + + +(catch-mcron-error (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 07023f9..63b4814 100644 --- a/src/mcron.in +++ b/src/mcron.in @@ -2,10 +2,60 @@ -*- 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) - (mcron utils)) -(catch-mcron-error (main)) +(use-modules (mcron scripts mcron) + (mcron utils) + (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)") + option (--log -l "write log messages to standard output") + option (--log-format "(ice-9 format) format string for log messages") + option (--date-format "(srfi srfi-19) date format for log messages") + 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 - 2022 Free Software Foundation, Inc." + license GPLv3) + +(catch-mcron-error (main --daemon --schedule --stdin --log --log-format + --date-format --!)) diff --git a/src/mcron/scripts/cron.scm b/src/mcron/scripts/cron.scm index a0c9a68..0a3b8b2 100644 --- a/src/mcron/scripts/cron.scm +++ b/src/mcron/scripts/cron.scm @@ -20,7 +20,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) @@ -32,33 +31,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). - --log-format=FMT (ice-9 format) format string for log messages - --date-format=FMT (srfi srfi-19) date format string for log messages") - (newline) - (show-package-information)) - - - -(define %options `((schedule (single-char #\s) (value #t) - (predicate ,string->number)) - (noetc (single-char #\n) (value #f)) - (log-format (value #t) (predicate ,validate-log-format)) - (date-format (value #t) (predicate ,validate-date-format)) - (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 @@ -112,10 +84,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 @@ -146,35 +115,24 @@ option.\n") ;;; Entry point. ;;; -(define* (main #:optional (args (command-line))) - - (define 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) - => (λ (count) - (display-schedule (max 1 (string->number count))) - (exit 0)))))) +(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).")) + ((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 + (cond (--schedule + => (λ (count) + (display-schedule (max 1 (string->number count))) + (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))))) - - ;; 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) +(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.")) + + ;; 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 11c0e34..39733f8 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 ;;; Copyright © 2021 Maxim Cournoyer ;;; ;;; This file is part of GNU Mcron. @@ -20,7 +20,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) @@ -32,31 +31,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 - (default guile) - -s, --schedule[=N] Display the next N (or 8) jobs that will be run - -l, --log Write log messages to standard output - --log-format=FMT (ice-9 format) format string for log messages - --date-format=FMT (srfi srfi-19) date format string for log messages - -?, --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)?$"))) @@ -112,44 +86,23 @@ directory. Double-check the folder and file permissions and syntax.")))) ;;; Entry point. ;;; -(define (main) +(define (main --daemon --schedule --stdin --log --log-format --date-format + 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)) - (log (single-char #\l) (value #f)) - (log-format (value #t) (predicate ,validate-log-format)) - (date-format (value #t) (predicate ,validate-date-format)) - (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) + (%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))))) (parameterize - ((%do-logging (option-ref options 'log (%do-logging))) - (%log-format (option-ref options 'log-format (%log-format))) - (%date-format (option-ref options 'date-format (%date-format)))) - (run-job-loop)))) + ((%do-logging --log) + (%log-format (or --log-format (%log-format))) + (%date-format (or --date-format (%date-format)))) + (catch-mcron-error (run-job-loop)))) -- cgit v1.2.3