1 /*
2  * ivykis, an event handling library
3  * Copyright (C) 2010 Lennert Buytenhek
4  * Dedicated to Marija Kulikova.
5  *
6  * This library is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License version
8  * 2.1 as published by the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser General Public License version 2.1 for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License version 2.1 along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <iv.h>
24 #include <iv_signal.h>
25 #include <iv_tls.h>
26 #include <iv_wait.h>
27 #include "iv_private.h"
28 #include "mutex.h"
29 
30 #ifndef WCONTINUED
31 #define WCONTINUED		0
32 #define WIFCONTINUED(x)		0
33 #endif
34 
35 #define IV_WAIT_STATUS_DEAD	1
36 
37 struct wait_event {
38 	struct iv_list_head	list;
39 	int			status;
40 #ifdef HAVE_WAIT4
41 	struct rusage		rusage;
42 #endif
43 };
44 
iv_wait_interest_compare(const struct iv_avl_node * _a,const struct iv_avl_node * _b)45 static int iv_wait_interest_compare(const struct iv_avl_node *_a,
46 				    const struct iv_avl_node *_b)
47 {
48 	const struct iv_wait_interest *a =
49 		iv_container_of(_a, struct iv_wait_interest, avl_node);
50 	const struct iv_wait_interest *b =
51 		iv_container_of(_b, struct iv_wait_interest, avl_node);
52 
53 	if (a->pid < b->pid)
54 		return -1;
55 	if (a->pid > b->pid)
56 		return 1;
57 
58 	return 0;
59 }
60 
61 static ___mutex_t iv_wait_lock;
62 static struct iv_avl_tree iv_wait_interests =
63 	IV_AVL_TREE_INIT(iv_wait_interest_compare);
64 
__iv_wait_interest_find(int pid)65 static struct iv_wait_interest *__iv_wait_interest_find(int pid)
66 {
67 	struct iv_avl_node *an;
68 
69 	an = iv_wait_interests.root;
70 	while (an != NULL) {
71 		struct iv_wait_interest *p;
72 
73 		p = iv_container_of(an, struct iv_wait_interest, avl_node);
74 		if (pid == p->pid)
75 			return p;
76 
77 		if (pid < p->pid)
78 			an = an->left;
79 		else
80 			an = an->right;
81 	}
82 
83 	return NULL;
84 }
85 
iv_wait_status_dead(int status)86 static int iv_wait_status_dead(int status)
87 {
88 	/*
89 	 * On FreeBSD, WIFCONTINUED(status) => WIFSIGNALED(status).
90 	 */
91 	if (WIFSIGNALED(status) && !WIFCONTINUED(status))
92 		return 1;
93 
94 	if (WIFEXITED(status))
95 		return 1;
96 
97 	return 0;
98 }
99 
iv_wait_got_sigchld(void * _dummy)100 static void iv_wait_got_sigchld(void *_dummy)
101 {
102 	___mutex_lock(&iv_wait_lock);
103 	while (1) {
104 		pid_t pid;
105 		int status;
106 		struct wait_event *we;
107 		struct iv_wait_interest *p;
108 
109 #ifdef HAVE_WAIT4
110 		struct rusage rusage;
111 
112 #ifdef __digital__
113 		union wait w;
114 
115 		pid = wait4(-1, &w, WNOHANG | WUNTRACED | WCONTINUED, &rusage);
116 		status = w.w_status;
117 #else
118 		pid = wait4(-1, &status,
119 			    WNOHANG | WUNTRACED | WCONTINUED, &rusage);
120 #endif
121 		if (pid <= 0) {
122 			if (pid < 0 && errno != ECHILD)
123 				perror("wait4");
124 			break;
125 		}
126 #else
127 		pid = waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED);
128 		if (pid <= 0) {
129 			if (pid < 0 && errno != ECHILD)
130 				perror("waitpid");
131 			break;
132 		}
133 #endif
134 
135 		we = malloc(sizeof(*we));
136 		if (we == NULL)
137 			iv_fatal("iv_wait_got_sigchld: out of memory");
138 
139 		we->status = status;
140 #ifdef HAVE_WAIT4
141 		we->rusage = rusage;
142 #endif
143 
144 		p = __iv_wait_interest_find(pid);
145 		if (p != NULL) {
146 			iv_list_add_tail(&we->list, &p->events_pending);
147 			iv_event_post(&p->ev);
148 		} else {
149 			free(we);
150 		}
151 
152 		/*
153 		 * If this pid is now dead, avoid queueing subsequent
154 		 * process status change events to this interest, as the
155 		 * pid might be reused between us queueing this event to
156 		 * the interest and the interest being unregistered,
157 		 * and events for the new user of this pid would then end
158 		 * up at the wrong interest.
159 		 */
160 		if (iv_wait_status_dead(status)) {
161 			iv_avl_tree_delete(&iv_wait_interests, &p->avl_node);
162 			p->flags = IV_WAIT_STATUS_DEAD;
163 		}
164 	}
165 	___mutex_unlock(&iv_wait_lock);
166 }
167 
168 struct iv_wait_thr_info {
169 	int				wait_count;
170 	struct iv_signal		sigchld_interest;
171 	struct iv_wait_interest		*handled_wait_interest;
172 };
173 
iv_wait_tls_init_thread(void * _tinfo)174 static void iv_wait_tls_init_thread(void *_tinfo)
175 {
176 	struct iv_wait_thr_info *tinfo = _tinfo;
177 
178 	tinfo->wait_count = 0;
179 
180 	IV_SIGNAL_INIT(&tinfo->sigchld_interest);
181 	tinfo->sigchld_interest.signum = SIGCHLD;
182 	tinfo->sigchld_interest.flags = IV_SIGNAL_FLAG_EXCLUSIVE;
183 	tinfo->sigchld_interest.handler = iv_wait_got_sigchld;
184 
185 	tinfo->handled_wait_interest = NULL;
186 }
187 
188 static struct iv_tls_user iv_wait_tls_user = {
189 	.sizeof_state	= sizeof(struct iv_wait_thr_info),
190 	.init_thread	= iv_wait_tls_init_thread,
191 };
192 
193 static void iv_wait_tls_init(void) __attribute__((constructor));
iv_wait_tls_init(void)194 static void iv_wait_tls_init(void)
195 {
196 	___mutex_init(&iv_wait_lock);
197 
198 	iv_tls_user_register(&iv_wait_tls_user);
199 }
200 
iv_wait_completion(void * _this)201 static void iv_wait_completion(void *_this)
202 {
203 	struct iv_wait_thr_info *tinfo = iv_tls_user_ptr(&iv_wait_tls_user);
204 	struct iv_wait_interest *this = _this;
205 	struct iv_list_head events;
206 
207 	___mutex_lock(&iv_wait_lock);
208 	__iv_list_steal_elements(&this->events_pending, &events);
209 	___mutex_unlock(&iv_wait_lock);
210 
211 	tinfo->handled_wait_interest = this;
212 
213 	while (!iv_list_empty(&events)) {
214 		struct wait_event *we;
215 
216 		we = iv_container_of(events.next, struct wait_event, list);
217 		iv_list_del(&we->list);
218 
219 		if (tinfo->handled_wait_interest != NULL) {
220 #ifdef HAVE_WAIT4
221 			this->handler(this->cookie, we->status, &we->rusage);
222 #else
223 			this->handler(this->cookie, we->status, NULL);
224 #endif
225 		}
226 
227 		free(we);
228 	}
229 
230 	tinfo->handled_wait_interest = NULL;
231 }
232 
__iv_wait_interest_register(struct iv_wait_thr_info * tinfo,struct iv_wait_interest * this)233 static void __iv_wait_interest_register(struct iv_wait_thr_info *tinfo,
234 					struct iv_wait_interest *this)
235 {
236 	if (!tinfo->wait_count++)
237 		iv_signal_register(&tinfo->sigchld_interest);
238 
239 	IV_EVENT_INIT(&this->ev);
240 	this->ev.handler = iv_wait_completion;
241 	this->ev.cookie = this;
242 	iv_event_register(&this->ev);
243 
244 	INIT_IV_LIST_HEAD(&this->events_pending);
245 
246 	this->dummy = NULL;
247 
248 	this->flags = 0;
249 }
250 
iv_wait_interest_register(struct iv_wait_interest * this)251 void iv_wait_interest_register(struct iv_wait_interest *this)
252 {
253 	struct iv_wait_thr_info *tinfo = iv_tls_user_ptr(&iv_wait_tls_user);
254 
255 	__iv_wait_interest_register(tinfo, this);
256 
257 	___mutex_lock(&iv_wait_lock);
258 	iv_avl_tree_insert(&iv_wait_interests, &this->avl_node);
259 	___mutex_unlock(&iv_wait_lock);
260 }
261 
__iv_wait_interest_unregister(struct iv_wait_thr_info * tinfo,struct iv_wait_interest * this)262 static void __iv_wait_interest_unregister(struct iv_wait_thr_info *tinfo,
263 					  struct iv_wait_interest *this)
264 {
265 	iv_event_unregister(&this->ev);
266 
267 	while (!iv_list_empty(&this->events_pending)) {
268 		struct wait_event *we;
269 
270 		we = iv_container_of(this->events_pending.next,
271 				     struct wait_event, list);
272 		iv_list_del(&we->list);
273 		free(we);
274 	}
275 
276 	if (tinfo->handled_wait_interest == this)
277 		tinfo->handled_wait_interest = NULL;
278 
279 	if (!--tinfo->wait_count)
280 		iv_signal_unregister(&tinfo->sigchld_interest);
281 }
282 
iv_wait_interest_register_spawn(struct iv_wait_interest * this,void (* fn)(void * cookie),void * cookie)283 int iv_wait_interest_register_spawn(struct iv_wait_interest *this,
284 				    void (*fn)(void *cookie), void *cookie)
285 {
286 	struct iv_wait_thr_info *tinfo = iv_tls_user_ptr(&iv_wait_tls_user);
287 	pid_t pid;
288 
289 	__iv_wait_interest_register(tinfo, this);
290 
291 	___mutex_lock(&iv_wait_lock);
292 
293 	pid = fork();
294 	if (pid < 0) {
295 		___mutex_unlock(&iv_wait_lock);
296 		__iv_wait_interest_unregister(tinfo, this);
297 		return pid;
298 	}
299 
300 	if (pid == 0) {
301 		iv_signal_child_reset_postfork();
302 		fn(cookie);
303 		exit(1);
304 	} else {
305 		this->pid = pid;
306 		iv_avl_tree_insert(&iv_wait_interests, &this->avl_node);
307 	}
308 
309 	___mutex_unlock(&iv_wait_lock);
310 
311 	return 0;
312 }
313 
iv_wait_interest_unregister(struct iv_wait_interest * this)314 void iv_wait_interest_unregister(struct iv_wait_interest *this)
315 {
316 	struct iv_wait_thr_info *tinfo = iv_tls_user_ptr(&iv_wait_tls_user);
317 
318 	___mutex_lock(&iv_wait_lock);
319 	if (!(this->flags & IV_WAIT_STATUS_DEAD))
320 		iv_avl_tree_delete(&iv_wait_interests, &this->avl_node);
321 	___mutex_unlock(&iv_wait_lock);
322 
323 	__iv_wait_interest_unregister(tinfo, this);
324 }
325 
iv_wait_interest_kill(const struct iv_wait_interest * this,int sig)326 int iv_wait_interest_kill(const struct iv_wait_interest *this, int sig)
327 {
328 	int ret;
329 
330 	___mutex_lock(&iv_wait_lock);
331 	if (!(this->flags & IV_WAIT_STATUS_DEAD))
332 		ret = kill(this->pid, sig);
333 	else
334 		ret = -ESRCH;
335 	___mutex_unlock(&iv_wait_lock);
336 
337 	return ret;
338 }
339