|
|
1.1 ! root 1: ;; -[Mon Nov 28 13:35:34 1988 by layer]- ! 2: ;; ! 3: ;; Allegro CL IPC interface ! 4: ;; ! 5: ;; copyright (c) 1987, 1988 Franz Inc, Berkeley, Ca. ! 6: ;; ! 7: ;; The software, data and information contained herein are proprietary ! 8: ;; to, and comprise valuable trade secrets of, Franz, Inc. They are ! 9: ;; given in confidence by Franz, Inc. pursuant to a written license ! 10: ;; agreement, and stored only in accordance with the terms of such license. ! 11: ;; ! 12: ;; Restricted Rights Legend ! 13: ;; ------------------------ ! 14: ;; Use, duplication, and disclosure by the Government are subject to ! 15: ;; restrictions of Restricted Rights for Commercial Software developed ! 16: ;; at private expense as specified in DOD FAR 52.227-7013 (c) (1) (ii). ! 17: ;; ! 18: ;; ! 19: ;; $Header: ipc.cl,v 1.20 89/02/14 17:14:34 layer Exp $ ! 20: ;; $Locker: $ ! 21: ;; ! 22: ;; This code is a preliminary IPC interface for ExCL. The functionality ! 23: ;; will be extended apace, but right now it only implements a Common Lisp ! 24: ;; Server. The server can be started by a CL and it establishes a daemon ! 25: ;; listening to a socket. Any process wanting to talk to the lisp ! 26: ;; can connect to the socket and a new Lisp Listener will be started. ! 27: ! 28: (provide :ipc) ! 29: ! 30: (in-package :ipc :use '(:lisp :excl :ff :mp)) ! 31: ! 32: (export '(start-lisp-listener-daemon open-network-stream ! 33: *unix-domain* *inet-port*)) ! 34: ! 35: (require :process) ! 36: (require :foreign) ! 37: (require :cstructs) ! 38: ! 39: (defvar *unix-domain* t ! 40: "If non-nil then use a UNIX domain socket, otherwise use an internet ! 41: domain port (see *inet-port* variable).") ! 42: ! 43: (defparameter *inet-port* 6789 ! 44: "The internet service port number on which Lisp listens for connections. ! 45: The value is this variable is only used when *unix-domain* is non-nil, in ! 46: which case a UNIX domain socket is used.") ! 47: ! 48: (defconstant *af-unix* 1 ! 49: "The AF_UNIX constant from /usr/include/sys/socket.h.") ! 50: ! 51: (defconstant *af-inet* 2 ! 52: "The AF_INET constant from /usr/include/sys/socket.h.") ! 53: ! 54: (defconstant *sock-stream* 1 ! 55: "The SOCK_STREAM constant from /usr/include/sys/socket.h.") ! 56: ! 57: (defvar *junk-name* (make-array 1)) ! 58: (defvar *junk-address* (make-array 1 :element-type '(unsigned-byte 32))) ! 59: ! 60: (defun entry-point-exists-p (string) ! 61: (setf (aref *junk-name* 0) string) ! 62: (setf (aref *junk-address* 0) 0) ! 63: (= 0 (get-entry-points *junk-name* *junk-address*))) ! 64: ! 65: (defvar lisp-listener-daemon-ff-loaded nil) ! 66: (defvar lisp-listener-daemon nil) ! 67: ! 68: (defparameter *needed-funcs* ! 69: (mapcar #'convert-to-lang ! 70: '("socket" "bind" "listen" "accept" "getsockname" "gethostbyname" ! 71: "connect" "bcopy" "bcmp" "bzero"))) ! 72: ! 73: (eval-when (load eval) ! 74: (unless lisp-listener-daemon-ff-loaded ! 75: (unless (eql (excl::machine-code) '#.comp::machine-code-tek4300) ! 76: (unless (or (eq '#.comp::machine-code-apollo (excl::machine-code)) ! 77: (dolist (name *needed-funcs* t) ! 78: (if (not (entry-point-exists-p name)) ! 79: (return nil)))) ! 80: (princ "; Loading from C library...") ! 81: (force-output) ! 82: (unless (load "" :verbose nil :unreferenced-lib-names *needed-funcs*) ! 83: (error "foreign load failed")) ! 84: (princ "done") ! 85: (terpri))) ! 86: (setq lisp-listener-daemon-ff-loaded t) ! 87: (defforeign-list '((getuid) (socket) (bind) (accept) ! 88: (getsockname) (gethostbyname) (select) ! 89: (connect) (bcopy) (bzero) (bcmp) (perror) ! 90: (unix-listen :entry-point #,(convert-to-lang "listen")) ! 91: (unix-close :entry-point #,(convert-to-lang "close"))) ! 92: :print nil))) ! 93: ! 94: (defcstruct sockaddr-in ! 95: (family :unsigned-short) ! 96: (port :unsigned-short) ! 97: (addr :unsigned-long) ! 98: (zero 8 :unsigned-byte)) ! 99: ! 100: (defcstruct sockaddr-un ! 101: (family :unsigned-short) ! 102: (path 109 :char)) ! 103: ! 104: (defcstruct timeval ! 105: (sec :long) ! 106: (usec :long)) ! 107: ! 108: (defcstruct unsigned-long ! 109: (unsigned-long :unsigned-long)) ! 110: ! 111: ;; from /usr/include/netdb.h ! 112: (defcstruct (hostent :malloc) ! 113: (name * :char) ! 114: (aliases * * :char) ! 115: (addrtype :long) ! 116: (length :long) ! 117: (addr * :char)) ! 118: ! 119: (defun start-lisp-listener-daemon () ! 120: "This function starts a process which listens to a socket for attempts to ! 121: connect, and starts a lisp listener for each connection. If the Lisp ! 122: listener ever completes, it makes sure files are closed." ! 123: (unless lisp-listener-daemon ! 124: (setq lisp-listener-daemon ! 125: (process-run-function "TCP Listener Socket Daemon" ! 126: 'lisp-listener-socket-daemon)) ! 127: (setf (getf (process-property-list lisp-listener-daemon) ':no-interrupts) ! 128: t))) ! 129: ! 130: (defvar *socket-pathname* nil) ! 131: ! 132: (defun lisp-listener-socket-daemon () ! 133: (let (listen-socket-fd ! 134: (listen-sockaddr ! 135: (if *unix-domain* ! 136: (make-cstruct 'sockaddr-un) ! 137: (make-cstruct 'sockaddr-in))) ! 138: (timeval (make-cstruct 'timeval)) ! 139: (mask-obj (make-cstruct 'unsigned-long)) ! 140: (int (make-cstruct 'unsigned-long)) ! 141: mask ! 142: stream ! 143: proc-name ! 144: fd) ! 145: ! 146: (unless *socket-pathname* ! 147: (format nil "~a/~a" (sys:getenv "HOME") ".excl_to_emacs")) ! 148: ! 149: (setf (timeval-sec timeval) 0 ! 150: (timeval-usec timeval) 0) ! 151: (unwind-protect ! 152: (progn ! 153: (if *unix-domain* (errorset (delete-file *socket-pathname*))) ! 154: (setq listen-socket-fd (socket ! 155: (if *unix-domain* *af-unix* *af-inet*) ! 156: *sock-stream* ! 157: 0)) ! 158: (when (< listen-socket-fd 0) ! 159: (perror "socket") ! 160: (setq listen-socket-fd nil) ! 161: (return-from lisp-listener-socket-daemon nil)) ! 162: (mp::mpwatchfor listen-socket-fd) ! 163: ! 164: ;; Compute a select mask for the daemon's socket. ! 165: (setq mask (ash 1 listen-socket-fd)) ! 166: ! 167: (if* *unix-domain* ! 168: then (setf (sockaddr-un-family listen-sockaddr) *af-unix*) ! 169: ;; Set pathname. ! 170: (dotimes (i (length *socket-pathname*) ! 171: (setf (sockaddr-un-path listen-sockaddr i) 0)) ! 172: (setf (sockaddr-un-path listen-sockaddr i) ! 173: (char-int (elt *socket-pathname* i)))) ! 174: else ;; a crock: ! 175: (bzero listen-sockaddr (ff::cstruct-len 'sockaddr-in)) ! 176: (setf (sockaddr-in-family listen-sockaddr) *af-inet* ! 177: (sockaddr-in-port listen-sockaddr) *inet-port*)) ! 178: ! 179: (unless (zerop (bind listen-socket-fd ! 180: listen-sockaddr ! 181: (if *unix-domain* ! 182: (+ (length *socket-pathname*) 2) ! 183: (ff::cstruct-len 'sockaddr-in)))) ! 184: (perror "bind") ! 185: (return-from lisp-listener-socket-daemon nil)) ! 186: ! 187: (unless (zerop (unix-listen listen-socket-fd 5)) ! 188: (perror "listen") ! 189: (return-from lisp-listener-socket-daemon nil)) ! 190: (loop ! 191: (process-wait "waiting for a connection" ! 192: #'(lambda (mask mask-obj timeout) ! 193: (setf (unsigned-long-unsigned-long mask-obj) mask) ! 194: (not (zerop (select 32 mask-obj 0 0 timeout)))) ! 195: mask mask-obj timeval) ! 196: (setf (unsigned-long-unsigned-long int) ! 197: (if *unix-domain* ! 198: (ff::cstruct-len 'sockaddr-un) ! 199: (ff::cstruct-len 'sockaddr-in))) ! 200: (setq fd (accept listen-socket-fd listen-sockaddr int)) ! 201: (when (< fd 0) ! 202: (perror "accept") ! 203: (return-from lisp-listener-socket-daemon nil)) ! 204: ! 205: (setq stream ! 206: (excl::make-buffered-terminal-stream fd fd t t)) ! 207: ! 208: ;; the first thing that comes over the stream is the name of the ! 209: ;; emacs buffer which was created--we name the process the same. ! 210: (setq proc-name (read stream)) ! 211: ! 212: (if* *unix-domain* ! 213: then (process-run-function ! 214: proc-name ! 215: 'lisp-listener-with-stream-as-terminal-io stream) ! 216: else (let ((hostaddr (logand ! 217: (sockaddr-in-addr listen-sockaddr) #xff))) ! 218: (format t ";;; starting listener-~d (host ~d)~%" fd ! 219: hostaddr) ! 220: (if* (and nil ! 221: ;; the next line checks that the connection ! 222: ;; is coming from the current machine ! 223: (not (eql 1 hostaddr)) ! 224: ) ! 225: then (format t ";;; access denied for addr ~s~%" ! 226: hostaddr) ! 227: (refuse-connection fd) ! 228: else (process-run-function ! 229: proc-name ! 230: 'lisp-listener-with-stream-as-terminal-io ! 231: stream)))))) ! 232: (when listen-socket-fd ! 233: (mp::mpunwatchfor listen-socket-fd) ! 234: (unix-close listen-socket-fd) ! 235: (setq lisp-listener-daemon nil))))) ! 236: ! 237: (defun refuse-connection (fd &aux s) ! 238: (setq s (excl::make-buffered-terminal-stream fd fd t t)) ! 239: (setf (excl::sm_read-char s) #'mp::stm-bterm-read-string-char-wait) ! 240: (format s "connection refused.~%") ! 241: (force-output s) ! 242: (setf (excl::sm_bterm-out-pos s) 0) ! 243: (close s)) ! 244: ! 245: (defun lisp-listener-with-stream-as-terminal-io (s) ! 246: (unwind-protect ! 247: (progn ! 248: (setf (excl::sm_read-char s) #'mp::stm-bterm-read-string-char-wait) ! 249: (tpl:start-interactive-top-level ! 250: s 'tpl:top-level-read-eval-print-loop nil)) ! 251: ;; This next crock is to prevent the force-output done by close from ! 252: ;; signalling an error if there are characters buffered to the output ! 253: ;; stream, which there will be if the remote client closed the connection. ! 254: ;; This should be changed to a clear-output once that works on a buffered ! 255: ;; terminal stream. ! 256: (setf (excl::sm_bterm-out-pos s) 0) ! 257: (close s))) ! 258: ! 259: (defun open-network-stream (&key host port socket-file) ! 260: "Open a stream to a port, which is a TCP/IP communication channel. There ! 261: are two types of ports supported, UNIX and INTERNET domain. The domain is ! 262: chosen based on the keyword arguments actually used: HOST and PORT are for ! 263: internet domain ports and SOCKET-FILE is for unix domain ports." ! 264: (if (and (or (null host) (null port)) ! 265: (null socket-file)) ! 266: (error "Must either supply HOST and PORT *or* SOCKET-FILE keywords.")) ! 267: (if* socket-file ! 268: then ;; UNIX domain ! 269: (let ((server (make-cstruct 'sockaddr-un)) ! 270: socket-fd) ! 271: (setf (sockaddr-un-family server) *af-unix*) ! 272: (dotimes (i (length socket-file) ! 273: (setf (sockaddr-un-path server i) 0)) ! 274: (setf (sockaddr-un-path server i) ! 275: (char-int (elt socket-file i)))) ! 276: (setq socket-fd (socket *af-unix* *sock-stream* 0)) ! 277: (if (< (connect socket-fd server (+ 2 (length socket-file))) 0) ! 278: (error "connect failed to ~s" socket-file)) ! 279: (excl::make-buffered-terminal-stream socket-fd socket-fd t t)) ! 280: else ;; INTERNET domain ! 281: (let (sock server hostaddress) ! 282: ;; Open a socket ! 283: (when (< (setf sock (socket *af-inet* *sock-stream* 0)) 0) ! 284: (error "couldn't open socket")) ! 285: ;; construct a socket address ! 286: (setf server (make-cstruct 'sockaddr-in)) ! 287: (bzero server (ff::cstruct-len 'sockaddr-in)) ! 288: (when (= (setf hostaddress (gethostbyname host)) 0) ! 289: (error "unknown host: ~a" host)) ! 290: ;; This next line is what they invented the word "hack" for. ! 291: ;; If I knew how to pass the address of the addr field in server, I ! 292: ;; wouldn't have to do this. ! 293: ;; Be sure that this precedes writes to other fields in server. ! 294: (if (not (= 4 (hostent-length hostaddress))) ! 295: (error "address length not 4")) ! 296: (setf (sockaddr-in-addr server) ! 297: (si:memref-int (hostent-addr hostaddress) 0 0 :unsigned-long)) ! 298: (setf (sockaddr-in-family server) *af-inet*) ! 299: (setf (sockaddr-in-port server) port) ! 300: ;; open the connection ! 301: (when (< (connect sock server (ff::cstruct-len 'sockaddr-in)) 0) ! 302: (unix-close sock) ! 303: (error "couldn't connect to socket")) ! 304: ;; build and return the stream ! 305: (excl::make-buffered-terminal-stream sock sock t t))))
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.