AboutSummaryRefsLogTreeCommitDiffStats
path: root/job-specifier.scm
diff options
context:
space:
mode:
Diffstat (limited to 'job-specifier.scm')
-rw-r--r--job-specifier.scm262
1 files changed, 262 insertions, 0 deletions
diff --git a/job-specifier.scm b/job-specifier.scm
new file mode 100644
index 0000000..3bba9b3
--- /dev/null
+++ b/job-specifier.scm
@@ -0,0 +1,262 @@
+;; Copyright (C) 2003 Dale Mellor
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+;; USA.
+
+
+
+;; This module defines all the functions that can be used by scheme mcron
+;; configuration files, namely the procedures for working out next times, the
+;; job procedure for registering new jobs (actually a wrapper around the core
+;; add-job function), and the procedure for declaring environment modifications.
+
+(define-module (mcron job-specifier)
+ #:export (range
+ next-year-from next-year
+ next-month-from next-month
+ next-day-from next-day
+ next-hour-from next-hour
+ next-minute-from next-minute
+ next-second-from next-second
+ set-configuration-user
+ set-configuration-time
+ job
+ find-best-next)
+ #:use-module (mcron core)
+ #:use-module (mcron environment)
+ #:use-module (mcron vixie-time)
+ #:re-export (append-environment-mods))
+
+
+
+;; Function (available to user configuration files) which produces a list of
+;; values from start up to (but not including) end. An optional step may be
+;; supplied, and (if positive) only every step'th value will go into the
+;; list. For example, (range 1 6 2) returns '(1 3 5).
+
+(define (range start end . step)
+ (let ((step (if (or (null? step)
+ (<= (car step) 0))
+ 1
+ (car step))))
+ (let loop ((start start))
+ (if (>= start end) '()
+ (cons start
+ (loop (+ start step)))))))
+
+
+
+;; Internal function (not supposed to be used directly in configuration files;
+;; it is exported from the module for the convenience of other parts of the
+;; mcron implementation) which takes a value and a list of possible next values
+;; (all assumed less than 9999). It returns a pair consisting of the smallest
+;; element of the list, and the smallest element larger than the current
+;; value. If an example of the latter cannot be found, 9999 will be returned.
+
+(define (find-best-next current next-list)
+ (let ((current-best (cons 9999 9999)))
+ (for-each (lambda (allowed-time)
+ (if (< allowed-time (car current-best))
+ (set-car! current-best allowed-time))
+ (if (and (> allowed-time current)
+ (< allowed-time (cdr current-best)))
+ (set-cdr! current-best allowed-time)))
+ next-list)
+ current-best))
+
+
+
+;; Internal function to return the time corresponding to some near future
+;; hour. If hour-list is not supplied, the time returned corresponds to the
+;; start of the next hour of the day.
+;;
+;; If the hour-list is supplied the time returned corresponds to the first hour
+;; of the day in the future which is contained in the list. If all the values in
+;; the list are less than the current hour, then the time returned will
+;; correspond to the first hour in the list *on the following day*.
+;;
+;; ... except that the function is actually generalized to deal with seconds,
+;; minutes, etc., in an obvious way :-)
+;;
+;; Note that value-list always comes from an optional argument to a procedure,
+;; so is wrapped up as the first element of a list (i.e. it is a list inside a
+;; list).
+
+(define (bump-time time value-list component higher-component
+ set-component! set-higher-component!)
+ (if (null? value-list)
+ (set-component! time (+ (component time) 1))
+ (let ((best-next (find-best-next (component time) (car value-list))))
+ (if (eqv? 9999 (cdr best-next))
+ (begin
+ (set-higher-component! time (+ (higher-component time) 1))
+ (set-component! time (car best-next)))
+ (set-component! time (cdr best-next)))))
+ (car (mktime time)))
+
+
+
+
+;; Set of configuration methods which use the above general function to bump
+;; specific components of time to the next legitimate value. In each case, all
+;; the components smaller than that of interest are taken to zero, so that for
+;; example the time of the next year will be the time at which the next year
+;; actually starts.
+
+(define (next-year-from current-time . year-list)
+ (let ((time (localtime current-time)))
+ (set-tm:mon time 0)
+ (set-tm:mday time 1)
+ (set-tm:hour time 0)
+ (set-tm:min time 0)
+ (set-tm:sec time 0)
+ (bump-time time year-list tm:year tm:year set-tm:year set-tm:year)))
+
+(define (next-month-from current-time . month-list)
+ (let ((time (localtime current-time)))
+ (set-tm:mday time 1)
+ (set-tm:hour time 0)
+ (set-tm:min time 0)
+ (set-tm:sec time 0)
+ (bump-time time month-list tm:mon tm:year set-tm:mon set-tm:year)))
+
+(define (next-day-from current-time . day-list)
+ (let ((time (localtime current-time)))
+ (set-tm:hour time 0)
+ (set-tm:min time 0)
+ (set-tm:sec time 0)
+ (bump-time time day-list tm:mday tm:mon set-tm:mday set-tm:mon)))
+
+(define (next-hour-from current-time . hour-list)
+ (let ((time (localtime current-time)))
+ (set-tm:min time 0)
+ (set-tm:sec time 0)
+ (bump-time time hour-list tm:hour tm:mday set-tm:hour set-tm:mday)))
+
+(define (next-minute-from current-time . minute-list)
+ (let ((time (localtime current-time)))
+ (set-tm:sec time 0)
+ (bump-time time minute-list tm:min tm:hour set-tm:min set-tm:hour)))
+
+(define (next-second-from current-time . second-list)
+ (let ((time (localtime current-time)))
+ (bump-time time second-list tm:sec tm:min set-tm:sec set-tm:min)))
+
+
+
+;; The current-action-time is the time a job was last run, the time from which
+;; the next time to run a job must be computed. (When the program is first run,
+;; this time is set to the configuration time so that jobs run from that moment
+;; forwards.) Once we have this, we supply versions of the time computation
+;; commands above which implicitly assume this value.
+
+(define current-action-time 0)
+
+
+
+;; We want to provide functions which take a single optional argument (as well
+;; as implicitly the current action time), but unlike usual scheme behaviour if
+;; the argument is missing we want to act like it is really missing, and if it
+;; is there we want to act like it is a genuine argument, not a list of
+;; optionals.
+
+(define (maybe-args function args)
+ (if (null? args)
+ (function current-action-time)
+ (function current-action-time (car args))))
+
+
+
+;; These are the convenience functions we were striving to define for the
+;; configuration files. They are wrappers for the next-X-from functions above,
+;; but implicitly use the current-action-time for the time argument.
+
+(define (next-year . args) (maybe-args next-year-from args))
+(define (next-month . args) (maybe-args next-month-from args))
+(define (next-day . args) (maybe-args next-day-from args))
+(define (next-hour . args) (maybe-args next-hour-from args))
+(define (next-minute . args) (maybe-args next-minute-from args))
+(define (next-second . args) (maybe-args next-second-from args))
+
+
+
+;; The default user for running jobs is the current one (who invoked this
+;; program). There are exceptions: when cron parses /etc/crontab the user is
+;; specified on each individual line; when cron parses /var/cron/tabs/* the user
+;; is derived from the filename of the crontab. These cases are dealt with by
+;; mutating this variable. Note that the variable is only used at configuration
+;; time; a UID is stored with each job and it is that which takes effect when
+;; the job actually runs.
+
+(define configuration-user (getpw (getuid)))
+(define configuration-time (current-time))
+
+(define (set-configuration-user user)
+ (set! configuration-user (if (or (string? user)
+ (integer? user))
+ (getpw user)
+ user)))
+(define (set-configuration-time time) (set! configuration-time time))
+
+
+
+;; The job function, available to configuration files for adding a job rule to
+;; the system.
+;;
+;; Here we must 'normalize' the next-time-function so that it is always a lambda
+;; function which takes one argument (the last time the job ran) and returns a
+;; single value (the next time the job should run). If the input value is a
+;; string this is parsed as a Vixie-style time specification, and if it is a
+;; list then we arrange to eval it (but note that such lists are expected to
+;; ignore the function parameter - the last run time is always read from the
+;; current-action-time global variable). A similar normalization is applied to
+;; the action.
+;;
+;; Here we also compute the first time that the job is supposed to run, by
+;; finding the next legitimate time from the current configuration time (set
+;; right at the top of this program).
+
+(define (job time-proc action . displayable)
+ (let ((action (cond ((procedure? action) action)
+ ((list? action) (lambda () (primitive-eval action)))
+ ((string? action) (lambda () (system action)))
+ (else
+ (display "job: invalid second argument (action; should be lamdba")
+ (display "function, string or list)\n")
+ (primitive-exit 2))))
+
+ (time-proc
+ (cond ((procedure? time-proc) time-proc)
+ ((string? time-proc) (parse-vixie-time time-proc))
+ ((list? time-proc) (lambda (current-time)
+ (primitive-eval time-proc)))
+ (else
+
+ (display "job: invalid first argument (next-time-function; should ")
+ (display "be function, string or list)")
+ (primitive-exit 3))))
+ (displayable
+ (cond ((not (null? displayable)) (car displayable))
+ ((procedure? action) "Lambda function")
+ ((string? action) action)
+ ((list? action) (with-output-to-string
+ (lambda () (display action)))))))
+ (add-job (lambda (current-time)
+ (set! current-action-time current-time)
+ (time-proc current-time))
+ action
+ displayable
+ configuration-time
+ configuration-user)))