2011-10-24 22:36:08 +00:00
|
|
|
#lang racket/base
|
|
|
|
;; (Temporary) example client and server
|
|
|
|
|
|
|
|
(require racket/tcp)
|
|
|
|
(require racket/pretty)
|
|
|
|
(require racket/match)
|
2011-10-25 20:45:15 +00:00
|
|
|
(require racket/class)
|
|
|
|
(require racket/port)
|
2011-10-26 23:16:16 +00:00
|
|
|
(require racket/sandbox)
|
2011-10-24 22:36:08 +00:00
|
|
|
|
2011-10-27 18:15:44 +00:00
|
|
|
(require "ssh-service.rkt")
|
2011-10-24 22:36:08 +00:00
|
|
|
(require "standard-thread.rkt")
|
|
|
|
|
2011-10-27 19:25:06 +00:00
|
|
|
(require "conversation.rkt")
|
|
|
|
|
2011-11-02 22:53:15 +00:00
|
|
|
(struct user-state (name master-sandbox master-namespace) #:transparent)
|
2011-10-24 22:36:08 +00:00
|
|
|
|
2011-11-02 22:53:15 +00:00
|
|
|
(define *user-states* (make-hash))
|
2011-10-27 19:25:06 +00:00
|
|
|
|
|
|
|
(define *interaction* (make-room 'interaction))
|
2011-10-31 12:54:51 +00:00
|
|
|
(spy-on *interaction*)
|
2011-10-27 19:25:06 +00:00
|
|
|
|
2011-10-27 22:13:49 +00:00
|
|
|
(define *interaction-handle* (make-parameter #f))
|
|
|
|
|
2011-10-27 19:25:06 +00:00
|
|
|
(define *prompt* "RacketSSH> ")
|
|
|
|
|
|
|
|
(define (->string/safe bs)
|
|
|
|
(cond
|
|
|
|
((string? bs) bs)
|
|
|
|
((bytes? bs) (with-handlers ((exn? (lambda (e) (bytes->string/latin-1 bs))))
|
|
|
|
(bytes->string/utf-8 bs)))
|
|
|
|
(else (call-with-output-string (lambda (p) (write bs p))))))
|
|
|
|
|
|
|
|
(define (dump-interactions handle)
|
|
|
|
(display *prompt*)
|
|
|
|
(let retry-without-prompt ()
|
|
|
|
(sync (handle-evt (send handle listen-evt)
|
|
|
|
(lambda (message)
|
|
|
|
(display "\033[1G\033[2K") ;; clear current line
|
|
|
|
(let loop ((message message))
|
|
|
|
(when message
|
|
|
|
(match message
|
|
|
|
((arrived who) (printf "*** ~a arrived\n" (->string/safe who)))
|
|
|
|
((departed who why) (printf "*** ~a departed (~a)\n"
|
|
|
|
(->string/safe who)
|
|
|
|
(->string/safe why)))
|
|
|
|
((says who what #f)
|
|
|
|
(printf " ~a: ~a\n" (->string/safe who) (->string/safe what)))
|
|
|
|
((says who what topic)
|
|
|
|
(printf " ~a (~a): ~a\n"
|
|
|
|
(->string/safe who)
|
|
|
|
(->string/safe topic)
|
|
|
|
(->string/safe what))))
|
|
|
|
(loop (send handle try-listen))))
|
|
|
|
(dump-interactions handle)))
|
|
|
|
(handle-evt (peek-string-evt 1 0 #f (current-input-port))
|
|
|
|
(lambda (s)
|
|
|
|
(cond
|
|
|
|
((eof-object? s))
|
|
|
|
((char-whitespace? (string-ref s 0))
|
|
|
|
(read-string 1 (current-input-port))
|
|
|
|
(retry-without-prompt))
|
|
|
|
(else 'ready-to-read-something-real)))))))
|
|
|
|
|
|
|
|
(define (call-with-interaction-prompt-read handle thunk)
|
|
|
|
(parameterize ((current-prompt-read (lambda ()
|
|
|
|
(dump-interactions handle)
|
|
|
|
(read-syntax "<ssh-session>" (current-input-port)))))
|
|
|
|
(thunk)))
|
|
|
|
|
2011-10-31 14:55:19 +00:00
|
|
|
(define (help)
|
|
|
|
(printf "This is RacketSSH, a secure REPL for Racket.\n")
|
|
|
|
(printf "Definitions made are kept in a per-user environment.\n")
|
|
|
|
(printf "Beyond core Racket,\n")
|
|
|
|
(printf " (say <any>) - communicates its argument to other logged-in users\n")
|
2011-10-31 15:35:07 +00:00
|
|
|
(printf " (help) - this help message\n")
|
|
|
|
(printf "If the reader gets confused, try control-L to make it reprint the line\n")
|
|
|
|
(printf "buffer, or ESC to clear the line buffer.\n"))
|
2011-10-31 14:55:19 +00:00
|
|
|
|
2011-11-02 22:53:15 +00:00
|
|
|
(define (say utterance)
|
|
|
|
(printf " You: ~a\n" (->string/safe utterance))
|
|
|
|
(send (*interaction-handle*) say utterance)
|
|
|
|
(void))
|
|
|
|
|
|
|
|
(define (get-user-state username)
|
|
|
|
(when (not (hash-has-key? *user-states* username))
|
|
|
|
(let* ((sb (make-evaluator 'racket))
|
|
|
|
(ns (call-in-sandbox-context sb current-namespace)))
|
|
|
|
(parameterize ((current-namespace ns))
|
|
|
|
(namespace-set-variable-value! 'help help)
|
|
|
|
(namespace-set-variable-value! 'say say))
|
|
|
|
(hash-set! *user-states* username
|
|
|
|
(user-state username
|
|
|
|
sb
|
|
|
|
ns))))
|
|
|
|
(hash-ref *user-states* username))
|
|
|
|
|
2011-10-27 18:15:44 +00:00
|
|
|
(define (repl-shell username in out)
|
2011-10-27 19:25:06 +00:00
|
|
|
(define handle (join-room *interaction* username))
|
2011-11-02 22:53:15 +00:00
|
|
|
(match-define (user-state _ master-sandbox master-namespace) (get-user-state username))
|
2011-10-27 18:15:44 +00:00
|
|
|
(parameterize ((current-input-port in)
|
|
|
|
(current-output-port out)
|
|
|
|
(current-error-port out)
|
|
|
|
(sandbox-input in)
|
|
|
|
(sandbox-output out)
|
|
|
|
(sandbox-error-output out)
|
2011-11-02 23:13:27 +00:00
|
|
|
(sandbox-memory-limit 2) ;; megabytes
|
|
|
|
(sandbox-eval-limits #f)
|
2011-11-02 22:53:15 +00:00
|
|
|
(sandbox-namespace-specs (list (lambda () master-namespace))))
|
|
|
|
(printf "Hello, ~a.\n" username)
|
|
|
|
(printf "Type (help) for help.\n")
|
|
|
|
(define slave-sandbox (make-evaluator '(begin)))
|
|
|
|
;; ^^ uses master-namespace via sandbox-namespace-specs
|
|
|
|
(slave-sandbox `(,*interaction-handle* ,handle))
|
|
|
|
(parameterize ((current-namespace master-namespace)
|
|
|
|
(current-eval slave-sandbox))
|
2011-10-27 19:25:06 +00:00
|
|
|
(call-with-interaction-prompt-read handle read-eval-print-loop))
|
2011-10-27 18:15:44 +00:00
|
|
|
(fprintf out "\nGoodbye!\n")
|
2011-11-02 22:53:15 +00:00
|
|
|
(kill-evaluator slave-sandbox)
|
2011-10-27 18:15:44 +00:00
|
|
|
(close-input-port in)
|
|
|
|
(close-output-port out)))
|
2011-10-24 22:36:08 +00:00
|
|
|
|
|
|
|
(define (t-server)
|
2011-10-27 22:14:04 +00:00
|
|
|
(define s (tcp-listen 2322 4 #t))
|
2011-10-24 22:36:08 +00:00
|
|
|
(printf "Accepting...\n")
|
2011-10-27 19:25:06 +00:00
|
|
|
(tcp-pty-ssh-server s repl-shell #:prompt *prompt*))
|
2011-10-24 22:36:08 +00:00
|
|
|
|
2011-11-02 22:53:15 +00:00
|
|
|
(t-server)
|