File:  [CSRG BSD Unix] / 43BSDReno / contrib / emacs-18.55 / dist-1.3 / fi / ipc.cl
Revision 1.1: download - view: text, annotated - select for diffs
Tue Apr 24 16:12:57 2018 UTC (8 years, 1 month ago) by root
CVS tags: MAIN, HEAD
Initial revision

;;					-[Mon Nov 28 13:35:34 1988 by layer]-
;;
;; Allegro CL IPC interface
;;
;; copyright (c) 1987, 1988 Franz Inc, Berkeley, Ca.
;;
;; The software, data and information contained herein are proprietary
;; to, and comprise valuable trade secrets of, Franz, Inc.  They are
;; given in confidence by Franz, Inc. pursuant to a written license
;; agreement, and stored only in accordance with the terms of such license.
;;
;; Restricted Rights Legend
;; ------------------------
;; Use, duplication, and disclosure by the Government are subject to
;; restrictions of Restricted Rights for Commercial Software developed
;; at private expense as specified in DOD FAR 52.227-7013 (c) (1) (ii).
;;
;;
;; $Header: /var/lib/cvsd/repos/CSRG/43BSDReno/contrib/emacs-18.55/dist-1.3/fi/ipc.cl,v 1.1 2018/04/24 16:12:57 root Exp $
;; $Locker:  $
;;
;; This code is a preliminary IPC interface for ExCL. The functionality
;; will be extended apace, but right now it only implements a Common Lisp
;; Server.  The server can be started by a CL and it establishes a daemon
;; listening to a socket.  Any process wanting to talk to the lisp
;; can connect to the socket and a new Lisp Listener will be started.

(provide :ipc)

(in-package :ipc :use '(:lisp :excl :ff :mp))

(export '(start-lisp-listener-daemon open-network-stream
	  *unix-domain* *inet-port*))

(require :process)
(require :foreign)
(require :cstructs)

