1 /*
2     Copyright (c) 2012-2013 Martin Sustrik  All rights reserved.
3     Copyright 2016 Garrett D'Amore <garrett@damore.org>
4 
5     Permission is hereby granted, free of charge, to any person obtaining a copy
6     of this software and associated documentation files (the "Software"),
7     to deal in the Software without restriction, including without limitation
8     the rights to use, copy, modify, merge, publish, distribute, sublicense,
9     and/or sell copies of the Software, and to permit persons to whom
10     the Software is furnished to do so, subject to the following conditions:
11 
12     The above copyright notice and this permission notice shall be included
13     in all copies or substantial portions of the Software.
14 
15     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21     IN THE SOFTWARE.
22 */
23 
24 #include "aipc.h"
25 
26 #include "../../utils/err.h"
27 #include "../../utils/cont.h"
28 #include "../../utils/attr.h"
29 
30 #define NN_AIPC_STATE_IDLE 1
31 #define NN_AIPC_STATE_ACCEPTING 2
32 #define NN_AIPC_STATE_ACTIVE 3
33 #define NN_AIPC_STATE_STOPPING_SIPC 4
34 #define NN_AIPC_STATE_STOPPING_USOCK 5
35 #define NN_AIPC_STATE_DONE 6
36 #define NN_AIPC_STATE_STOPPING_SIPC_FINAL 7
37 #define NN_AIPC_STATE_STOPPING 8
38 
39 #define NN_AIPC_SRC_USOCK 1
40 #define NN_AIPC_SRC_SIPC 2
41 #define NN_AIPC_SRC_LISTENER 3
42 
43 /*  Private functions. */
44 static void nn_aipc_handler (struct nn_fsm *self, int src, int type,
45    void *srcptr);
46 static void nn_aipc_shutdown (struct nn_fsm *self, int src, int type,
47    void *srcptr);
48 
nn_aipc_init(struct nn_aipc * self,int src,struct nn_ep * ep,struct nn_fsm * owner)49 void nn_aipc_init (struct nn_aipc *self, int src,
50     struct nn_ep *ep, struct nn_fsm *owner)
51 {
52     nn_fsm_init (&self->fsm, nn_aipc_handler, nn_aipc_shutdown,
53         src, self, owner);
54     self->state = NN_AIPC_STATE_IDLE;
55     self->ep = ep;
56     nn_usock_init (&self->usock, NN_AIPC_SRC_USOCK, &self->fsm);
57     self->listener = NULL;
58     self->listener_owner.src = -1;
59     self->listener_owner.fsm = NULL;
60     nn_sipc_init (&self->sipc, NN_AIPC_SRC_SIPC, ep, &self->fsm);
61     nn_fsm_event_init (&self->accepted);
62     nn_fsm_event_init (&self->done);
63     nn_list_item_init (&self->item);
64 }
65 
nn_aipc_term(struct nn_aipc * self)66 void nn_aipc_term (struct nn_aipc *self)
67 {
68     nn_assert_state (self, NN_AIPC_STATE_IDLE);
69 
70     nn_list_item_term (&self->item);
71     nn_fsm_event_term (&self->done);
72     nn_fsm_event_term (&self->accepted);
73     nn_sipc_term (&self->sipc);
74     nn_usock_term (&self->usock);
75     nn_fsm_term (&self->fsm);
76 }
77 
nn_aipc_isidle(struct nn_aipc * self)78 int nn_aipc_isidle (struct nn_aipc *self)
79 {
80     return nn_fsm_isidle (&self->fsm);
81 }
82 
nn_aipc_start(struct nn_aipc * self,struct nn_usock * listener)83 void nn_aipc_start (struct nn_aipc *self, struct nn_usock *listener)
84 {
85 #if defined NN_HAVE_WINDOWS
86     size_t sz;
87 #endif
88     nn_assert_state (self, NN_AIPC_STATE_IDLE);
89 
90     /*  Take ownership of the listener socket. */
91     self->listener = listener;
92     self->listener_owner.src = NN_AIPC_SRC_LISTENER;
93     self->listener_owner.fsm = &self->fsm;
94     nn_usock_swap_owner (listener, &self->listener_owner);
95 
96 #if defined NN_HAVE_WINDOWS
97     /* Get/Set security attribute pointer*/
98     nn_ep_getopt (self->ep, NN_IPC, NN_IPC_SEC_ATTR, &self->usock.sec_attr, &sz);
99     nn_ep_getopt (self->ep, NN_IPC, NN_IPC_OUTBUFSZ, &self->usock.outbuffersz, &sz);
100     nn_ep_getopt (self->ep, NN_IPC, NN_IPC_INBUFSZ, &self->usock.inbuffersz, &sz);
101 #endif
102 
103     /*  Start the state machine. */
104     nn_fsm_start (&self->fsm);
105 }
106 
nn_aipc_stop(struct nn_aipc * self)107 void nn_aipc_stop (struct nn_aipc *self)
108 {
109     nn_fsm_stop (&self->fsm);
110 }
111 
nn_aipc_shutdown(struct nn_fsm * self,int src,int type,NN_UNUSED void * srcptr)112 static void nn_aipc_shutdown (struct nn_fsm *self, int src, int type,
113     NN_UNUSED void *srcptr)
114 {
115     struct nn_aipc *aipc;
116 
117     aipc = nn_cont (self, struct nn_aipc, fsm);
118 
119     if (nn_slow (src == NN_FSM_ACTION && type == NN_FSM_STOP)) {
120         if (!nn_sipc_isidle (&aipc->sipc)) {
121             nn_ep_stat_increment (aipc->ep, NN_STAT_DROPPED_CONNECTIONS, 1);
122             nn_sipc_stop (&aipc->sipc);
123         }
124         aipc->state = NN_AIPC_STATE_STOPPING_SIPC_FINAL;
125     }
126     if (nn_slow (aipc->state == NN_AIPC_STATE_STOPPING_SIPC_FINAL)) {
127         if (!nn_sipc_isidle (&aipc->sipc))
128             return;
129         nn_usock_stop (&aipc->usock);
130         aipc->state = NN_AIPC_STATE_STOPPING;
131     }
132     if (nn_slow (aipc->state == NN_AIPC_STATE_STOPPING)) {
133         if (!nn_usock_isidle (&aipc->usock))
134             return;
135        if (aipc->listener) {
136             nn_assert (aipc->listener_owner.fsm);
137             nn_usock_swap_owner (aipc->listener, &aipc->listener_owner);
138             aipc->listener = NULL;
139             aipc->listener_owner.src = -1;
140             aipc->listener_owner.fsm = NULL;
141         }
142         aipc->state = NN_AIPC_STATE_IDLE;
143         nn_fsm_stopped (&aipc->fsm, NN_AIPC_STOPPED);
144         return;
145     }
146 
147     nn_fsm_bad_state(aipc->state, src, type);
148 }
149 
nn_aipc_handler(struct nn_fsm * self,int src,int type,NN_UNUSED void * srcptr)150 static void nn_aipc_handler (struct nn_fsm *self, int src, int type,
151     NN_UNUSED void *srcptr)
152 {
153     struct nn_aipc *aipc;
154     int val;
155     size_t sz;
156 
157     aipc = nn_cont (self, struct nn_aipc, fsm);
158 
159     switch (aipc->state) {
160 
161 /******************************************************************************/
162 /*  IDLE state.                                                               */
163 /*  The state machine wasn't yet started.                                     */
164 /******************************************************************************/
165     case NN_AIPC_STATE_IDLE:
166         switch (src) {
167 
168         case NN_FSM_ACTION:
169             switch (type) {
170             case NN_FSM_START:
171                 nn_usock_accept (&aipc->usock, aipc->listener);
172                 aipc->state = NN_AIPC_STATE_ACCEPTING;
173                 return;
174             default:
175                 nn_fsm_bad_action (aipc->state, src, type);
176             }
177 
178         default:
179             nn_fsm_bad_source (aipc->state, src, type);
180         }
181 
182 /******************************************************************************/
183 /*  ACCEPTING state.                                                          */
184 /*  Waiting for incoming connection.                                          */
185 /******************************************************************************/
186     case NN_AIPC_STATE_ACCEPTING:
187         switch (src) {
188 
189         case NN_AIPC_SRC_USOCK:
190             switch (type) {
191             case NN_USOCK_ACCEPTED:
192                 nn_ep_clear_error (aipc->ep);
193 
194                 /*  Set the relevant socket options. */
195                 sz = sizeof (val);
196                 nn_ep_getopt (aipc->ep, NN_SOL_SOCKET, NN_SNDBUF, &val, &sz);
197                 nn_assert (sz == sizeof (val));
198                 nn_usock_setsockopt (&aipc->usock, SOL_SOCKET, SO_SNDBUF,
199                     &val, sizeof (val));
200                 sz = sizeof (val);
201                 nn_ep_getopt (aipc->ep, NN_SOL_SOCKET, NN_RCVBUF, &val, &sz);
202                 nn_assert (sz == sizeof (val));
203                 nn_usock_setsockopt (&aipc->usock, SOL_SOCKET, SO_RCVBUF,
204                     &val, sizeof (val));
205 
206                 /*  Return ownership of the listening socket to the parent. */
207                 nn_usock_swap_owner (aipc->listener, &aipc->listener_owner);
208                 aipc->listener = NULL;
209                 aipc->listener_owner.src = -1;
210                 aipc->listener_owner.fsm = NULL;
211                 nn_fsm_raise (&aipc->fsm, &aipc->accepted, NN_AIPC_ACCEPTED);
212 
213                 /*  Start the sipc state machine. */
214                 nn_usock_activate (&aipc->usock);
215                 nn_sipc_start (&aipc->sipc, &aipc->usock);
216                 aipc->state = NN_AIPC_STATE_ACTIVE;
217 
218                 nn_ep_stat_increment (aipc->ep,
219                     NN_STAT_ACCEPTED_CONNECTIONS, 1);
220 
221                 return;
222 
223             default:
224                 nn_fsm_bad_action (aipc->state, src, type);
225             }
226 
227         case NN_AIPC_SRC_LISTENER:
228             switch (type) {
229             case NN_USOCK_ACCEPT_ERROR:
230                 nn_ep_set_error (aipc->ep, nn_usock_geterrno (aipc->listener));
231                 nn_ep_stat_increment (aipc->ep, NN_STAT_ACCEPT_ERRORS, 1);
232                 nn_usock_accept (&aipc->usock, aipc->listener);
233 
234                 return;
235 
236             default:
237                 nn_fsm_bad_action (aipc->state, src, type);
238             }
239 
240         default:
241             nn_fsm_bad_source (aipc->state, src, type);
242         }
243 
244 /******************************************************************************/
245 /*  ACTIVE state.                                                             */
246 /******************************************************************************/
247     case NN_AIPC_STATE_ACTIVE:
248         switch (src) {
249 
250         case NN_AIPC_SRC_SIPC:
251             switch (type) {
252             case NN_SIPC_ERROR:
253                 nn_sipc_stop (&aipc->sipc);
254                 aipc->state = NN_AIPC_STATE_STOPPING_SIPC;
255                 nn_ep_stat_increment (aipc->ep, NN_STAT_BROKEN_CONNECTIONS, 1);
256                 return;
257             default:
258                 nn_fsm_bad_action (aipc->state, src, type);
259             }
260 
261         default:
262             nn_fsm_bad_source (aipc->state, src, type);
263         }
264 
265 /******************************************************************************/
266 /*  STOPPING_SIPC state.                                                      */
267 /******************************************************************************/
268     case NN_AIPC_STATE_STOPPING_SIPC:
269         switch (src) {
270 
271         case NN_AIPC_SRC_SIPC:
272             switch (type) {
273             case NN_USOCK_SHUTDOWN:
274                 return;
275             case NN_SIPC_STOPPED:
276                 nn_usock_stop (&aipc->usock);
277                 aipc->state = NN_AIPC_STATE_STOPPING_USOCK;
278                 return;
279             default:
280                 nn_fsm_bad_action (aipc->state, src, type);
281             }
282 
283         default:
284             nn_fsm_bad_source (aipc->state, src, type);
285         }
286 
287 /******************************************************************************/
288 /*  STOPPING_USOCK state.                                                      */
289 /******************************************************************************/
290     case NN_AIPC_STATE_STOPPING_USOCK:
291         switch (src) {
292 
293         case NN_AIPC_SRC_USOCK:
294             switch (type) {
295             case NN_USOCK_SHUTDOWN:
296                 return;
297             case NN_USOCK_STOPPED:
298                 nn_fsm_raise (&aipc->fsm, &aipc->done, NN_AIPC_ERROR);
299                 aipc->state = NN_AIPC_STATE_DONE;
300                 return;
301             default:
302                 nn_fsm_bad_action (aipc->state, src, type);
303             }
304 
305         default:
306             nn_fsm_bad_source (aipc->state, src, type);
307         }
308 
309 /******************************************************************************/
310 /*  Invalid state.                                                            */
311 /******************************************************************************/
312     default:
313         nn_fsm_bad_state (aipc->state, src, type);
314     }
315 }
316