Racketized handin-server

This commit is contained in:
Eli Barzilay 2010-09-12 02:22:01 -04:00
parent bd4a5fb706
commit 6b471afcfd
9 changed files with 56 additions and 59 deletions

View File

@ -1,10 +1,10 @@
#lang scheme/base #lang scheme/base
(require (for-syntax scheme/base) "utils.ss" (require (for-syntax scheme/base) "utils.rkt"
scheme/file scheme/class mred) scheme/file scheme/class mred)
(provide (except-out (all-from-out scheme/base) #%module-begin) (provide (except-out (all-from-out scheme/base) #%module-begin)
(all-from-out "utils.ss")) (all-from-out "utils.rkt"))
(provide (rename-out [module-begin~ #%module-begin])) (provide (rename-out [module-begin~ #%module-begin]))
(define-syntax (module-begin~ stx) (define-syntax (module-begin~ stx)
@ -43,7 +43,7 @@
;; the student is always assumed to exist ;; the student is always assumed to exist
(cdr (get-preference (if (string? user) (string->symbol user) user) (cdr (get-preference (if (string? user) (string->symbol user) user)
(lambda () #f) 'timestamp (lambda () #f) 'timestamp
(build-path server-dir "users.ss")))) (build-path server-dir "users.rktd"))))
(provide user-substs) (provide user-substs)
(define (user-substs user str) (define (user-substs user str)
@ -127,7 +127,8 @@
(let ([name (and (is-a? x snip%) (let ([name (and (is-a? x snip%)
(send (send x get-snipclass) get-classname))]) (send (send x get-snipclass) get-classname))])
(cond [(equal? name "wximage") "{{IMAGE}}"] (cond [(equal? name "wximage") "{{IMAGE}}"]
[(equal? name "(lib \"comment-snip.ss\" \"framework\")") [(regexp-match? #rx"(lib \"comment-snip.(?:rkt|ss)\" \"framework\")"
name)
;; comments will have ";" prefix on every line, and "\n" suffix ;; comments will have ";" prefix on every line, and "\n" suffix
(format ";{{COMMENT:\n~a;}}\n" (format ";{{COMMENT:\n~a;}}\n"
(send x get-text 0 (send x get-count)))] (send x get-text 0 (send x get-count)))]
@ -175,7 +176,7 @@
(define (submission->bytes submission maxwidth textualize? untabify? (define (submission->bytes submission maxwidth textualize? untabify?
markup-prefix bad-re) markup-prefix bad-re)
(define magic #rx#"^(?:#reader[(]lib\"read.ss\"\"wxme\"[)])?WXME") (define magic #rx#"^(?:#reader[(]lib\"read.(?:rkt|ss)\"\"wxme\"[)])?WXME")
(unless (regexp-match? magic submission) (unless (regexp-match? magic submission)
(error* "bad submission format, expecting a single DrRacket submission")) (error* "bad submission format, expecting a single DrRacket submission"))
(let-values ([(defs inters) (unpack-submission submission)]) (let-values ([(defs inters) (unpack-submission submission)])

View File

@ -4,19 +4,19 @@
scheme/port scheme/port
openssl openssl
scheme/file scheme/file
"private/logger.ss" "private/logger.rkt"
"private/config.ss" "private/config.rkt"
"private/lock.ss" "private/lock.rkt"
"private/md5.ss" "private/md5.rkt"
"private/run-status.ss" "private/run-status.rkt"
"private/reloadable.ss" "private/reloadable.rkt"
"private/hooker.ss" "private/hooker.rkt"
(prefix-in web: "web-status-server.ss") (prefix-in web: "web-status-server.rkt")
;; this sets some global parameter values, and this needs ;; this sets some global parameter values, and this needs
;; to be done in the main thread, rather than later in a ;; to be done in the main thread, rather than later in a
;; user session thread (that will make the global changes ;; user session thread (that will make the global changes
;; not to be global.) ;; not to be global.)
"sandbox.ss") "sandbox.rkt")
(install-logger-port) (install-logger-port)
@ -48,8 +48,8 @@
(lambda (f) (lambda (f)
(error 'handin-server (error 'handin-server
"unable to clean up lock file: ~s" f)) "unable to clean up lock file: ~s" f))
"users.ss")) "users.rktd"))
"users.ss") "users.rktd")
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -214,7 +214,7 @@
(save-submission s (build-path ATTEMPT-DIR "handin")) (save-submission s (build-path ATTEMPT-DIR "handin"))
(timeout-control 'reset) (timeout-control 'reset)
(log-line "checking ~a for ~a" assignment users) (log-line "checking ~a for ~a" assignment users)
(let* ([checker* (path->complete-path (build-path 'up "checker.ss"))] (let* ([checker* (path->complete-path (build-path 'up "checker.rkt"))]
[checker* (and (file-exists? checker*) [checker* (and (file-exists? checker*)
(parameterize ([current-directory server-dir]) (parameterize ([current-directory server-dir])
(auto-reload-value (auto-reload-value
@ -282,7 +282,7 @@
(parameterize ([current-directory submission-dir]) (parameterize ([current-directory submission-dir])
(define magics '(#"WXME" (define magics '(#"WXME"
#"<<<MULTI-SUBMISSION-FILE>>>" #"<<<MULTI-SUBMISSION-FILE>>>"
#"#reader(lib\"read.ss\"\"wxme\")WXME")) #"#reader(lib\"read.rkt\"\"wxme\")WXME"))
(define mlen (apply max (map bytes-length magics))) (define mlen (apply max (map bytes-length magics)))
(define file (define file
;; find the newest wxme file ;; find the newest wxme file
@ -326,12 +326,12 @@
(list (string->symbol username)) (list data) (list (string->symbol username)) (list data)
(lambda (f) (lambda (f)
(error* "user database busy; please try again, and alert the adminstrator if problems persist")) (error* "user database busy; please try again, and alert the adminstrator if problems persist"))
"users.ss")) "users.rktd"))
orig-custodian)) orig-custodian))
(define (get-user-data username) (define (get-user-data username)
(get-preference (string->symbol username) (lambda () #f) 'timestamp (get-preference (string->symbol username) (lambda () #f) 'timestamp
"users.ss")) "users.rktd"))
(define (check-field value field-re field-name field-desc) (define (check-field value field-re field-name field-desc)
(unless (cond [(or (string? field-re) (regexp? field-re)) (unless (cond [(or (string? field-re) (regexp? field-re))
(regexp-match field-re value)] (regexp-match field-re value)]
@ -372,8 +372,8 @@
(error* "username must not begin or end with a space or period")) (error* "username must not begin or end with a space or period"))
(when (regexp-match #rx"^solution" username) (when (regexp-match #rx"^solution" username)
(error* "the username prefix \"solution\" is reserved")) (error* "the username prefix \"solution\" is reserved"))
(when (string=? "checker.ss" username) (when (string=? "checker.rkt" username)
(error* "the username \"checker.ss\" is reserved")) (error* "the username \"checker.rkt\" is reserved"))
(when (get-user-data username) (when (get-user-data username)
(error* "username already exists: `~a'" username)) (error* "username already exists: `~a'" username))
(for ([str (in-list extra-fields)] (for ([str (in-list extra-fields)]

View File

@ -10,7 +10,7 @@
dir dir
(error 'config "handin server directory does not exist: ~e" dir)))) (error 'config "handin server directory does not exist: ~e" dir))))
(define config-file (path->complete-path "config.ss" server-dir)) (define config-file (path->complete-path "config.rktd" server-dir))
(define poll-freq 2000.0) ; poll at most once every two seconds (define poll-freq 2000.0) ; poll at most once every two seconds

View File

@ -1,6 +1,6 @@
#lang scheme/base #lang scheme/base
(require "config.ss" "logger.ss" "reloadable.ss") (require "config.rkt" "logger.rkt" "reloadable.rkt")
(provide hook) (provide hook)

View File

@ -1,6 +1,6 @@
#lang scheme/base #lang scheme/base
(require "config.ss" scheme/date scheme/port) (require "config.rkt" scheme/date scheme/port)
(provide current-session) (provide current-session)
(define current-session (make-parameter #f)) (define current-session (make-parameter #f))

View File

@ -1,6 +1,6 @@
#lang scheme/base #lang scheme/base
(require syntax/moddep "logger.ss") (require syntax/moddep "logger.rkt")
(provide reload-module) (provide reload-module)
(define (reload-module modspec path) (define (reload-module modspec path)
@ -23,24 +23,20 @@
(provide auto-reload-value) (provide auto-reload-value)
(define module-times (make-hash)) (define module-times (make-hash))
(define (auto-reload-value modspec valname) (define (auto-reload-value modspec valname)
(let* ([path (resolve-module-path modspec #f)] ; HACK: set!ed below (define path0 (resolve-module-path modspec #f))
[last (hash-ref module-times path #f)] (define last (hash-ref module-times path0 #f))
[cur (file-or-directory-modify-seconds (define-values (path cur)
path (let ([s (file-or-directory-modify-seconds path0 #f (lambda () #f))])
#f (if s
(lambda () (values path0 s)
(if (regexp-match #rx#"[.]rkt$" (path->bytes path)) (let* ([p (and (regexp-match? #rx#"[.]rkt$" (path->bytes path0))
(file-or-directory-modify-seconds (path-replace-suffix path0 #".ss"))]
(begin [s (and p (file-or-directory-modify-seconds p #f (lambda () #f)))])
(set! path (path-replace-suffix path #".ss")) (if s (values p s) (values path0 +inf.0))))))
path) (unless (equal? cur last)
#f (hash-set! module-times path cur)
(lambda () +inf.0)) (reload-module modspec path))
+inf.0)))]) (dynamic-require modspec valname))
(unless (equal? cur last)
(hash-set! module-times path cur)
(reload-module modspec path))
(dynamic-require modspec valname)))
(define poll-freq 2000.0) ; poll at most once every two seconds (define poll-freq 2000.0) ; poll at most once every two seconds

View File

@ -18,14 +18,14 @@
NOTE: For real use, you need a new key.} NOTE: For real use, you need a new key.}
@item{Create a file @filepath{users.ss} with the following content: @item{Create a file @filepath{users.rktd} with the following content:
@schemeblock[ @schemeblock[
((tester ("8fe4c11451281c094a6578e6ddbf5eed" ((tester ("8fe4c11451281c094a6578e6ddbf5eed"
"Tester" "1" "test@cs")))]} "Tester" "1" "test@cs")))]}
@item{Make a @filepath{test} subdirectory in your new directory.} @item{Make a @filepath{test} subdirectory in your new directory.}
@item{Create a file @filepath{config.ss} with the following content: @item{Create a file @filepath{config.rktd} with the following content:
@schemeblock[((active-dirs ("test")))]} @schemeblock[((active-dirs ("test")))]}
@item{In your new directory, run @item{In your new directory, run

View File

@ -21,7 +21,7 @@ This directory contains the following files and sub-directories:
distributed to students with the handin client, distributed to students with the handin client,
@filepath{private-key.pem} is kept private.} @filepath{private-key.pem} is kept private.}
@item{@filepath{config.ss}: configuration options. The file format is @item{@filepath{config.rktd}: configuration options. The file format is
@verbatim[#:indent 2]{((<key> <val>) ...)} @verbatim[#:indent 2]{((<key> <val>) ...)}
The following keys can be used: The following keys can be used:
@ -143,9 +143,9 @@ This directory contains the following files and sub-directories:
#rx"^[^@<>\"`',]+@cs\\.utah\\.edu$" #rx"^[^@<>\"`',]+@cs\\.utah\\.edu$"
"A Utah CS email address"))}| "A Utah CS email address"))}|
The order of these fields will be used both on the client GUI side The order of these fields will be used both on the client GUI side
and in the @filepath{users.ss} file (see below). and in the @filepath{users.rktd} file (see below).
@; JBC: a hyperlink here for users.ss? @; JBC: a hyperlink here for users.rktd?
The second item in a field description can also be the symbol The second item in a field description can also be the symbol
@schemeid[-], which marks this field as one that is hidden from the @schemeid[-], which marks this field as one that is hidden from the
@ -154,7 +154,7 @@ This directory contains the following files and sub-directories:
fields will be left empty. This is useful for adding information fields will be left empty. This is useful for adding information
that you have on students from another source, for example, adding that you have on students from another source, for example, adding
information from a course roster. You should manually edit the information from a course roster. You should manually edit the
@filepath{users.ss} file and fill in such information. (The third @filepath{users.rktd} file and fill in such information. (The third
element for such descriptors is ignored.)} element for such descriptors is ignored.)}
@item{@indexed-scheme[hook-file] --- a path (relative to handin @item{@indexed-scheme[hook-file] --- a path (relative to handin
@ -206,7 +206,7 @@ This directory contains the following files and sub-directories:
(apply format "~a: ~s" key+val)) (apply format "~a: ~s" key+val))
alist))))]}}] alist))))]}}]
Changes to @filepath{config.ss} are detected, the file will be Changes to @filepath{config.rktd} are detected, the file will be
re-read, and options are reloaded. A few options are fixed at startup re-read, and options are reloaded. A few options are fixed at startup
time: port numbers and log file specs are fixed as configured at time: port numbers and log file specs are fixed as configured at
startup. All other options will change the behavior of the running startup. All other options will change the behavior of the running
@ -218,7 +218,7 @@ This directory contains the following files and sub-directories:
not save until the new contents is ready.) This is most useful for not save until the new contents is ready.) This is most useful for
closing & opening submissions directories.} closing & opening submissions directories.}
@item{@filepath{users.ss} (created if not present when a user is added): @item{@filepath{users.rktd} (created if not present when a user is added):
keeps the list of user accounts, along with the associated password keeps the list of user accounts, along with the associated password
(actually the MD5 hash of the password), and extra string fields as (actually the MD5 hash of the password), and extra string fields as
specified by the @schemeid[extra-fields] configuration entry (in the specified by the @schemeid[extra-fields] configuration entry (in the
@ -240,30 +240,30 @@ This directory contains the following files and sub-directories:
pathnames, and they cannot end or begin in spaces or periods. pathnames, and they cannot end or begin in spaces or periods.
If the @schemeid[allow-new-users] configuration allows new users, the If the @schemeid[allow-new-users] configuration allows new users, the
@filepath{users.ss} file can be updated by the server with new users. @filepath{users.rktd} file can be updated by the server with new users.
It can always be updated by the server to change passwords. It can always be updated by the server to change passwords.
If you have access to a standard Unix password file (from If you have access to a standard Unix password file (from
@filepath{/etc/passwd} or @filepath{/etc/shadow}), then you can @filepath{/etc/passwd} or @filepath{/etc/shadow}), then you can
construct a @filepath{users.ss} file that will allow users to use construct a @filepath{users.rktd} file that will allow users to use
their normal passwords. To achieve this, use a list with their normal passwords. To achieve this, use a list with
@schemeid[unix] as the first element and the system's encrypted @schemeid[unix] as the first element and the system's encrypted
password string as the second element. Such passwords can be used, password string as the second element. Such passwords can be used,
but when users change them, a plain md5 hash will be used. but when users change them, a plain md5 hash will be used.
You can combine this with other fields from the password file to You can combine this with other fields from the password file to
create your @filepath{users.ss}, but make sure you have information create your @filepath{users.rktd}, but make sure you have information
that matches your @schemeid[extra-fields] specification. For example, that matches your @schemeid[extra-fields] specification. For example,
given this system file: given this system file:
@verbatim[#:indent 2]|{ @verbatim[#:indent 2]|{
foo:wRzN1u5q2SqRD:1203:1203:L.E. Foo :/home/foo:/bin/tcsh foo:wRzN1u5q2SqRD:1203:1203:L.E. Foo :/home/foo:/bin/tcsh
bar:$1$dKlU0OkJ$t63TzKz:1205:1205:Bar Z. Lie:/home/bar:/bin/bash}| bar:$1$dKlU0OkJ$t63TzKz:1205:1205:Bar Z. Lie:/home/bar:/bin/bash}|
you can create this @filepath{users.ss} file: you can create this @filepath{users.rktd} file:
@verbatim[#:indent 2]|{ @verbatim[#:indent 2]|{
((foo ((unix "wRzN1u5q2SqRD") "L.E. Foo" "?")) ((foo ((unix "wRzN1u5q2SqRD") "L.E. Foo" "?"))
(bar ((unix "$1$dKlU0OkJ$t63TzKz") "Bar Z. Lie" "?")))}| (bar ((unix "$1$dKlU0OkJ$t63TzKz") "Bar Z. Lie" "?")))}|
which can be combined with this setting for @schemeid[extra-fields] in which can be combined with this setting for @schemeid[extra-fields] in
your @filepath{config.ss}: your @filepath{config.rktd}:
@verbatim[#:indent 2]{ @verbatim[#:indent 2]{
... ...
(extra-fields (("Full Name" #f #f) (extra-fields (("Full Name" #f #f)
@ -354,7 +354,7 @@ This directory contains the following files and sub-directories:
exception; the exception message will be relayed back to the exception; the exception message will be relayed back to the
student. The module is loaded when the current directory is the student. The module is loaded when the current directory is the
main server directory, so it can read files from there (but note main server directory, so it can read files from there (but note
that to read values from @filepath{config.ss} it is better to use that to read values from @filepath{config.rktd} it is better to use
@scheme[get-conf]). Also, the module will be reloaded if the @scheme[get-conf]). Also, the module will be reloaded if the
checker file is modified; there's no need to restart the server, checker file is modified; there's no need to restart the server,
but make sure that you do not save a broken checker (i.e., do not but make sure that you do not save a broken checker (i.e., do not

View File

@ -23,7 +23,7 @@
(body ([bgcolor "white"]) (h1 ((align "center")) ,title) ,@body))) (body ([bgcolor "white"]) (h1 ((align "center")) ,title) ,@body)))
(define get-user-data (define get-user-data
(let ([users-file (build-path server-dir "users.ss")]) (let ([users-file (build-path server-dir "users.rktd")])
(unless (file-exists? users-file) (unless (file-exists? users-file)
(log-line "WARNING: users file missing on startup: ~a" users-file)) (log-line "WARNING: users file missing on startup: ~a" users-file))
(lambda (user) (lambda (user)