192 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			Scheme
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			Scheme
		
	
	
	
	
	
;;   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 file provides methods for reading a complete Vixie-style configuration
 | 
						|
;; file, either from a real file or an already opened port. It also exposes the
 | 
						|
;; method for parsing the time-specification part of a Vixie string, so that
 | 
						|
;; these can be used to form the next-time-function of a job in a Guile
 | 
						|
;; configuration file.
 | 
						|
 | 
						|
(define-module (mcron vixie-specification)
 | 
						|
  #:export (parse-user-vixie-line
 | 
						|
            parse-system-vixie-line
 | 
						|
            read-vixie-port
 | 
						|
            read-vixie-file
 | 
						|
            check-system-crontab)
 | 
						|
  #:use-module ((mcron config) :select (config-socket-file))
 | 
						|
  #:use-module (mcron core)
 | 
						|
  #:use-module (mcron job-specifier)
 | 
						|
  #:use-module (mcron redirect)
 | 
						|
  #:use-module (mcron vixie-time))
 | 
						|
 | 
						|
 | 
						|
(use-modules (ice-9 regex) (ice-9 rdelim)
 | 
						|
             (srfi srfi-1) (srfi srfi-2) (srfi srfi-13) (srfi srfi-14))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
;; A line in a Vixie-style crontab file which gives a command specification
 | 
						|
;; carries two pieces of information: a time specification consisting of five
 | 
						|
;; space-separated items, and a command which is also separated from the time
 | 
						|
;; specification by a space. The line is broken into the two components, and the
 | 
						|
;; job procedure run to add the two pieces of information to the job list (this
 | 
						|
;; will in turn use the above function to turn the time specification into a
 | 
						|
;; function for computing future run times of the command).
 | 
						|
 | 
						|
(define parse-user-vixie-line-regexp
 | 
						|
  (make-regexp "^[[:space:]]*(([^[:space:]]+[[:space:]]+){5})(.*)$"))
 | 
						|
 | 
						|
(define (parse-user-vixie-line line)
 | 
						|
  (let ((match (regexp-exec parse-user-vixie-line-regexp line)))
 | 
						|
    (if (not match) (begin (display "Bad job line in Vixie file.\n")
 | 
						|
                           (primitive-exit 10)))
 | 
						|
    (job (match:substring match 1)
 | 
						|
         (lambda () (with-mail-out (match:substring match 3)))
 | 
						|
         (match:substring match 3))))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
;; The case of reading a line from /etc/crontab is similar to above but the user
 | 
						|
;; ID appears in the sixth field, before the action.
 | 
						|
 | 
						|
(define parse-system-vixie-line-regexp
 | 
						|
  (make-regexp (string-append "^[[:space:]]*(([^[:space:]]+[[:space:]]+){5})"
 | 
						|
                              "([[:alpha:]][[:alnum:]_]*)[[:space:]]+(.*)$")))
 | 
						|
 | 
						|
(define (parse-system-vixie-line line)
 | 
						|
  (let ((match (regexp-exec parse-system-vixie-line-regexp line)))
 | 
						|
    (if (not match) (begin (display "Bad job line in /etc/crontab.\n")
 | 
						|
                           (primitive-exit 11)))
 | 
						|
    (let ((user (match:substring match 3)))
 | 
						|
      (set-configuration-user user)
 | 
						|
      (job (match:substring match 1)
 | 
						|
           (lambda () (with-mail-out (match:substring match 4)
 | 
						|
                                     user))
 | 
						|
           (match:substring match 4)))))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
;; Procedure to act on an environment variable specification in a Vixie-style
 | 
						|
;; configuration file, by adding an entry to the alist above. Returns #t if the
 | 
						|
;; operation was successful, #f if the line could not be interpreted as an
 | 
						|
;; environment specification.
 | 
						|
 | 
						|
(define parse-vixie-environment-regexp1
 | 
						|
  (make-regexp
 | 
						|
   "^[ \t]*([[:alpha:]_][[:alnum:]_]*)[ \t]*=[ \t]*\"(.*)\"[ \t]*$"))
 | 
						|
(define parse-vixie-environment-regexp2
 | 
						|
  (make-regexp
 | 
						|
   "^[ \t]*([[:alpha:]_][[:alnum:]_]*)[ \t]*=[ \t]*\'(.*)\'[ \t]*$"))
 | 
						|
(define parse-vixie-environment-regexp3
 | 
						|
  (make-regexp
 | 
						|
   "^[ \t]*([[:alpha:]_][[:alnum:]_]*)[ \t]*=[ \t]*(.*[^ \t])[ \t]*$"))
 | 
						|
(define parse-vixie-environment-regexp4
 | 
						|
  (make-regexp
 | 
						|
   "^[ \t]*([[:alpha:]_][[:alnum:]_]*)[ \t]*=[ \t]*$"))
 | 
						|
 | 
						|
 | 
						|
