Files
emacs/lisp/epc/epcs.el
2021-01-30 14:52:51 +01:00

161 lines
5.7 KiB
EmacsLisp
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;;; epcs.el --- EPC Server
;; Copyright (C) 2011,2012,2013 Masashi Sakurai
;; Author: Masashi Sakurai <m.sakurai at kiwanami.net>
;; Keywords: lisp
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(eval-when-compile (require 'cl))
(require 'epc)
(defvar epcs:client-processes nil
"[internal] A list of ([process object] . [`epc:manager' instance]).
When the server process accepts the client connection, the
`epc:manager' instance is created and stored in this variable
`epcs:client-processes'. This variable is used for the management
purpose.")
;; epcs:server
;; name : process name (string) ex: "EPC Server 1"
;; process : server process object
;; port : port number
;; connect-function : initialize function for `epc:manager' instances
(defstruct epcs:server name process port connect-function)
(defvar epcs:server-processes nil
"[internal] A list of ([process object] . [`epcs:server' instance]).
This variable is used for the management purpose.")
(defun epcs:server-start (connect-function &optional port)
"Start TCP Server and return the main process object."
(lexical-let*
((connect-function connect-function)
(name (format "EPC Server %s" (epc:uid)))
(buf (epc:make-procbuf (format "*%s*" name)))
(main-process
(make-network-process
:name name
:buffer buf
:family 'ipv4 :server t :nowait t
:host "127.0.0.1" :service (or port t)
:sentinel
(lambda (process message)
(epcs:sentinel process message connect-function)))))
(unless port
;; notify port number to the parent process via STDOUT.
(message "%s\n" (process-contact main-process :service)))
(push (cons main-process
(make-epcs:server
:name name :process main-process
:port (process-contact main-process :service)
:connect-function connect-function))
epcs:server-processes)
main-process))
(defun epcs:server-stop (process)
"Stop the TCP server process."
(cond
((and process
(assq process epcs:server-processes))
(epc:log "EPCS: Shutdown Server: %S" process)
(let ((buf (process-buffer process)))
(delete-process process)
(kill-buffer buf))
(setq epcs:server-processes
(assq-delete-all process epcs:server-processes)))
(t (error "Not found in the server process list. [%S]" process))))
(defun epcs:get-manager-by-process (proc)
"[internal] Return the epc:manager instance for the PROC."
(loop for (pp . mngr) in epcs:client-processes
if (eql pp proc)
do (return mngr)
finally return nil))
(defun epcs:kill-all-processes ()
"Kill all child processes for debug purpose."
(interactive)
(loop for (proc . mngr) in epcs:client-processes
do (ignore-errors
(delete-process proc)
(kill-buffer (process-buffer proc)))))
(defun epcs:accept (process)
"[internal] Initialize the process and return epc:manager object."
(epc:log "EPCS: >> Connection accept: %S" process)
(lexical-let* ((connection-id (epc:uid))
(connection-name (format "epc con %s" connection-id))
(channel (cc:signal-channel connection-name))
(connection (make-epc:connection
:name connection-name
:process process
:buffer (process-buffer process)
:channel channel)))
(epc:log "EPCS: >> Connection establish")
(set-process-coding-system process 'binary 'binary)
(set-process-filter process
(lambda (p m)
(epc:process-filter connection p m)))
(set-process-sentinel process
(lambda (p e)
(epc:process-sentinel connection p e)))
(make-epc:manager :server-process process :port t
:connection connection)))
(defun epcs:sentinel (process message connect-function)
"[internal] Process sentinel handler for the server process."
(epc:log "EPCS: SENTINEL: %S %S" process message)
(let ((mngr (epcs:get-manager-by-process process)))
(cond
;; new connection
((and (string-match "open" message) (null mngr))
(condition-case err
(let ((mngr (epcs:accept process)))
(push (cons process mngr) epcs:client-processes)
(epc:init-epc-layer mngr)
(when connect-function (funcall connect-function mngr))
mngr)
('error
(epc:log "EPCS: Protocol error: %S" err)
(epc:log "EPCS: ABORT %S" process)
(delete-process process))))
;; ignore
((null mngr) nil )
;; disconnect
(t
(let ((pair (assq process epcs:client-processes)) d)
(when pair
(epc:log "EPCS: DISCONNECT %S" process)
(epc:stop-epc (cdr pair))
(setq epcs:client-processes
(assq-delete-all process epcs:client-processes))
))
nil))))
;; Management GUI
;; todo...
(provide 'epcs)
;;; epcs.el ends here