Skip to content

Commit

Permalink
Moved out EPMD client code into new file epmd-client.lisp to prepare …
Browse files Browse the repository at this point in the history
…for refactoring.
  • Loading branch information
flambard committed May 23, 2013
1 parent 342da23 commit 491aae4
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 118 deletions.
3 changes: 3 additions & 0 deletions cleric.asd
Expand Up @@ -33,7 +33,10 @@
:depends-on ("packages"
"atom-cache"))
(:file "epmd"
:depends-on ("packages"))
(:file "epmd-client"
:depends-on ("packages"
"epmd"
"listen"
"local-node"
"remote-node"))
Expand Down
117 changes: 117 additions & 0 deletions src/epmd-client.lisp
@@ -0,0 +1,117 @@
;;;; Functions for querying EPMD (Erlang Port Mapped Daemon)

(in-package :cleric-epmd)

;;; EPMD port
(defconstant +epmd-port+ 4369
"The default TCP port the EPMD listens on.")

(defvar *epmd-socket* nil
"The EPMD socket. NIL if not registered in EPMD.")



(defun connect-to-epmd (&optional (host "localhost"))
(let ((socket (handler-case (usocket:socket-connect host
+epmd-port+
:element-type 'octet)
(usocket:connection-refused-error ()
(error 'unreachable-error))
(usocket:unknown-error ()
(error 'host-unknown-error)))))
(setf (usocket:socket-stream socket)
(make-flexi-stream (usocket:socket-stream socket)))
socket))


;;;
;;; WITH-EPMD-CONNECTION-STREAM macro
;;;

(defmacro with-epmd-connection-stream
((stream-var &optional (host "localhost")) &body body)
"Create a local scope where STREAM-VAR is a socket stream connected to the EPMD."
(let ((socket-var (gensym)))
`(let* ((,socket-var (connect-to-epmd ,host))
(,stream-var (usocket:socket-stream ,socket-var)))
(unwind-protect (progn ,@body)
(usocket:socket-close ,socket-var))) ))


;;;
;;; EPMD API
;;;

(defun publish ()
(if *epmd-socket*
(error 'already-registered)
(restart-case
(if (not (listening-p))
(error 'not-listening-on-socket)
(let* ((socket (connect-to-epmd))
(epmd (usocket:socket-stream socket)))
(write-alive2-request
epmd (node-name (this-node)) (listening-port))
(finish-output epmd)
(let ((creation (read-alive2-response epmd)))
(declare (ignore creation))
(setf *epmd-socket* socket)
t)))
(start-listening-on-socket ()
:report "Start listening on a socket."
:test (lambda (c)
(declare (ignore c))
(not (listening-p)))
(start-listening)
(publish)))))

(defun published-p ()
(not (null *epmd-socket*)))

(defun unpublish ()
(when *epmd-socket*
(usocket:socket-close *epmd-socket*)
(setf *epmd-socket* nil)
t))

(defun lookup-node (node-name &optional (host "localhost"))
"Query the EPMD about a node. Returns a REMOTE-NODE object that represents the node."
(with-epmd-connection-stream (epmd host)
(write-port-please2-request epmd node-name)
(finish-output epmd)
(read-port-please2-response epmd host)))

(defun print-all-registered-nodes (&optional (host "localhost") (stream t))
"Query the EPMD about all registered nodes and print the information."
(with-epmd-connection-stream (epmd host)
(write-names-request epmd)
(finish-output epmd)
(multiple-value-bind (epmd-port node-info)
(read-names-response epmd)
(declare (ignore epmd-port))
(format stream "~{~a~%~}" node-info)
t)))


;;;
;;; Conditions
;;;

(define-condition already-registered (error)
()
(:documentation "This error is signaled when trying to register on the EPMD when already registered."))

(define-condition host-unknown-error (error)
;; USOCKET:UNKNOWN-ERROR
()
(:documentation "This error is signaled if the hostname for EPMD is unresolvable."))

(define-condition unreachable-error (error)
;; USOCKET:CONNECTION-REFUSED-ERROR
()
(:documentation "This error is signaled when the EPMD is unreachable."))

(define-condition response-error (error)
;; Useful?
()
(:documentation "This error is signaled when the EPMD sends an error response."))
119 changes: 1 addition & 118 deletions src/epmd.lisp
@@ -1,11 +1,7 @@
;;;; Functions for querying EPMD (Erlang Port Mapped Daemon)
;;;; The EPMD protocol

(in-package :cleric-epmd)

;;; EPMD port
(defconstant +epmd-port+ 4369
"The default TCP port the EPMD listens on.")

;;; EPMD message tags
(defconstant +port2-resp+ #\w)
(defconstant +alive2-req+ #\x)
Expand All @@ -21,9 +17,6 @@

(defconstant +protocol-tcpip4+ 0)

(defvar *epmd-socket* nil
"The EPMD socket. NIL if not registered in EPMD.")


;;;
;;; ALIVE2_REQ
Expand Down Expand Up @@ -180,113 +173,3 @@
for line = (read-line stream nil)
while line collect line)))


;;;
;;; WITH-EPMD-CONNECTION-STREAM macro
;;;

(defmacro with-epmd-connection-stream
((stream-var &optional (host "localhost")) &body body)
"Create a local scope where STREAM-VAR is a socket stream connected to the EPMD."
(let ((socket-var (gensym)))
`(let* ((,socket-var (connect-to-epmd ,host))
(,stream-var (usocket:socket-stream ,socket-var)))
(unwind-protect (progn ,@body)
(usocket:socket-close ,socket-var))) ))


;;;
;;; EPMD API
;;;

(defun publish ()
(if *epmd-socket*
(error 'already-registered)
(restart-case
(if (not (listening-p))
(error 'not-listening-on-socket)
(let* ((socket (connect-to-epmd))
(epmd (usocket:socket-stream socket)))
(write-alive2-request
epmd (node-name (this-node)) (listening-port))
(finish-output epmd)
(let ((creation (read-alive2-response epmd)))
(declare (ignore creation))
(setf *epmd-socket* socket)
t)))
(start-listening-on-socket ()
:report "Start listening on a socket."
:test (lambda (c)
(declare (ignore c))
(not (listening-p)))
(start-listening)
(publish)))))

(defun published-p ()
(not (null *epmd-socket*)))

(defun unpublish ()
(when *epmd-socket*
(usocket:socket-close *epmd-socket*)
(setf *epmd-socket* nil)
t))

(defun lookup-node (node-name &optional (host "localhost"))
"Query the EPMD about a node. Returns a REMOTE-NODE object that represents the node."
(with-epmd-connection-stream (epmd host)
(write-port-please2-request epmd node-name)
(finish-output epmd)
(read-port-please2-response epmd host)))

(defun print-all-registered-nodes (&optional (host "localhost") (stream t))
"Query the EPMD about all registered nodes and print the information."
(with-epmd-connection-stream (epmd host)
(write-names-request epmd)
(finish-output epmd)
(multiple-value-bind (epmd-port node-info)
(read-names-response epmd)
(declare (ignore epmd-port))
(format stream "~{~a~%~}" node-info)
t)))


;;;
;;; Helper functions
;;;

(defun connect-to-epmd (&optional (host "localhost"))
(let ((socket (handler-case (usocket:socket-connect host
+epmd-port+
:element-type 'octet)
(usocket:connection-refused-error ()
(error 'unreachable-error))
(usocket:unknown-error ()
(error 'host-unknown-error)))))
(setf (usocket:socket-stream socket)
(make-flexi-stream (usocket:socket-stream socket)))
socket))


;;;
;;; Conditions
;;;

(define-condition already-registered (error)
()
(:documentation "This error is signaled when trying to register on the EPMD when already registered."))

(define-condition host-unknown-error (error)
;; USOCKET:UNKNOWN-ERROR
()
(:documentation "This error is signaled if the hostname for EPMD is unresolvable."))

(define-condition unreachable-error (error)
;; USOCKET:CONNECTION-REFUSED-ERROR
()
(:documentation "This error is signaled when the EPMD is unreachable."))


(define-condition response-error (error)
;; Useful?
()
(:documentation "This error is signaled when the EPMD sends an error response."))

0 comments on commit 491aae4

Please sign in to comment.