(defvar *unix-domain* t
  "If non-nil then use a UNIX domain socket, otherwise use an internet
domain port (see *inet-port* variable).")

(defparameter *inet-port* 6789
  "The internet service port number on which Lisp listens for connections.
The value is this variable is only used when *unix-domain* is non-nil, in
which case a UNIX domain socket is used.")

(defconstant *af-unix* 1
  "The AF_UNIX constant from /usr/include/sys/socket.h.")

(defconstant *af-inet* 2
  "The AF_INET constant from /usr/include/sys/socket.h.")

(defconstant *sock-stream* 1
  "The SOCK_STREAM constant from /usr/include/sys/socket.h.")

(defvar *junk-name* (make-array 1))
(defvar *junk-address* (make-array 1 :element-type '(unsigned-byte 32)))

(defun entry-point-exists-p (string)
  (setf (aref *junk-name* 0) string)
  (setf (aref *junk-address* 0) 0)
  (= 0 (get-entry-points *junk-name* *junk-address*)))

(defvar lisp-listener-daemon-ff-loaded nil)
(defvar lisp-listener-daemon nil)

(defparameter *needed-funcs*
  (mapcar #'convert-to-lang
	  '("socket" "bind" "listen" "accept" "getsockname" "gethostbyname"
	    "connect" "bcopy" "bcmp" "bzero")))

(eval-when (load eval)
  (unless lisp-listener-daemon-ff-loaded
    (unless (eql (excl::machine-code) '#.comp::machine-code-tek4300)
      (unless (or (eq '#.comp::machine-code-apollo (excl::machine-code))
		  (dolist (name *needed-funcs* t)
		    (if (not (entry-point-exists-p name))
			(return nil))))
	(princ ";  Loading from C library...")
	(force-output)
	(unless (load "" :verbose nil :unreferenced-lib-names *needed-funcs*)
	  (error "foreign load failed"))
	(princ "done")
	(terpri)))
    (setq lisp-listener-daemon-ff-loaded t)
    (defforeign-list '((getuid) (socket) (bind) (accept)
		       (getsockname) (gethostbyname) (select)
		       (connect) (bcopy) (bzero) (bcmp) (perror)
		       (unix-listen :entry-point #,(convert-to-lang "listen"))
		       (unix-close :entry-point #,(convert-to-lang "close")))
	:print nil)))

(defcstruct sockaddr-in
  (family :unsigned-short)
  (port :unsigned-short)
  (addr :unsigned-long)
  (zero 8 :unsigned-byte))

(defcstruct sockaddr-un
  (family :unsigned-short)
  (path 109 :char))

(defcstruct timeval
  (sec :long)
  (usec :long))

(defcstruct unsigned-long
  (unsigned-long :unsigned-long))

;; from /usr/include/netdb.h
(defcstruct (hostent :malloc)
  (name * :char)
  (aliases * * :char)
  (addrtype :long)
  (length :long)
  (addr * :char))

(defun start-lisp-listener-daemon ()
  "This function starts a process which listens to a socket for attempts to
connect, and starts a lisp listener for each connection.  If the Lisp
listener ever completes, it makes sure files are closed."
  (unless lisp-listener-daemon
    (setq lisp-listener-daemon
      (process-run-function "TCP Listener Socket Daemon"
			    'lisp-listener-socket-daemon))
    (setf (getf (process-property-list lisp-listener-daemon) ':no-interrupts)
          t)))

(defvar *socket-pathname* nil)

(defun lisp-listener-socket-daemon ()
  (let (listen-socket-fd
	(listen-sockaddr
	 (if *unix-domain*
	     (make-cstruct 'sockaddr-un)
	   (make-cstruct 'sockaddr-in)))
	(timeval (make-cstruct 'timeval))
	(mask-obj (make-cstruct 'unsigned-long))
	(int (make-cstruct 'unsigned-long))
	mask
	stream
	proc-name
	fd)
    
    (unless *socket-pathname*
      (format nil "~a/~a" (sys:getenv "HOME") ".excl_to_emacs"))

    (setf (timeval-sec timeval) 0
	  (timeval-usec timeval) 0)
    (unwind-protect
	(progn
	  (if *unix-domain* (errorset (delete-file *socket-pathname*)))
	  (setq listen-socket-fd (socket
				  (if *unix-domain* *af-unix* *af-inet*)
				  *sock-stream*
				  0))
	  (when (< listen-socket-fd 0)
	    (perror "socket")
	    (setq listen-socket-fd nil)
	    (return-from lisp-listener-socket-daemon nil))
	  (mp::mpwatchfor listen-socket-fd)

	  ;; Compute a select mask for the daemon's socket.
	  (setq mask (ash 1 listen-socket-fd))

	  (if* *unix-domain*
	     then (setf (sockaddr-un-family listen-sockaddr) *af-unix*)
		  ;; Set pathname.
		  (dotimes (i (length *socket-pathname*)
			    (setf (sockaddr-un-path listen-sockaddr i) 0))
		    (setf (sockaddr-un-path listen-sockaddr i)
		      (char-int (elt *socket-pathname* i))))
	     else ;; a crock:
		  (bzero listen-sockaddr (ff::cstruct-len 'sockaddr-in))
		  (setf (sockaddr-in-family listen-sockaddr) *af-inet*
			(sockaddr-in-port listen-sockaddr) *inet-port*))
	  
	  (unless (zerop (bind listen-socket-fd
			       listen-sockaddr
			       (if *unix-domain*
				   (+ (length *socket-pathname*) 2)
				 (ff::cstruct-len 'sockaddr-in))))
	    (perror "bind")
	    (return-from lisp-listener-socket-daemon nil))

	  (unless (zerop (unix-listen listen-socket-fd 5))
	    (perror "listen")
	    (return-from lisp-listener-socket-daemon nil))
	  (loop
	   (process-wait "waiting for a connection"
			 #'(lambda (mask mask-obj timeout)
			     (setf (unsigned-long-unsigned-long mask-obj) mask)
			     (not (zerop (select 32 mask-obj 0 0 timeout))))
			 mask mask-obj timeval)
	   (setf (unsigned-long-unsigned-long int)
	     (if *unix-domain*
		 (ff::cstruct-len 'sockaddr-un)
	       (ff::cstruct-len 'sockaddr-in)))
	   (setq fd (accept listen-socket-fd listen-sockaddr int))
	   (when (< fd 0)
	     (perror "accept")
	     (return-from lisp-listener-socket-daemon nil))
	   
	   (setq stream
	     (excl::make-buffered-terminal-stream fd fd t t))
	   
	   ;; the first thing that comes over the stream is the name of the
	   ;; emacs buffer which was created--we name the process the same.
	   (setq proc-name (read stream))
	   
	   (if* *unix-domain*
	      then (process-run-function
		    proc-name
		    'lisp-listener-with-stream-as-terminal-io stream)
	      else (let ((hostaddr (logand
				    (sockaddr-in-addr listen-sockaddr) #xff)))
		     (format t ";;; starting listener-~d (host ~d)~%" fd
			     hostaddr)
		     (if* (and nil
			       ;; the next line checks that the connection
			       ;; is coming from the current machine
			       (not (eql 1 hostaddr))
			       )
			then (format t ";;; access denied for addr ~s~%"
				     hostaddr)
			     (refuse-connection fd)
			else (process-run-function
			      proc-name
			      'lisp-listener-with-stream-as-terminal-io
			      stream))))))
      (when listen-socket-fd
	(mp::mpunwatchfor listen-socket-fd)
	(unix-close listen-socket-fd)
	(setq lisp-listener-daemon nil)))))

(defun refuse-connection (fd &aux s)
  (setq s (excl::make-buffered-terminal-stream fd fd t t))
  (setf (excl::sm_read-char s) #'mp::stm-bterm-read-string-char-wait)
  (format s "connection refused.~%")
  (force-output s)
  (setf (excl::sm_bterm-out-pos s) 0)
  (close s))

(defun lisp-listener-with-stream-as-terminal-io (s)
  (unwind-protect
      (progn
	(setf (excl::sm_read-char s) #'mp::stm-bterm-read-string-char-wait)
	(tpl:start-interactive-top-level
	 s 'tpl:top-level-read-eval-print-loop nil))
    ;; This next crock is to prevent the force-output done by close from
    ;; signalling an error if there are characters buffered to the output
    ;; stream, which there will be if the remote client closed the connection.
    ;; This should be changed to a clear-output once that works on a buffered
    ;; terminal stream.
    (setf (excl::sm_bterm-out-pos s) 0)
    (close s)))

(defun open-network-stream (&key host port socket-file)
  "Open a stream to a port, which is a TCP/IP communication channel.  There
are two types of ports supported, UNIX and INTERNET domain.  The domain is
chosen based on the keyword arguments actually used: HOST and PORT are for
internet domain ports and SOCKET-FILE is for unix domain ports."
  (if (and (or (null host) (null port))
	   (null socket-file))
      (error "Must either supply HOST and PORT *or* SOCKET-FILE keywords."))
  (if* socket-file
     then ;; UNIX domain
	  (let ((server (make-cstruct 'sockaddr-un))
		socket-fd)
	    (setf (sockaddr-un-family server) *af-unix*)
	    (dotimes (i (length socket-file)
		      (setf (sockaddr-un-path server i) 0))
	      (setf (sockaddr-un-path server i)
		(char-int (elt socket-file i))))
	    (setq socket-fd (socket *af-unix* *sock-stream* 0))
	    (if (< (connect socket-fd server (+ 2 (length socket-file))) 0)
		(error "connect failed to ~s" socket-file))
	    (excl::make-buffered-terminal-stream socket-fd socket-fd t t))
     else ;; INTERNET domain
	  (let (sock server hostaddress)
	    ;; Open a socket
	    (when (< (setf sock (socket *af-inet* *sock-stream* 0)) 0)
	      (error "couldn't open socket"))
	    ;; construct a socket address
	    (setf server (make-cstruct 'sockaddr-in))
	    (bzero server (ff::cstruct-len 'sockaddr-in))
	    (when (= (setf hostaddress (gethostbyname host)) 0)
	      (error "unknown host: ~a" host))
	    ;; This next line is what they invented the word "hack" for.
	    ;; If I knew how to pass the address of the addr field in server, I
	    ;; wouldn't have to do this.
	    ;; Be sure that this precedes writes to other fields in server.
	    (if (not (= 4 (hostent-length hostaddress)))
		(error "address length not 4"))
	    (setf (sockaddr-in-addr server)
	      (si:memref-int (hostent-addr hostaddress) 0 0 :unsigned-long))
	    (setf (sockaddr-in-family server) *af-inet*)
	    (setf (sockaddr-in-port server) port)
	    ;; open the connection
	    (when (< (connect sock server (ff::cstruct-len 'sockaddr-in)) 0)
	      (unix-close sock)
	      (error "couldn't connect to socket"))
	    ;; build and return the stream
	    (excl::make-buffered-terminal-stream sock sock t t))))

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.