1#include <systemd/sd-event.h>
2
3#include <private-lib-core.h>
4#include "private-lib-event-libs-sdevent.h"
5
6#define pt_to_priv_sd(_pt) ((struct lws_pt_eventlibs_sdevent *)(_pt)->evlib_pt)
7#define wsi_to_priv_sd(_w) ((struct lws_wsi_watcher_sdevent *)(_w)->evlib_wsi)
8
9struct lws_pt_eventlibs_sdevent {
10	struct lws_context_per_thread *pt;
11	struct sd_event *io_loop;
12	struct sd_event_source *sultimer;
13	struct sd_event_source *idletimer;
14};
15
16struct lws_wsi_watcher_sdevent {
17	struct sd_event_source *source;
18	uint32_t events;
19};
20
21static int
22sultimer_handler(sd_event_source *s, uint64_t usec, void *userdata)
23{
24	struct lws_context_per_thread *pt = (struct lws_context_per_thread *)userdata;
25
26	lws_usec_t us;
27
28	lws_context_lock(pt->context, __func__);
29	lws_pt_lock(pt, __func__);
30
31	us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
32				    lws_now_usecs());
33	if (us) {
34		uint64_t at;
35
36		sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &at);
37		at += (uint64_t)us;
38		sd_event_source_set_time(pt_to_priv_sd(pt)->sultimer, at);
39		sd_event_source_set_enabled(pt_to_priv_sd(pt)->sultimer,
40					    SD_EVENT_ONESHOT);
41	}
42
43	lws_pt_unlock(pt);
44	lws_context_unlock(pt->context);
45
46	return 0;
47}
48
49static int
50idle_handler(sd_event_source *s, uint64_t usec, void *userdata)
51{
52	struct lws_context_per_thread *pt = (struct lws_context_per_thread *)userdata;
53
54	lws_usec_t us;
55
56	lws_service_do_ripe_rxflow(pt);
57
58	lws_context_lock(pt->context, __func__);
59	lws_pt_lock(pt, __func__);
60
61	/*
62	 * is there anybody with pending stuff that needs service forcing?
63	 */
64	 if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
65		 /* -1 timeout means just do forced service */
66		 _lws_plat_service_forced_tsi(pt->context, pt->tid);
67
68	 /* account for sultimer */
69
70	 us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
71				     lws_now_usecs());
72
73	 if (us) {
74		 uint64_t at;
75
76		 sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &at);
77		 at += (uint64_t)us;
78		 sd_event_source_set_time(pt_to_priv_sd(pt)->sultimer, at);
79		 sd_event_source_set_enabled(pt_to_priv_sd(pt)->sultimer,
80					     SD_EVENT_ONESHOT);
81	 }
82
83	 sd_event_source_set_enabled(pt_to_priv_sd(pt)->idletimer, SD_EVENT_OFF);
84
85	 lws_pt_unlock(pt);
86	 lws_context_unlock(pt->context);
87
88	 return 0;
89}
90
91static int
92sock_accept_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata)
93{
94	struct lws *wsi = (struct lws *)userdata;
95	struct lws_context *context = wsi->a.context;
96	struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
97	struct sd_event_source *idletimer, *watcher;
98	struct lws_pollfd eventfd;
99
100	lws_context_lock(pt->context, __func__);
101	lws_pt_lock(pt, __func__);
102
103	if (pt->is_destroyed)
104		goto bail;
105
106	eventfd.fd = fd;
107	eventfd.events = 0;
108	eventfd.revents = 0;
109
110	if (revents & EPOLLIN) {
111		eventfd.events |= LWS_POLLIN;
112		eventfd.revents |= LWS_POLLIN;
113	}
114
115	if (revents & EPOLLOUT) {
116		eventfd.events |= LWS_POLLOUT;
117		eventfd.revents |= LWS_POLLOUT;
118	}
119
120	lws_pt_unlock(pt);
121	lws_context_unlock(pt->context);
122
123	lws_service_fd_tsi(context, &eventfd, wsi->tsi);
124
125	if (pt->destroy_self) {
126		lws_context_destroy(pt->context);
127		return -1;
128	}
129
130	/* fire idle handler */
131	idletimer = pt_to_priv_sd(pt)->idletimer;
132	if (idletimer) {
133		sd_event_source_set_time(idletimer, (uint64_t) 0);
134		sd_event_source_set_enabled(idletimer, SD_EVENT_ON);
135	}
136
137	/*
138	 * allow further events
139	 *
140	 * Note:
141	 * do not move the assignment up, lws_service_fd_tsi may invalidate it!
142	 */
143	watcher = wsi_to_priv_sd(wsi)->source;
144	if (watcher)
145		sd_event_source_set_enabled(watcher, SD_EVENT_ONESHOT);
146
147	return 0;
148
149bail:
150	lws_pt_unlock(pt);
151	lws_context_unlock(pt->context);
152
153	return -1;
154}
155
156static void
157io_sd(struct lws *wsi, unsigned int flags)
158{
159	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
160
161	/*
162	 * Only manipulate if there is an event source, and if
163	 * the pt is still alive
164	 */
165	if (!pt_to_priv_sd(pt)->io_loop ||
166	    !wsi_to_priv_sd(wsi)->source ||
167	    pt->is_destroyed)
168		return;
169
170	// assert that the requested flags do not contain anything unexpected
171	if (!((flags & (LWS_EV_START | LWS_EV_STOP)) &&
172	    (flags & (LWS_EV_READ | LWS_EV_WRITE)))) {
173		lwsl_wsi_err(wsi, "assert: flags %d", flags);
174		assert(0);
175	}
176
177	// we are overdoing a bit here, so it resembles the structure in libuv.c
178	if (flags & LWS_EV_START) {
179		if (flags & LWS_EV_WRITE)
180			wsi_to_priv_sd(wsi)->events |= EPOLLOUT;
181
182		if (flags & LWS_EV_READ)
183			wsi_to_priv_sd(wsi)->events |= EPOLLIN;
184
185		sd_event_source_set_io_events(wsi_to_priv_sd(wsi)->source,
186					      wsi_to_priv_sd(wsi)->events);
187		sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source,
188					    SD_EVENT_ONESHOT);
189	} else {
190		if (flags & LWS_EV_WRITE)
191			wsi_to_priv_sd(wsi)->events =
192				wsi_to_priv_sd(wsi)->events &
193					(uint32_t)(~EPOLLOUT);
194
195		if (flags & LWS_EV_READ)
196			wsi_to_priv_sd(wsi)->events =
197				wsi_to_priv_sd(wsi)->events &
198					(uint32_t)(~EPOLLIN);
199
200		sd_event_source_set_io_events(wsi_to_priv_sd(wsi)->source,
201					      wsi_to_priv_sd(wsi)->events);
202
203		if (!(wsi_to_priv_sd(wsi)->events & (EPOLLIN | EPOLLOUT)))
204			sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source,
205						    SD_EVENT_ONESHOT);
206		else
207			sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source,
208						    SD_EVENT_OFF);
209	}
210}
211
212static int
213init_vhost_listen_wsi_sd(struct lws *wsi)
214{
215	struct lws_context_per_thread *pt;
216
217	if (!wsi)
218		return 0;
219
220	pt = &wsi->a.context->pt[(int)wsi->tsi];
221
222	sd_event_add_io(pt_to_priv_sd(pt)->io_loop,
223			&wsi_to_priv_sd(wsi)->source,
224			wsi->desc.sockfd,
225			wsi_to_priv_sd(wsi)->events,
226			sock_accept_handler,
227			wsi);
228
229	io_sd(wsi, LWS_EV_START | LWS_EV_READ);
230
231	return 0;
232}
233
234static int
235elops_listen_init_sdevent(struct lws_dll2 *d, void *user)
236{
237	struct lws *wsi = lws_container_of(d, struct lws, listen_list);
238
239	if (init_vhost_listen_wsi_sd(wsi) == -1)
240		return -1;
241
242	return 0;
243}
244
245static int
246init_pt_sd(struct lws_context *context, void *_loop, int tsi)
247{
248	struct lws_context_per_thread *pt = &context->pt[tsi];
249	struct lws_pt_eventlibs_sdevent *ptpriv = pt_to_priv_sd(pt);
250	struct sd_event *loop = (struct sd_event *)_loop;
251	int first = 1;  /* first to create and initialize the loop */
252
253	ptpriv->pt = pt;
254
255	/* make sure we have an event loop */
256	if (!ptpriv->io_loop) {
257		if (!loop) {
258			if (sd_event_default(&loop) < 0) {
259				lwsl_cx_err(context, "sd_event_default failed");
260
261				return -1;
262			}
263			pt->event_loop_foreign = 0;
264		} else {
265			sd_event_ref(loop);
266			pt->event_loop_foreign = 1;
267		}
268
269		ptpriv->io_loop = loop;
270	} else
271		 /*
272		  * If the loop was initialized before, we do not need to
273		  * do full initialization
274		  */
275		first = 0;
276
277	lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_init_sdevent);
278
279	if (first) {
280
281		if (0 > sd_event_add_time(loop,
282				&ptpriv->sultimer,
283				CLOCK_MONOTONIC,
284				UINT64_MAX,
285				0,
286				sultimer_handler,
287				(void*) pt
288		))
289			return -1;
290
291		if (0 > sd_event_add_time(loop,
292				&ptpriv->idletimer,
293				CLOCK_MONOTONIC,
294				0,
295				0,
296				idle_handler,
297				(void *)pt))
298			return -1;
299
300		sd_event_source_set_enabled(ptpriv->idletimer, SD_EVENT_ON);
301
302		if (0 > sd_event_source_set_priority(ptpriv->idletimer,
303						     SD_EVENT_PRIORITY_IDLE))
304			return -1;
305
306	}
307
308	return 0;
309}
310
311static void
312wsi_destroy_sd(struct lws *wsi)
313{
314	if (!wsi)
315		return;
316
317	io_sd(wsi, LWS_EV_STOP | (LWS_EV_READ | LWS_EV_WRITE));
318
319	if (wsi_to_priv_sd(wsi)->source) {
320		sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source,
321					    SD_EVENT_OFF);
322		sd_event_source_unref(wsi_to_priv_sd(wsi)->source);
323		wsi_to_priv_sd(wsi)->source = NULL;
324	}
325}
326
327static int
328wsi_logical_close_sd(struct lws *wsi)
329{
330	wsi_destroy_sd(wsi);
331
332	return 0;
333}
334
335static int
336sock_accept_sd(struct lws *wsi)
337{
338	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
339
340	if (wsi->role_ops->file_handle)
341		sd_event_add_io(pt_to_priv_sd(pt)->io_loop,
342				&wsi_to_priv_sd(wsi)->source,
343				wsi->desc.filefd,
344				wsi_to_priv_sd(wsi)->events,
345				sock_accept_handler,
346				wsi);
347	else
348		sd_event_add_io(pt_to_priv_sd(pt)->io_loop,
349				&wsi_to_priv_sd(wsi)->source,
350				wsi->desc.sockfd,
351				wsi_to_priv_sd(wsi)->events,
352				sock_accept_handler,
353				wsi);
354
355	return 0;
356}
357
358static void
359run_pt_sd(struct lws_context *context, int tsi)
360{
361	struct lws_context_per_thread *pt = &context->pt[tsi];
362	struct lws_pt_eventlibs_sdevent *ptpriv = pt_to_priv_sd(pt);
363
364	if (ptpriv->io_loop)
365		sd_event_run(ptpriv->io_loop, (uint64_t) -1);
366}
367
368static int
369elops_listen_destroy_sdevent(struct lws_dll2 *d, void *user)
370{
371	struct lws *wsi = lws_container_of(d, struct lws, listen_list);
372
373	wsi_logical_close_sd(wsi);
374
375	return 0;
376}
377
378static void
379destroy_pt_sd(struct lws_context *context, int tsi)
380{
381	struct lws_context_per_thread *pt = &context->pt[tsi];
382	struct lws_pt_eventlibs_sdevent *ptpriv = pt_to_priv_sd(pt);
383
384	lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_destroy_sdevent);
385
386	if (ptpriv->sultimer) {
387		sd_event_source_set_enabled(ptpriv->sultimer,
388					    SD_EVENT_OFF);
389		sd_event_source_unref(ptpriv->sultimer);
390		ptpriv->sultimer = NULL;
391	}
392
393	if (ptpriv->idletimer) {
394		sd_event_source_set_enabled(ptpriv->idletimer,
395					    SD_EVENT_OFF);
396		sd_event_source_unref(ptpriv->idletimer);
397		ptpriv->idletimer = NULL;
398	}
399
400	if (ptpriv->io_loop) {
401		sd_event_unref(ptpriv->io_loop);
402		ptpriv->io_loop = NULL;
403	}
404}
405
406const struct lws_event_loop_ops event_loop_ops_sdevent = {
407		.name				= "sdevent",
408		.init_context			= NULL,
409		.destroy_context1		= NULL,
410		.destroy_context2		= NULL,
411		.init_vhost_listen_wsi		= init_vhost_listen_wsi_sd,
412		.init_pt			= init_pt_sd,
413		.wsi_logical_close		= wsi_logical_close_sd,
414		.check_client_connect_ok	= NULL,
415		.close_handle_manually		= NULL,
416		.sock_accept			= sock_accept_sd,
417		.io				= io_sd,
418		.run_pt				= run_pt_sd,
419		.destroy_pt			= destroy_pt_sd,
420		.destroy_wsi			= wsi_destroy_sd,
421
422		.flags				= 0,
423
424		.evlib_size_ctx			= 0,
425		.evlib_size_pt			= sizeof(struct lws_pt_eventlibs_sdevent),
426		.evlib_size_vh			= 0,
427		.evlib_size_wsi			= sizeof(struct lws_wsi_watcher_sdevent),
428};
429
430#if defined(LWS_WITH_EVLIB_PLUGINS)
431LWS_VISIBLE
432#endif
433const lws_plugin_evlib_t evlib_sd = {
434		.hdr = {
435				"systemd event loop",
436				"lws_evlib_plugin",
437				LWS_BUILD_HASH,
438				LWS_PLUGIN_API_MAGIC
439		},
440
441		.ops	= &event_loop_ops_sdevent
442};
443