(define (parse-vixie-environment string)
 | 
						|
  (let ((match (or (regexp-exec parse-vixie-environment-regexp1 string)
 | 
						|
                   (regexp-exec parse-vixie-environment-regexp2 string)
 | 
						|
                   (regexp-exec parse-vixie-environment-regexp3 string))))
 | 
						|
    (if match
 | 
						|
        (append-environment-mods (match:substring match 1)
 | 
						|
                                 (match:substring match 2))
 | 
						|
        (and-let* ((match (regexp-exec parse-vixie-environment-regexp4 string)))
 | 
						|
                  (append-environment-mods (match:substring match 1) #f)))))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
;; The next procedure reads an entire Vixie-style file. For each line in the
 | 
						|
;; file there are three possibilities (after continuation lines have been
 | 
						|
;; appended): the line is blank or contains only a comment, the line contains an
 | 
						|
;; environment modifier which will be handled in the mcron environment module,
 | 
						|
;; or the line contains a command specification in which case we use the
 | 
						|
;; procedure above to add an entry to the internal job list.
 | 
						|
;;
 | 
						|
;; Note that the environment modifications are cleared, so that there is no
 | 
						|
;; interference between crontab files (this might lead to unpredictable
 | 
						|
;; behaviour because the order in which crontab files are processed, if there is
 | 
						|
;; more than one, is generally undefined).
 | 
						|
 | 
						|
(define read-vixie-file-comment-regexp
 | 
						|
  (make-regexp "^[[:space:]]*(#.*)?$"))
 | 
						|
 | 
						|
 | 
						|
(define (read-vixie-port port . parse-vixie-line)
 | 
						|
  (clear-environment-mods)
 | 
						|
  (if port
 | 
						|
      (let ((parse-vixie-line
 | 
						|
             (if (null? parse-vixie-line) parse-user-vixie-line
 | 
						|
                 (car parse-vixie-line))))
 | 
						|
        (do ((line (read-line port) (read-line port)))
 | 
						|
            ((eof-object? line))
 | 
						|
          
 | 
						|
          ;; If the line ends with \, append the next line.
 | 
						|
          (while (and (>= (string-length line) 1)
 | 
						|
                      (char=? (string-ref line
 | 
						|
                                          (- (string-length line) 1))
 | 
						|
                               #\\))
 | 
						|
            (let ((next-line (read-line port)))
 | 
						|
              (if (eof-object? next-line)
 | 
						|
                  (set! next-line ""))
 | 
						|
              (set! line
 | 
						|
                    (string-append
 | 
						|
                     (substring line 0 (- (string-length line) 1))
 | 
						|
                     next-line))))
 | 
						|
 | 
						|
          ;; Consider the three cases mentioned in the description.
 | 
						|
          (or (regexp-exec read-vixie-file-comment-regexp line)
 | 
						|
              (parse-vixie-environment line)
 | 
						|
              (parse-vixie-line line))))))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
;; If a file cannot be opened, we must silently ignore it because it may have
 | 
						|
;; been removed by crontab. However, if the file is there it must be parseable,
 | 
						|
;; otherwise the error must be propagated to the caller.
 | 
						|
 | 
						|
(define (read-vixie-file file-path . parse-vixie-line)
 | 
						|
  (let ((port #f))
 | 
						|
    (catch #t (lambda () (set! port (open-input-file file-path)))
 | 
						|
           (lambda (key . args) (set! port #f)))
 | 
						|
    (if port
 | 
						|
        (begin
 | 
						|
          (if (null? parse-vixie-line)
 | 
						|
              (read-vixie-port port)
 | 
						|
              (read-vixie-port port (car parse-vixie-line)))
 | 
						|
          (close port)))))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
;; A procedure which determines if the /etc/crontab file has been recently
 | 
						|
;; modified, and, if so, signals the main routine to re-read the file. We run
 | 
						|
;; under the with-mail-to command so that the process runs as a child,
 | 
						|
;; preventing lockup. If cron is supposed to check for updates to /etc/crontab,
 | 
						|
;; then this procedure will be called about 5 seconds before every minute.
 | 
						|
 | 
						|
(define (check-system-crontab)
 | 
						|
  (with-mail-out (lambda ()
 | 
						|
                  (let ((mtime (stat:mtime (stat "/etc/crontab"))))
 | 
						|
                    (if (> mtime (- (current-time) 60))
 | 
						|
                        (let ((socket (socket AF_UNIX SOCK_STREAM 0)))
 | 
						|
                          (connect socket AF_UNIX config-socket-file)
 | 
						|
                          (display "/etc/crontab" socket)
 | 
						|
                          (close socket)))))))
 |