home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / comp / lang / lisp / 2403 < prev    next >
Encoding:
Text File  |  1992-09-10  |  25.4 KB  |  749 lines

  1. Newsgroups: comp.lang.lisp
  2. Path: sparky!uunet!stanford.edu!CSD-NewsHost.Stanford.EDU!Xenon.Stanford.EDU!geddis
  3. From: geddis@Xenon.Stanford.EDU (Don Geddis)
  4. Subject: Re: Interprocess comms using Allegro 4.1
  5. To: djasson@afterlife.ncsc.mil
  6. Message-ID: <geddis.716162932@Xenon.Stanford.EDU>
  7. Keywords: IPC, Allegro, socket, stream
  8. Sender: news@CSD-NewsHost.Stanford.EDU
  9. Reply-To: Geddis@CS.Stanford.Edu
  10. Organization: CS Department, Stanford University, California, USA
  11. References: <1992Sep10.171035.13307@afterlife.ncsc.mil>
  12. Distribution: usa
  13. Date: 10 Sep 92 22:08:52 GMT
  14. Lines: 733
  15.  
  16. djasson@zion (Drew Asson) writes:
  17. >This is a request for help to set up a unix-domain (socket file) server
  18. >in Allegro (4.1), that will accept 1 connection from a C code client.
  19. >The lisp side only need service one connection at a time (ergo no
  20. >fork needed), but I'd like it to stay resident and be able to accept
  21. >another connection when the last one is finished.
  22.  
  23. >I've looked at the sample code in the ipc.cl file.  Setting up a lisp
  24. >client is no problem (just use open-network-stream), but I want a lisp
  25. >server. So I started playing around w/ the function lisp-server-socket-daemon.
  26. >I thought I took out all I needed.  But it didn't work.
  27.  
  28. Without analyzing your detailed problem, I'll just say that I had exactly
  29. the same desire a few months ago.  Contacting Franz led to them sending me
  30. an experimental copy of a new ipc.cl file.  With some minor modifications
  31. by myself, it seems to work fine.  I'll include my modified ipc.cl and
  32. some sample server code below.
  33.  
  34. -------------------------------------------------------------------------------
  35. IPC.CL
  36.  
  37. ;;                    -[Wed Jan 15 14:27:09 1992 by layer]-
  38. ;;
  39. ;; Allegro CL IPC interface [cer version!!]
  40. ;;
  41. ;; copyright (c) 1986-1991 Franz Inc, Berkeley, CA
  42. ;;
  43. ;; The software, data and information contained herein are proprietary
  44. ;; to, and comprise valuable trade secrets of, Franz, Inc.  They are
  45. ;; given in confidence by Franz, Inc. pursuant to a written license
  46. ;; agreement, and may be stored and used only in accordance with the terms
  47. ;; of such license.
  48. ;;
  49. ;; Restricted Rights Legend
  50. ;; ------------------------
  51. ;; Use, duplication, and disclosure of the software, data and information
  52. ;; contained herein by any agency, department or entity of the U.S.
  53. ;; Government are subject to restrictions of Restricted Rights for
  54. ;; Commercial Software developed at private expense as specified in FAR
  55. ;; 52.227-19 or DOD FAR Suppplement 252 52.227-7013 (c) (1) (ii), as
  56. ;; applicable.
  57. ;;
  58.  
  59. ;;; Added 7/23/92 by Don Geddis (Geddis@CS.Stanford.Edu)
  60. (defconstant new-ipc-installed t)
  61.  
  62. ;;;;; I hacked this  to support the LEP protocol
  63. ;;;; This seems like a dreadful piece of code.
  64. ;;;; Changes:
  65. ;;;; 1. Support for multiple servers listening on different connections.
  66. ;;;; 2. The default behaviour is that the server reads a protocol name that specifies the type of the
  67. ;;;; server process to create.  Currently, supported protocols are:
  68. ;;;; a. :listener - RUN a listener
  69. ;;;; b. :lep - start up an LEP server
  70.  
  71. (eval-when (compile eval load)
  72.   (if (find-symbol (symbol-name :rcsnote) (find-package :si))
  73.       (push :has-rcsnote *features*))
  74.   
  75.   #-(or little-endian big-endian)
  76.   (pushnew
  77.    (let ((x '#(1)))
  78.      (if (not (= 0 (sys::memref x #.(comp::mdparam 'comp::md-svector-data0-adj)
  79.                 0 :unsigned-byte)))
  80.      :little-endian
  81.        :big-endian))
  82.    *features*)
  83.   )
  84.  
  85. #+has-rcsnote
  86. (si::rcsnote
  87.  "ipc"
  88.  "$aclHeader: ipc.cl,v 1.50 91/12/04 01:10:45 cox acl4_1 $")
  89.  
  90. (eval-when (compile eval load)
  91.   (excl::require :process)
  92.   (excl::require :foreign)
  93.   (excl::provide :ipc))
  94.  
  95. #+(version>= 4 0)
  96. (defpackage :ipc (:use :lisp :excl :ff :mp)
  97.         (:export #:start-lisp-listener-daemon #:open-network-stream
  98.              #:*inet-port-min* #:*inet-port-max* #:*inet-port-used*
  99.              #:start-process-for-network-stream
  100.              ))
  101.  
  102. #+(version>= 4 0)
  103. (in-package :ipc)
  104.  
  105. #-(version>= 4 0)
  106. (in-package :ipc :use '(:lisp :excl :ff :mp))
  107.  
  108. #-(version>= 4 0)
  109. (export '(start-lisp-listener-daemon open-network-stream
  110.       *inet-port-min* *inet-port-max* *inet-port-used*))
  111.  
  112. #+allegro-v3.1
  113. (progn
  114.   (excl::require :cstructs)
  115.   (defmacro ff::cstruct-length (x) `(ff::cstruct-len ,x))
  116.   )
  117.  
  118. #+(or allegro-v3.2 (version>= 4 0))
  119. (eval-when (compile eval load)
  120.   (excl::require :defctype))
  121.  
  122. (defvar *unix-domain* nil
  123.   ;; If non-nil then use a UNIX domain socket, otherwise use an internet
  124.   ;; domain port (see *inet-port* variable).
  125.   )
  126.  
  127. (defvar *socket-pathname* nil
  128.   ;; When the UNIX domain is used, then if non-nil this is the pathname of
  129.   ;; the socket file to use for the communication between GNU Emacs and
  130.   ;; Allegro CL.
  131.   )
  132.  
  133. (defparameter *inet-port-min* 1025 
  134.   "The smallest internet service port number on which Lisp listens for
  135. connections.")
  136.  
  137. (defparameter *inet-port-max* 2025
  138.   "The largest internet service port number on which Lisp listens for
  139. connections.")
  140.  
  141. (defvar *inet-port-used* 0
  142.   "Port actually in use for lisp listeners.")
  143.  
  144. (defvar *inet-listener-password* 0
  145.   ;; A magic number that mnust be supplied to verify that someone trying to
  146.   ;; connect to our listener is really the person who started the lisp
  147.   )
  148.  
  149. (defvar *ipc-version* 1  
  150.   ;; This indicates the version of the IPC protocol
  151.   )
  152.  
  153. (defconstant *af-unix* 1
  154.   ;; The AF_UNIX constant from /usr/include/sys/socket.h.
  155.   )
  156.  
  157. (defconstant *af-inet* 2
  158.   ;; The AF_INET constant from /usr/include/sys/socket.h.
  159.   )
  160.  
  161. (defconstant *sock-stream* 1
  162.   ;; The SOCK_STREAM constant from /usr/include/sys/socket.h.
  163.   )
  164.  
  165. ;; from <netinet/in.h>
  166. (defcstruct sockaddr-in
  167.   (family :unsigned-short)        ; short sin_family
  168.   (port :unsigned-short)        ; u_short sin_port
  169.   (addr :unsigned-long)            ; struct in_addr sin_addr
  170.   (zero 8 :char)            ; char sin_zero[8]
  171.   )
  172.  
  173. ;; from <??>
  174. (defcstruct sockaddr-un
  175.   (family :unsigned-short)
  176.   (path 109 :char))
  177.  
  178. ;; from <netdb.h>
  179. (defcstruct (hostent :malloc)
  180.   (name * :char)            ; char *h_name
  181.   (aliases * * :char)            ; char **h_aliases
  182.   (addrtype :long)            ; int h_addrtype
  183.   (length :long)            ; int h_length
  184.   (addr * :char)            ; char *h_addr   --or--
  185.                     ; char **h_addr_list (for SunOS 4.0)
  186.   )
  187.  
  188. (defcstruct (servent :malloc)        ; Returned by getservent
  189.   (name * :char)
  190.   (aliases * * :char)
  191.   (port :signed-long)
  192.   (proto * :char))
  193.  
  194. (defcstruct timeval
  195.   (sec :long)
  196.   (usec :long))
  197.  
  198. (defcstruct unsigned-long
  199.   (unsigned-long :unsigned-long))
  200.  
  201. (defvar .lisp-listener-daemon-ff-loaded. nil)
  202. (defvar .lisp-listener-daemon. nil)
  203.  
  204. (defparameter .needed-funcs.
  205.     (mapcar #'convert-to-lang
  206.         ;; this list appears in makefile.cl, too
  207.         '("socket" "bind" "listen" "accept" "getsockname"
  208.           "gethostbyname" "getservbyname"
  209.           "connect" "bcopy" "bcmp" "bzero")))
  210.  
  211. (defvar .junk-name. (make-array 1))
  212. (defvar .junk-address. (make-array 1 :element-type '(unsigned-byte 32)))
  213.  
  214. (defun entry-point-exists-p (string)
  215.   (setf (aref .junk-name. 0) string)
  216.   (setf (aref .junk-address. 0) 0)
  217.   (= 0 (get-entry-points .junk-name. .junk-address.)))
  218.  
  219. (eval-when (load eval)
  220.   (unless .lisp-listener-daemon-ff-loaded.
  221.     (excl::machine-case :host
  222.       ((:apollo :tek4300 :rs6000))
  223.       (t (unless (dolist (name .needed-funcs. t)
  224.            (if (not (entry-point-exists-p name))
  225.                (return nil)))
  226.        (princ ";  Loading TCP routines from C library...")
  227.        (force-output)
  228.        (unless (load ""
  229.              :verbose nil
  230.              :unreferenced-lib-names .needed-funcs.
  231.              #+(target sgi4d) :system-libraries
  232.              #+(target sgi4d) '("bsd")
  233.              )
  234.          (error "foreign load failed"))
  235.        (princ "done")
  236.        (terpri)
  237.        (force-output))))
  238.  
  239.     (setq .lisp-listener-daemon-ff-loaded. t)
  240.     (defforeign-list '((getuid :entry-point #,(convert-to-lang "getuid"))
  241.                (socket :entry-point #,(convert-to-lang "socket"))
  242.                (bind :entry-point #,(convert-to-lang "bind"))
  243.                (accept :entry-point #,(convert-to-lang "accept"))
  244.                (getsockname :entry-point #,(convert-to-lang
  245.                             "getsockname"))
  246.                (gethostbyname :entry-point #,(convert-to-lang
  247.                               "gethostbyname"))
  248.                (getservbyname :entry-point #,(convert-to-lang
  249.                               "getservbyname"))
  250.                (select :entry-point #,(convert-to-lang "select"))
  251.                (connect :entry-point #,(convert-to-lang "connect"))
  252.                (bcopy :entry-point #,(convert-to-lang "bcopy"))
  253.                (bzero :entry-point #,(convert-to-lang "bzero"))
  254.                (bcmp :entry-point #,(convert-to-lang "bcmp"))
  255.                (perror :entry-point #,(convert-to-lang "perror"))
  256.                (unix-listen :entry-point #,(convert-to-lang "listen"))
  257.                (unix-close :entry-point #,(convert-to-lang "close")))
  258.              :print nil)))
  259.  
  260. (if* (entry-point-exists-p (convert-to-lang "lisp_htons"))
  261.    then ;; Allegro CL 3.1 or later...
  262.     (defforeign-list '((lisp_htons :entry-point #,(convert-to-lang
  263.                                "lisp_htons"))
  264.                (lisp_htonl :entry-point #,(convert-to-lang
  265.                                "lisp_htonl"))
  266.                (lisp_ntohs :entry-point #,(convert-to-lang
  267.                                "lisp_ntohs"))
  268.                (lisp_ntohl :entry-point #,(convert-to-lang
  269.                                "lisp_ntohl")))
  270.              :print nil)
  271.    else ;; pre-3.1 Allegro CL.  Do it the hard way...
  272.     #+little-endian
  273.     (progn
  274.       (setf (symbol-function 'lisp_htons)
  275.         #'(lambda (x)
  276.         (logior (ash (logand x #x00ff) 8)
  277.             (ash (logand x #xff00) -8))))
  278.       (setf (symbol-function 'lisp_ntohs)
  279.         #'(lambda (x) (lisp_htons x)))
  280.  
  281.       (setf (symbol-function 'lisp_htonl)
  282.         #'(lambda (x)
  283.         (logior (ash (logand x #x000000ff)  24)
  284.             (ash (logand x #x0000ff00)   8)
  285.             (ash (logand x #x00ff0000)  -8)
  286.             (ash (logand x #xff000000) -24))))
  287.       (setf (symbol-function 'lisp_ntohl)
  288.         #'(lambda (x) (lisp_htonl x))))
  289.  
  290.     #+big-endian
  291.     (progn
  292.       (setf (symbol-function 'lisp_htons) #'(lambda (x) x))
  293.       (setf (symbol-function 'lisp_htonl) #'(lambda (x) x))
  294.       (setf (symbol-function 'lisp_ntohs) #'(lambda (x) x))
  295.       (setf (symbol-function 'lisp_ntohl) #'(lambda (x) x))))
  296.  
  297. (defun start-lisp-listener-daemon (&rest args)
  298.   "Starts a daemon process which listens to a socket for attempts to
  299. connect, and starts a lisp listener for each connection.  If the Lisp
  300. listener ever completes, the daemon makes sure its connection is closed."
  301.   (cond ((or (null .lisp-listener-daemon.)
  302.          (not (member .lisp-listener-daemon. mp::*all-processes*
  303.               :test #'eq)))
  304.      (setq .lisp-listener-daemon.
  305.        (apply #'process-run-function "TCP Listener Socket Daemon"
  306.           'lisp-server-socket-daemon
  307.           args))
  308.      (setf (mp::process-interruptable-p .lisp-listener-daemon.) nil)
  309.      (setf (getf (process-property-list .lisp-listener-daemon.)
  310.              ':survive-dumplisp)
  311.        'mp:process-kill))
  312.     (t (warn "the listener daemon is already running"))))
  313.  
  314. (defun kill-lisp-listener-daemon ()
  315.   (if* (or (null .lisp-listener-daemon.)
  316.        (not (member .lisp-listener-daemon. mp::*all-processes*
  317.             :test #'eq))
  318.        (progn
  319.          (mp:process-kill .lisp-listener-daemon.)
  320.          ;; this makes sure the kill is allowed to complete:
  321.          (mp:process-allow-schedule)
  322.          t))
  323.      then (setq .lisp-listener-daemon. nil)
  324.       t
  325.      else (error "couldn't kill the listener daemon")))
  326.  
  327. (defun start-lisp-server-process (&rest args 
  328.                   &key (server-name
  329.                     "TCP Listener Socket Daemon")
  330.                   &allow-other-keys)
  331.   (remf args :server-name)
  332.   (let ((proc
  333.      (apply #'process-run-function server-name
  334.         'lisp-server-socket-daemon
  335.         args)))
  336.     (setf (mp::process-interruptable-p proc) nil)
  337.     (setf (getf (process-property-list proc) ':survive-dumplisp) 't)
  338.     proc))
  339.  
  340. ;;;============================================================================
  341. ;;; New "lisp-server-socket-daemon" added 7/7/92 by Don Geddis
  342. ;;; Code from smh@franz.com (Steve Haflich)
  343.  
  344. (defun lisp-server-socket-daemon (&rest args &key
  345.                   (use-lep nil)
  346.                   (startup-stream-process
  347.                    (if use-lep 
  348.                        #'new-default-stream-startup
  349.                      #'default-stream-startup))
  350.                   ((:unix-domain *unix-domain*) *unix-domain*)
  351.                   (inet-port 0 inet-port-p)
  352.                    ((:inet-port-min *inet-port-min*) 
  353.                     (if inet-port-p inet-port *inet-port-min*))
  354.                    ((:inet-port-max *inet-port-max*) 
  355.                     (if inet-port-p inet-port *inet-port-max*))
  356.                   ((:socket-pathname *socket-pathname*) 
  357.                    *socket-pathname*)
  358.                   (raw-connection nil)
  359.                   &allow-other-keys
  360. ;;; We should decide this if we
  361. ;;; implement background streams 
  362. ;;; then it is no good just outputting to random locations.
  363.                   &aux 
  364.                   (*standard-output* *initial-terminal-io*)
  365.                   (*debug-io* *initial-terminal-io*)
  366.                   (*terminal-io* *initial-terminal-io*)
  367.                   (*error-output* *initial-terminal-io*))
  368.  
  369.   ;; Code added 7/19/92 by Don Geddis (Geddis@CS.Stanford.Edu)
  370.   (when raw-connection
  371.     (setq startup-stream-process #'don-stream-startup) )
  372.   
  373.   ;; use-lep should default to Nil and the LEP should start things up with T
  374.   ;; In this way a new lisp can work with the old Emacs interface
  375.   (block bad-news
  376.     (let (listen-socket-fd
  377.       (listen-sockaddr
  378.        (if *unix-domain*
  379.            (make-cstruct 'sockaddr-un)
  380.          (let ((sin (make-cstruct 'sockaddr-in)))
  381.            (bzero sin (ff::cstruct-length 'sockaddr-in))
  382.            sin)))
  383.       (timeval (make-cstruct 'timeval))
  384.       (mask-obj (make-cstruct 'unsigned-long))
  385.       (int (make-cstruct 'unsigned-long))
  386.       mask
  387.       stream
  388.       fd)
  389.     
  390.       (unless *socket-pathname*
  391.     (setq *socket-pathname* (format nil "/tmp/GnuToAcl~d" (getuid))))
  392.  
  393.       (setf (timeval-sec timeval) 0
  394.         (timeval-usec timeval) 0)
  395.       (unwind-protect
  396.       (progn
  397.         (if *unix-domain* (errorset (delete-file *socket-pathname*)))
  398.         (setq listen-socket-fd (socket
  399.                     (if *unix-domain* *af-unix* *af-inet*)
  400.                     *sock-stream*
  401.                     0))
  402.         (when (< listen-socket-fd 0)
  403.           (perror "socket")
  404.           (setq listen-socket-fd nil)
  405.           (return-from bad-news))
  406.         (mp::mpwatchfor listen-socket-fd)
  407.  
  408.         ;; Compute a select mask for the daemon's socket.
  409.         (setq mask (ash 1 listen-socket-fd))
  410.  
  411.         (if* *unix-domain*
  412.            then (setf (sockaddr-un-family listen-sockaddr) *af-unix*)
  413.            ;; Set pathname.
  414.             (dotimes (i (length *socket-pathname*)
  415.                    (setf (sockaddr-un-path listen-sockaddr i) 0))
  416.               (setf (sockaddr-un-path listen-sockaddr i)
  417.             (char-int (elt *socket-pathname* i))))
  418.             (unless (zerop (bind listen-socket-fd
  419.                      listen-sockaddr
  420.                      (+ (length *socket-pathname*) 2)))
  421.               (perror "bind")
  422.               (return-from bad-news))
  423.            else (setf (sockaddr-in-family listen-sockaddr) *af-inet*)
  424.             (do ((port *inet-port-min* (1+ port)))
  425.             ((progn
  426.                (setf (sockaddr-in-port listen-sockaddr)
  427.                  (lisp_htons (setq *inet-port-used* port)))
  428.                (zerop (bind listen-socket-fd
  429.                     listen-sockaddr
  430.                     (ff::cstruct-length 'sockaddr-in)))))
  431.               (if* (= port *inet-port-max*)
  432.              then (perror "bind")
  433.                   (return-from bad-news))))
  434.  
  435.         (finish-output)
  436.         (unless (zerop (unix-listen listen-socket-fd 5))
  437.           (perror "listen")
  438.           (return-from bad-news))
  439.         
  440.         (if* (not raw-connection)
  441.            then (format t "~d ~d ~a ~s ~d" 
  442.                 ;; ^Aport password case-mode socket-file ipc-version^A
  443.                 (if* *unix-domain*
  444.                    then 0
  445.                    else *inet-port-used*)
  446.                 (setq *inet-listener-password* 
  447.                   (random 1000000 (make-random-state t)))
  448.                 (case excl::*current-case-mode*
  449.                   ((:case-insensitive-upper
  450.                 :case-sensitive-upper) ":upper")
  451.                   ((:case-insensitive-lower
  452.                 :case-sensitive-lower) ":lower"))
  453.                 (if* *unix-domain*
  454.                    then *socket-pathname*
  455.                    else nil)
  456.                 *ipc-version*)
  457.             
  458.             (finish-output))
  459.         (loop
  460.           (process-wait "waiting for a connection"
  461.                 #'(lambda (mask mask-obj timeout)
  462.                 (setf (unsigned-long-unsigned-long
  463.                        mask-obj)
  464.                   mask)
  465.                 (not (zerop (select 32 mask-obj 0 0 timeout))))
  466.                 mask mask-obj timeval)
  467.           (setf (unsigned-long-unsigned-long int)
  468.         (if *unix-domain*
  469.             (ff::cstruct-length 'sockaddr-un)
  470.           (ff::cstruct-length 'sockaddr-in)))
  471.           (setq fd (accept listen-socket-fd listen-sockaddr int))
  472.           (finish-output *standard-output*)
  473.           (finish-output *error-output*)
  474.           (when (< fd 0)
  475.         (perror "accept")
  476.         (return-from bad-news))
  477.         
  478.           (setq stream (make-ipc-terminal-stream fd))
  479.        
  480.           ;; the first thing that comes over the stream is the name of the
  481.           ;; emacs buffer which was created--we name the process the
  482.           ;; same.
  483.           ;; For internet sockets, the next thing is the password.
  484.           (apply startup-stream-process stream :fd fd 
  485.              :listen-sockaddr listen-sockaddr  
  486.              ;; unused? -smh
  487.              ;; :listen-sockaddr-len int
  488.              args)))
  489.     (when listen-socket-fd
  490.       (mp::mpunwatchfor listen-socket-fd)
  491.       (unix-close listen-socket-fd)
  492.       (if* (not raw-connection)
  493.          then (setq .lisp-listener-daemon. nil))
  494.       ))))
  495.   (error "couldn't start listener daemon"))
  496.  
  497. ;; added function, corresponds to start-lisp-server-process
  498.  
  499. (defun kill-lisp-server-process (process)
  500.   (if* (or (null process)
  501.        (not (member process mp::*all-processes* :test #'eq))
  502.        (progn
  503.          (mp:process-kill process)
  504.          ;; this makes sure the kill is allowed to complete:
  505.          (mp:process-allow-schedule)
  506.          t))
  507.      then t
  508.      else (error "couldn't kill the listener daemon")))
  509.  
  510. ;;;============================================================================
  511.  
  512. ;;;; The first thing that comes over is the name of the protocol to use.
  513. ;;;; The old scheme is the listener protocol which means fire up a listener.
  514. ;;;; The LEP uses the :lep protocol
  515.  
  516. (defun new-default-stream-startup (stream &rest args)
  517.   (declare (ignore function))
  518.   (apply #'start-process-for-network-stream stream (read stream) args))
  519.  
  520. (defgeneric start-process-for-network-stream (stream protocol &rest arguments)
  521.         (:documentation "Create the process to serve the stream"))
  522.  
  523. (defmethod start-process-for-network-stream (stream (protocol (eql :listener)) &rest args)
  524.   (apply #'default-stream-startup stream args))
  525.  
  526. (defmethod start-process-for-network-stream (stream protocol &rest args)
  527.   (declare (ignore args))
  528.   (warn "Tried to start unknown protocol ~S for stream ~S" protocol stream))
  529.  
  530. ;;; Code added 7/19/92 by Don Geddis (Geddis@CS.Stanford.Edu)
  531. (defun don-stream-startup
  532.   (stream 
  533.    &key (function 'lisp-listener-with-stream-as-terminal-io)
  534.    listen-sockaddr fd
  535.    &allow-other-keys)
  536.   (process-run-function "Don-Process" function stream) )
  537.  
  538. (defun default-stream-startup (stream 
  539.                    &key (function 'lisp-listener-with-stream-as-terminal-io)
  540.                     listen-sockaddr fd
  541.                    &allow-other-keys)
  542.   (let ((proc-name (read stream))
  543.     (password
  544.      (if (null *unix-domain*)
  545.          (read stream))))
  546.     (if* *unix-domain*
  547.        then (process-run-function
  548.          proc-name
  549.          function
  550.          stream)
  551.        else (if* (not (and (numberp password)
  552.                (= password *inet-listener-password*)))
  553.            then (let ((hostaddr
  554.                (lisp_ntohl
  555.                 (sockaddr-in-addr listen-sockaddr))))
  556.               (format t ";; access denied for host ")
  557.               (format-in-addr t hostaddr)
  558.               (format t ", password ~s~%" password))
  559.             (refuse-connection fd)
  560.            else (process-run-function
  561.              proc-name
  562.              function
  563.              stream)))))
  564.  
  565. (defun refuse-connection (fd &aux s)
  566.   (setq s (make-ipc-terminal-stream fd))
  567.   #-(version>= 4 0)
  568.   (setf (excl::sm_read-char s) #'mp::stm-bterm-read-string-char-wait)
  569.   (format s "connection refused.~%")
  570.   (force-output s)
  571.   #-(version>= 4 0) (setf (excl::sm_bterm-out-pos s) 0)
  572.   #+(version>= 4 0) (excl::clear-output-1 s)
  573.   (close s))
  574.  
  575. (defun lisp-listener-with-stream-as-terminal-io (s)
  576.   (unwind-protect
  577.       #+(version>= 4 0)
  578.       (tpl:start-interactive-top-level
  579.        s 'tpl:top-level-read-eval-print-loop nil)
  580.       #-(version>= 4 0)
  581.       (progn
  582.     (setf (excl::sm_read-char s) #'mp::stm-bterm-read-string-char-wait)
  583.     (tpl:start-interactive-top-level
  584.      s 'tpl:top-level-read-eval-print-loop nil))
  585.     ;; This next crock is to prevent the force-output done by close from
  586.     ;; signalling an error if there are characters buffered to the output
  587.     ;; stream, which there will be if the remote client closed the connection.
  588.     ;; This should be changed to a clear-output once that works on a buffered
  589.     ;; terminal stream.
  590.     #-(version>= 4 0) (setf (excl::sm_bterm-out-pos s) 0)
  591.     #+(version>= 4 0) (excl::clear-output-1 s)
  592.     (close s)))
  593.  
  594. (defun open-network-stream (&key host port socket-file)
  595.   "Open a stream to a port, which is a TCP/IP communication channel.  There
  596. are two types of ports supported, UNIX and INTERNET domain.  The domain is
  597. chosen based on the keyword arguments supplied:
  598. For internet domain:
  599.  HOST is the string host name or an integer internet address.
  600.  PORT is the string service name or a port number.
  601. For Unix domain:
  602.  SOCKET-FILE is the string pathname of the socket."
  603.   (when (or (and host port socket-file)
  604.         (and (or (null host) (null port))
  605.          (null socket-file)))
  606.     (error "Must either supply HOST and PORT *or* SOCKET-FILE keywords."))
  607.   (if* socket-file
  608.      then ;; UNIX domain
  609.       (let ((server (make-cstruct 'sockaddr-un))
  610.         socket-fd)
  611.         (setf (sockaddr-un-family server) *af-unix*)
  612.         (dotimes (i (length socket-file)
  613.               (setf (sockaddr-un-path server i) 0))
  614.           (setf (sockaddr-un-path server i)
  615.         (char-int (elt socket-file i))))
  616.         (setq socket-fd (socket *af-unix* *sock-stream* 0))
  617.         (if (< (connect socket-fd server (+ 2 (length socket-file))) 0)
  618.         (error "connect failed to ~s" socket-file))
  619.         (make-ipc-terminal-stream socket-fd))
  620.      else ;; INTERNET domain
  621.       (let (sock server hostaddress)
  622.         ;; Open a socket
  623.         (when (< (setf sock (socket *af-inet* *sock-stream* 0)) 0)
  624.           (error "couldn't open socket"))
  625.         ;; construct a socket address
  626.         (setf server (make-cstruct 'sockaddr-in))
  627.         (bzero server (ff::cstruct-length 'sockaddr-in))
  628.         (if* (integerp host)
  629.            then (setq hostaddress (lisp_htonl host))
  630.          elseif (stringp host)
  631.            then (when (= (setq hostaddress (gethostbyname host)) 0)
  632.               (error "unknown host: ~a" host))
  633.             (if (not (= 4 (hostent-length hostaddress)))
  634.             (error "address length not 4"))
  635.             (setq hostaddress
  636.               (let ((addr (hostent-addr hostaddress)))
  637.             (si:memref-int
  638.              ;; SunOS 4.0 requires an extra indirection
  639.              (if (or (member comp::.target.
  640.                      '(:hp :sgi4d :sony :dec3100 :rs6000)
  641.                      :test #'eq)
  642.                  (probe-file "/lib/ld.so"))
  643.                  (si:memref-int addr 0 0 :unsigned-long)
  644.                addr)
  645.              0 0 :unsigned-long)))
  646.            else (error "HOST not a string or integer internet address:"
  647.                host))
  648.         (setf (sockaddr-in-addr server) hostaddress)
  649.  
  650.         (setf (sockaddr-in-port server)
  651.           (if* (integerp port)
  652.          then (lisp_htons port)
  653.            elseif (stringp port)
  654.          then (let ((serv (getservbyname port "tcp")))
  655.             (if* (= 0 serv)
  656.                then (error "Unknown service name: ~s" port))
  657.             (servent-port serv))
  658.          else (error "PORT not a string or integer internet service:"
  659.                  port)))
  660.  
  661.         (setf (sockaddr-in-family server) *af-inet*)
  662.         ;; open the connection
  663.         ;; Modified 7/21/92 by Don Geddis (Geddis@CS.Stanford.Edu)
  664.         ;;   so that connection failure returns nil instead of an error
  665.         ;; 8/12/92 Replace perror/error with format to *error-output*
  666.         (if (< (connect sock server (ff::cstruct-length 'sockaddr-in)) 0)
  667.         (progn
  668.           (format *error-output* "Connect failure: ")
  669.           (unix-close sock)
  670.           (format *error-output* "couldn't connect to socket")
  671.           nil )
  672.           ;; build and return the stream
  673.           (make-ipc-terminal-stream sock)))))
  674.  
  675. (defun make-ipc-terminal-stream (fd)
  676.   #+allegro-v3.1 (excl::make-buffered-terminal-stream fd fd t t)
  677.   #+(version>= 4 0) (make-instance 'excl::bidirectional-terminal-stream
  678.            :fn-in  fd
  679.            :fn-out fd)
  680.   )
  681.  
  682. (defun format-in-addr (stm addr)    ; assumes host byte order
  683.   (format stm "[~11,1,1,'.<~2,'0D~;~2,'0D~;~2,'0D~;~2,'0D~>]"
  684.       (ldb (byte 8 24) addr)
  685.       (ldb (byte 8 16) addr)
  686.       (ldb (byte 8  8) addr)
  687.       (ldb (byte 8  0) addr)))
  688.  
  689. #|
  690. ;;; Allegro Common Lisp as the client side of `finger'.
  691. ;;; This is a simple demo of how easily a lisp program can become the
  692. ;;; client of a client/server connection.
  693.  
  694. (defun finger (host &optional (name ""))
  695.   (with-open-stream (s (open-network-stream :host host
  696.                         :port "finger"))
  697.     ;; The first interaction of the finger protocol is to send the
  698.     ;; name in which we are interested.  The empty string will
  699.     ;; report on all current users.
  700.     (format s "~a~%" name)
  701.     ;; Then we read back multiple lines of information.
  702.     (do ((line (read-line s nil nil) (read-line s nil nil)))
  703.     ((null line) (values))
  704.       ;; The returned data contains extra #\return characters
  705.       ;; which we might not want to print.
  706.       (write-line (remove #\return line)))))
  707. |#
  708.  
  709. -------------------------------------------------------------------------------
  710. MY SAMPLE CODE TO USE IPC.CL
  711.  
  712. (unless (boundp 'new-ipc-installed)
  713.   (load "/local/lib/cl/code/ipc") )    ; Use the modified IPC code
  714.  
  715. ;;;----------------------------------------------------------------------------
  716.  
  717. (defconstant *myself* 2130706433 "Self IP address: 127.0.0.1")
  718.  
  719. (defun test-port-send
  720.     (port &optional (str (format nil "Test of send to ~A" port)))
  721.   (with-open-stream
  722.       (s (ipc:open-network-stream :host *myself* :port port))
  723.     (format s "~A~%" str)
  724.     (force-output s) ))
  725.  
  726. (defvar *test-process* nil)
  727.  
  728. (defun test-port-daemon (tcp-stream)
  729.   (let ((str (read-line tcp-stream)))
  730.     (format t "Got ~S~%" str) ))
  731.  
  732. (defun setup-test-daemon (port)
  733.   (when *test-process*
  734.     (ipc::kill-lisp-server-process *test-process*) )
  735.   (setq *test-process*
  736.     (ipc::start-lisp-server-process
  737.      :server-name "Test Daemon" :function #'test-port-daemon
  738.      :inet-port port :raw-connection t ) ))
  739.  
  740. ;;;----------------------------------------------------------------------------
  741.  
  742. Hope this helps.
  743.  
  744.     -- Don Geddis
  745. -- 
  746. Don Geddis        "It's too bad that whole families have to be torn apart
  747. Geddis@CS.Stanford.Edu     by something as simple as wild dogs." -- DT on SNL
  748. Hi, I'm a replicating signature virus!  Join in the fun and copy me into yours!
  749.