;;; CL server - not yet fully implemented ;;; ;;; Copyright (C) 1997-2000 by Sam Steingold ;;; This is Free Software, covered by the GNU GPL (v2) ;;; See http://www.gnu.org/copyleft/gpl.html ;;; ;;; $Id: server.lisp,v 1.7 2002/03/27 23:33:11 sds Exp $ ;;; $Source: /cvsroot/clocc/clocc/src/cllib/server.lisp,v $ (eval-when (compile load eval) (require :cllib-base (translate-logical-pathname "clocc:src;cllib;base")) ;; `list-format' (require :cllib-log (translate-logical-pathname "cllib:log")) ;; `package-short-name' (require :cllib-prompt (translate-logical-pathname "cllib:prompt")) (require :port-net (translate-logical-pathname "port:net"))) (in-package :cllib) (export '(cl-server)) ;;; ;;; }}}{{{ ;;; (defcustom *cl-server-port* integer 453 "*The default port for `cl-server'. Defaults to (parse-integer \"cl\" :radix 36) ==> 453") (defcustom *cl-server-password* (or null simple-string) "ansi-cl" "*The simple authentication. Set to NIL to disable.") (defcustom *cl-server-quit* list '("bye" "quit" "exit") "*The list of `quit' commands.") (defun cl-server-mesg (sock fmt &rest args) (princ "[") (current-time) (princ "]") (apply #'format t fmt args) (format t "~60t~a~%" (socket-string sock))) (defun cl-server (&key (port *cl-server-port*) (password *cl-server-password*)) "Establish a connection and answer questions." (declare (integer port)) (let ((serv (open-socket-server port))) (declare (type socket-server serv)) (unwind-protect (loop :for sock :of-type socket = (socket-accept serv) :with pwd :do (cl-server-mesg sock "connected:") (let ((*standard-output* sock)) (sysinfo)) :if (or (null password) (progn (format sock "Enter password: ") (force-output sock) (let ((str (read-line sock nil ""))) (declare (simple-string str)) (setq pwd (subseq str 0 (1- (length str)))) (string= pwd password)))) :do (format sock "password ok~%to quit, type one of~?.~%" (list-format "~s") *cl-server-quit*) (loop :for ii :of-type index-t :upfrom 1 :do (format sock "~a[~d]: > " (package-short-name *package*) ii) (force-output sock) (handler-case (let ((form (read sock))) (when (and (or (stringp form) (symbolp form)) (member form *cl-server-quit* :test #'string-equal)) (return)) (format sock "~{~a~^ ;~%~}~%" (multiple-value-list (eval form)))) (error (co) (format sock "error:~%~a~%...flushed...~%" co)))) (format sock "goodbye~%") (cl-server-mesg sock "connection closed:") :else :do (format sock "wrong password~%") (cl-server-mesg sock "access denied [~a]:" pwd) :end :do (close sock)) (socket-server-close serv)))) ;;; cmucl/src/code/multi-proc.lisp #+cmu (defun start-lisp-connection-listener (&key (port 1025) (password (random (expt 2 24)))) (declare (type (unsigned-byte 16) port)) "Create a Lisp connection listener, listening on a TCP port for new connections and starting a new top-level loop for each. If a password is not given then one will be generated and reported." (labels (;; The session top level read eval loop. (start-top-level (stream) (unwind-protect (let* ((*terminal-io* stream) (*standard-input* (make-synonym-stream '*terminal-io*)) (*standard-output* *standard-input*) (*error-output* *standard-input*) (*debug-io* *standard-input*) (*query-io* *standard-input*) (*trace-output* *standard-input*)) (format t "Enter password: ") (finish-output) (let* ((*read-eval* nil) (read-password (handler-case (read) (error () (return-from start-top-level))))) (unless (equal read-password password) (return-from start-top-level))) (sysinfo) (mp::top-level)) (handler-case (close stream) (error ())))) ;; The body of the connection listener. (listener () (declare (optimize (speed 3))) (let ((serv (open-socket-server port))) (unwind-protect (progn (format t "~&;;; Started lisp connection listener on ~ port ~d with password ~d~%" port password) (loop ;; Wait for new connections. (let ((sock (socket-accept serv))) (make-process (format nil "Lisp session from ~a" (socket-string sock)) #'(lambda () (start-top-level sock)))))) ;; Close the listener stream. (when serv (socket-server-close serv)))))) ;; Make the listening thread. (make-process (format nil "Lisp connection listener on port ~d" port) #'listener))) #| In Linux 2.2.X with the binmisc module, any .x86f file is a binary file. pvaneynd:~$ cat /usr/doc/cmucl/examples/Demos/register-lisp-as-executables.sh #!/bin/sh echo ':lisp:E::x86f::/usr/bin/lisp-start:' > /proc/sys/fs/binfmt_misc/register # this should only work for root under linux 2.1.XX or later # now you can do "chmod a+x hello.x86f" and # ./hello.x86f # from your favorite shell. pvaneynd:~$ cat /usr/doc/cmucl/examples/Demos/lisp-start #!/bin/sh /usr/bin/lisp -load $1 There is even a demo of how to use a lisp-server to wait for commands, so you avoid the startup-delay of cmucl... pvaneynd:~$ cat /usr/doc/cmucl/examples/Demos/Start-up-server.lisp (in-package :user) (format t "THIS A A HUDGE SECURITY RISC. CHANGE THE PASSWORD!!!~%~%") (setf mp::*idle-process* mp::*initial-process*) (mp::start-lisp-connection-listener :port 6789 :password "Clara") |# (provide :cllib-server) ;;; file server.lisp ends here