-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathepcs.el
160 lines (138 loc) · 5.66 KB
/
epcs.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
;;; 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