scsh-users
[Top] [All Lists]

Code critique request

To: scsh-users@scsh.net
Subject: Code critique request
From: seanwinship@yahoo.com (Sean Winship)
Date: Mon, 21 Jun 2004 10:24:54 +0200 (MST)
List-id: <scsh-users.list-id.scsh.net>
Organization: http://groups.google.com
I wrote the following script to track inactive users on one of our
machines.  I have modified it as necessary so as not to identify my
employer; hopefully it still makes sense.  We have hardware available
internally for "skunkworks" use.  Employees can get probationary
access which will be revoked if they aren't actively using the
resources.  Anyone with an active project will not be considered
inactive, even if they are still listed as probationary.

I've been learning Scheme on my own and have no one at work to discuss
it with.  Any comments or critiques of this script would be
appreciated.  I'm particularly concerned about my use of setf!, which
seems to go against the spirit of Scheme that I've picked up from
SICP.

Thanks for any help.

Sean

----- begin inactives.scsh -----

#!/usr/local/bin/scsh -o list-lib -s
!#

;; Some constants.
(define *user-file* "/home/adm/USERS")

(define *base-user-dir* "/home")
(define *base-user-info-dir* "/home/adm/users")
(define *active-projects-dir* "/home/adm/projects")

(define *last-login-field-name* "last_login")
(define *user-level-field-name* "level")

(define *externally-confirmed-active* '("joe" "jim" "fred"))

;; Some convenience functions.
(define one-month-ago (- (time) (* 30 24 60 60)))
(define two-months-ago (- (time) (* 60 24 60 60)))
(define three-months-ago (- (time) (* 90 24 60 60)))
(define six-months-ago (- (time) (* 180 24 60 60)))

;; Return the name of the specified user's info file.
(define (user-file-name user-name)
  (string-append *base-user-info-dir*
                 "/"
                 (substring user-name 0 1)
                 "/"
                 user-name
                 ".info"))

;; Return the value of the specified field in the user file.
(define (field-value name field)
  (let ((read-user-file (field-reader (infix-splitter)))
        (value ""))
    (if (file-exists? (user-file-name name))
        (call-with-input-file (user-file-name name)
          (lambda (port)
            (awk (read-user-file port) (record fields) ()
                 ((equal? (car fields) field)
                  (set! value (cadr fields)))))))
    value))

;; Return the date of the last login of the specified user, in seconds
;; since the epoch.
(define (last-login name)
  (string->number (field-value name *last-login-field-name*)))

;; Return the user's level.
(define (user-level name)
  (field-value name *user-level-field-name*))

;; Return a the number of days since the specified user last logged
;; in.
(define (days-since-last-login name)
  (floor (/ (- (time) (last-login name)) (* 24 60 60))))

;; Return the number of files in the tree rooted at the specified
;; base directory that have been modified since the specified date.
;; Modified directories are not counted.
(define (count-recent-files base-dir since-date)
  (if (and (file-exists? base-dir)
           (file-directory? base-dir))
      (with-cwd base-dir
                (let ((count 0))
                  (for-each (lambda (file)
                              (if (file-directory? file)
                                  (set! count
                                        (+ count
                                           (count-recent-files
                                            file
                                            since-date)))
                                  (if (< since-date (file-last-mod
file))
                                      (set! count (+ 1 count)))))
                            (directory-files))
                  count))
      #f))

;; Return the home directory of the specified user.
(define (user-home-dir user-name)
  (string-append *base-user-dir* "/" user-name))

;; List all users of the specified level.
(define (list-users level)
  (let ((read-users (field-reader (infix-splitter ":")))
        (users (list)))
    (call-with-input-file *user-file*
      (lambda (port)
        (awk (read-users port) (record fields) ()
             ((and (equal? (nth fields 1) level)
                   (equal? (user-level (nth fields 0)) level))
              (set! users (cons (nth fields 0) users))))))
    users))

;; List all users with an entry in the active projects directory.
(define (contributing-users)
  (map string-downcase (directory-files *active-projects-dir*)))

;; Remove a sequence from another sequence.
(define (remove-sequence to-remove remove-from)
  (filter (lambda (x) (not (member x to-remove))) remove-from))

;; Output a report showing, for each probationary user, number of days
;; since last login, and number of files changed in the past 30, 60,
90,
;; and 180 days.
(define (user-activity-report)
  (map (lambda (user-name)
         (list user-name
               (days-since-last-login user-name)
               (count-recent-files (user-home-dir user-name)
one-month-ago)
               (count-recent-files (user-home-dir user-name)
                                   two-months-ago)
               (count-recent-files (user-home-dir user-name)
                                   three-months-ago)
               (count-recent-files (user-home-dir user-name)
                                   six-months-ago)))
       (remove-sequence (contributing-users) (list-users
"probationary"))))

(display (user-activity-report))

----- end inactives.scsh -----

<Prev in Thread] Current Thread [Next in Thread>