[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Communicating Lisp images

   Date: 	Fri, 11 Jun 1993 10:01:58 PDT
   From: bha <bha%gumby.boeing.com@ada3.ca.boeing.com>

   Do you have any examples/samples of code that facilitates
   communication between two ACL images?  What I want to do is implement
   a simple eval server where an ACL image (4.2beta) acts as a client and
   communicates with an ICAD image (ACL 4.1beta) acting as a server.

We've used the following in ACL 4.1, mostly to talk to lisp from shell
scripts, but it also works from lisp to lisp.  A sample client is
given at the end.  Enjoy.


(eval-when (compile eval load)
  (require :ipc)
  (require :process))

(cl:defpackage :eval-service
  (:use :common-lisp :ipc :mp)
  (:export *eval-service-port* eval-server *eval-server-stream* force-prin1))

(cl:in-package :eval-service)

(defvar *eval-service-port* 4322)
(defvar *eval-server-stream* nil)

(defun eval-server (&key (port *eval-service-port*)
                         (binds `((*package* ,(find-package :cl-user))))
  (start-lisp-listener-daemon :use-lep t :inet-port port :binds binds)
  (unless background
    (process-wait "waiting" #'(lambda (p) (not (process-active-p p)))
                  (process-name-to-process "TCP Listener Socket Daemon"))))

(defmethod start-process-for-network-stream (stream (p (eql :eval-service))
                                             &rest args)
  (apply #'process-run-function (read stream) #'eval-server-proc stream args))

(defun eval-server-proc (stream &key binds &allow-other-keys)
  (unwind-protect (eval-server-loop stream binds)
    (excl::clear-output-1 stream)
    (close stream)))

(defvar *tracing* t)

(defun eval-server-loop (stream binds
                         &aux (process-name (process-name *current-process*)))
  (handler-bind ((stream-error
                  #'(lambda (condition)
                      (when *tracing*
                        (format *error-output* "~&; ~A EXITING: ~?~%"
                                (simple-condition-format-control condition)
                                (simple-condition-format-arguments condition)))
                      (return-from eval-server-loop))))
     (let ((*eval-server-stream* stream)
           (*print-pretty* nil))
       (progv (mapcar #'car binds) (mapcar #'cadr binds)
         (when *tracing*
           (format *error-output* "~&; ~A OPEN~%" process-name))
         (loop (let ((form (error-trapping-read stream)))
                 (case form
                   (:exit (return))
                   (:error (force-prin1 :error stream))
                   (t (force-prin1 (error-trapping-eval form) stream)))))
         (when *tracing*
           (format *error-output* "~&; ~A EXIT~%" process-name))))))

(defun force-prin1 (obj stream)
  (prin1 obj stream)
  (terpri stream)
  (force-output stream))

(defun error-trapping-read (stream)
  (handler-bind ((simple-error
                  #'(lambda (condition)
                      (when *tracing*
                        (format *error-output* "~&; ~A READ ERROR: ~?~%"
                                (process-name *current-process*)
                                (simple-condition-format-control condition)
                                (simple-condition-format-arguments condition)))
                      ;; skip the rest of the input
                      ;; assume each input is contained on one line
                      (when (listen) (read-line stream))
                      (return-from error-trapping-read :error))))
    (read stream)))

(defun error-trapping-eval
    (form &aux (process-name (process-name *current-process*)))
  (when *tracing*
    (let ((*print-length* 5)
          (*print-level* 3))
      (format *error-output* "~&; ~A > ~S~%" process-name form)))
  (handler-bind ((simple-error
                  #'(lambda (condition)
                      (when *tracing*
                        (format *error-output* "~&; ~A EVAL ERROR: ~?~%"
                                (simple-condition-format-control condition)
                                (simple-condition-format-arguments condition)))
                      ;; all errors return :ERROR
                      (return-from error-trapping-eval :error))))
    (eval form)))

;;;; simple client

(defun remote-exec (host)
  ;; client side
  (let ((stream (open-network-stream :host host :port *eval-service-port*)))
    ;; first send type of connection and name
    (force-prin1 :eval-service stream)
     (format nil "~A@~A" (system:getenv "USER") (system:getenv "HOST"))
     (format t "~A > " host)
     (let ((input (read)))
       (force-prin1 input stream)
       (when (eq input :exit) (return))
       (force-prin1 (read stream) *standard-output*)))))