1 /*
2    Unix SMB/CIFS implementation.
3    main select loop and event handling
4    Copyright (C) Stefan Metzmacher      2013
5    Copyright (C) Jeremy Allison         2013
6 
7      ** NOTE! The following LGPL license applies to the tevent
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10 
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 3 of the License, or (at your option) any later version.
15 
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20 
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 */
24 
25 /*
26   This is SAMBA's default event loop code
27 
28   - we try to use epoll if configure detected support for it
29     otherwise we use poll()
30   - if epoll is broken on the system or the kernel doesn't support it
31     at runtime we fallback to poll()
32 */
33 
34 #include "replace.h"
35 #include "tevent.h"
36 #include "tevent_util.h"
37 #include "tevent_internal.h"
38 
39 struct std_event_glue {
40 	const struct tevent_ops *epoll_ops;
41 	const struct tevent_ops *poll_ops;
42 	struct tevent_ops *glue_ops;
43 	bool fallback_replay;
44 };
45 
46 static int std_event_context_init(struct tevent_context *ev);
47 
48 static const struct tevent_ops std_event_ops = {
49 	.context_init           = std_event_context_init,
50 };
51 
52 /*
53   If this function gets called. epoll failed at runtime.
54   Move us to using poll instead. If we return false here,
55   caller should abort().
56 */
57 #ifdef HAVE_EPOLL
std_fallback_to_poll(struct tevent_context * ev,bool replay)58 static bool std_fallback_to_poll(struct tevent_context *ev, bool replay)
59 {
60 	void *glue_ptr = talloc_parent(ev->ops);
61 	struct std_event_glue *glue =
62 		talloc_get_type_abort(glue_ptr,
63 		struct std_event_glue);
64 	int ret;
65 	struct tevent_fd *fde;
66 
67 	glue->fallback_replay = replay;
68 
69 	/* First switch all the ops to poll. */
70 	glue->epoll_ops = NULL;
71 
72 	/*
73 	 * Set custom_ops the same as poll.
74 	 */
75 	*glue->glue_ops = *glue->poll_ops;
76 	glue->glue_ops->context_init = std_event_context_init;
77 
78 	/* Next initialize the poll backend. */
79 	ret = glue->poll_ops->context_init(ev);
80 	if (ret != 0) {
81 		return false;
82 	}
83 
84 	/*
85 	 * Now we have to change all the existing file descriptor
86 	 * events from the epoll backend to the poll backend.
87 	 */
88 	for (fde = ev->fd_events; fde; fde = fde->next) {
89 		bool ok;
90 
91 		/* Re-add this event as a poll backend event. */
92 		ok = tevent_poll_event_add_fd_internal(ev, fde);
93 		if (!ok) {
94 			return false;
95 		}
96 	}
97 
98 	return true;
99 }
100 #endif
101 
std_event_loop_once(struct tevent_context * ev,const char * location)102 static int std_event_loop_once(struct tevent_context *ev, const char *location)
103 {
104 	void *glue_ptr = talloc_parent(ev->ops);
105 	struct std_event_glue *glue =
106 		talloc_get_type_abort(glue_ptr,
107 		struct std_event_glue);
108 	int ret;
109 
110 	ret = glue->epoll_ops->loop_once(ev, location);
111 	/*
112 	 * If the above hasn't panicked due to an epoll interface failure,
113 	 * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
114 	 * signify fallback to poll_ops.
115 	 */
116 	if (glue->epoll_ops != NULL) {
117 		/* No fallback */
118 		return ret;
119 	}
120 
121 	if (!glue->fallback_replay) {
122 		/*
123 		 * The problem happened while modifying an event.
124 		 * An event handler was triggered in this case
125 		 * and there is no need to call loop_once() again.
126 		 */
127 		return ret;
128 	}
129 
130 	return glue->poll_ops->loop_once(ev, location);
131 }
132 
std_event_loop_wait(struct tevent_context * ev,const char * location)133 static int std_event_loop_wait(struct tevent_context *ev, const char *location)
134 {
135 	void *glue_ptr = talloc_parent(ev->ops);
136 	struct std_event_glue *glue =
137 		talloc_get_type_abort(glue_ptr,
138 		struct std_event_glue);
139 	int ret;
140 
141 	ret = glue->epoll_ops->loop_wait(ev, location);
142 	/*
143 	 * If the above hasn't panicked due to an epoll interface failure,
144 	 * std_fallback_to_poll() wasn't called, and hasn't cleared epoll_ops to
145 	 * signify fallback to poll_ops.
146 	 */
147 	if (glue->epoll_ops != NULL) {
148 		/* No fallback */
149 		return ret;
150 	}
151 
152 	return glue->poll_ops->loop_wait(ev, location);
153 }
154 /*
155   Initialize the epoll backend and allow it to call a
156   switch function if epoll fails at runtime.
157 */
std_event_context_init(struct tevent_context * ev)158 static int std_event_context_init(struct tevent_context *ev)
159 {
160 	struct std_event_glue *glue;
161 	int ret;
162 
163 	/*
164 	 * If this is the first initialization
165 	 * we need to set up the allocated ops
166 	 * pointers.
167 	 */
168 
169 	if (ev->ops == &std_event_ops) {
170 		glue = talloc_zero(ev, struct std_event_glue);
171 		if (glue == NULL) {
172 			return -1;
173 		}
174 
175 		glue->epoll_ops = tevent_find_ops_byname("epoll");
176 
177 		glue->poll_ops = tevent_find_ops_byname("poll");
178 		if (glue->poll_ops == NULL) {
179 			return -1;
180 		}
181 
182 		/*
183 		 * Allocate space for our custom ops.
184 		 * Allocate as a child of our epoll_ops pointer
185 		 * so we can easily get to it using talloc_parent.
186 		 */
187 		glue->glue_ops = talloc_zero(glue, struct tevent_ops);
188 		if (glue->glue_ops == NULL) {
189 			talloc_free(glue);
190 			return -1;
191 		}
192 
193 		ev->ops = glue->glue_ops;
194 	} else {
195 		void *glue_ptr = talloc_parent(ev->ops);
196 		glue = talloc_get_type_abort(glue_ptr, struct std_event_glue);
197 	}
198 
199 	if (glue->epoll_ops != NULL) {
200 		/*
201 		 * Set custom_ops the same as epoll,
202 		 * except re-init using std_event_context_init()
203 		 * and use std_event_loop_once() to add the
204 		 * ability to fallback to a poll backend on
205 		 * epoll runtime error.
206 		 */
207 		*glue->glue_ops = *glue->epoll_ops;
208 		glue->glue_ops->context_init = std_event_context_init;
209 		glue->glue_ops->loop_once = std_event_loop_once;
210 		glue->glue_ops->loop_wait = std_event_loop_wait;
211 
212 		ret = glue->epoll_ops->context_init(ev);
213 		if (ret == -1) {
214 			goto fallback;
215 		}
216 #ifdef HAVE_EPOLL
217 		tevent_epoll_set_panic_fallback(ev, std_fallback_to_poll);
218 #endif
219 
220 		return ret;
221 	}
222 
223 fallback:
224 	glue->epoll_ops = NULL;
225 
226 	/*
227 	 * Set custom_ops the same as poll.
228 	 */
229 	*glue->glue_ops = *glue->poll_ops;
230 	glue->glue_ops->context_init = std_event_context_init;
231 
232 	return glue->poll_ops->context_init(ev);
233 }
234 
tevent_standard_init(void)235 _PRIVATE_ bool tevent_standard_init(void)
236 {
237 	return tevent_register_backend("standard", &std_event_ops);
238 }
239