1 /*
2  * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Alex Hornung <ahornung@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include <sys/types.h>
35 #include <sys/device.h>
36 #include <sys/wait.h>
37 #include <sys/socket.h>
38 #include <sys/ioctl.h>
39 #include <sys/poll.h>
40 #include <sys/queue.h>
41 #include <sys/un.h>
42 #include <cpu/inttypes.h>
43 
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <libgen.h>
48 #include <regex.h>
49 #include <signal.h>
50 #include <stdarg.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <syslog.h>
55 #include <unistd.h>
56 
57 #include <libprop/proplib.h>
58 #include <sys/udev.h>
59 #define LIBDEVATTR_INTERNAL
60 #include "devattr.h"
61 
62 struct udev_enumerate {
63 	struct udev	*udev_ctx;
64 	prop_array_t	ev_filt;
65 	prop_array_t	pa;
66 	int	refs;
67 	TAILQ_HEAD(, udev_list_entry)	list_entries;
68 };
69 
70 struct udev_list_entry {
71 	struct udev	*udev_ctx;
72 	prop_dictionary_t	dict;
73 	TAILQ_ENTRY(udev_list_entry)	link;
74 };
75 
76 struct udev_enumerate *
77 udev_enumerate_new(struct udev *udev_ctx)
78 {
79 	struct udev_enumerate *udev_enum;
80 
81 	udev_enum = malloc(sizeof(struct udev_enumerate));
82 
83 	udev_enum->refs = 1;
84 	udev_enum->ev_filt = NULL;
85 	udev_enum->pa = NULL;
86 	TAILQ_INIT(&udev_enum->list_entries);
87 	udev_enum->udev_ctx = udev_ref(udev_ctx);
88 
89 	return udev_enum;
90 }
91 
92 struct udev_enumerate *
93 udev_enumerate_ref(struct udev_enumerate *udev_enum)
94 {
95 	atomic_add_int(&udev_enum->refs, 1);
96 
97 	return udev_enum;
98 }
99 
100 void
101 udev_enumerate_unref(struct udev_enumerate *udev_enum)
102 {
103 	struct udev_list_entry	*le;
104 	int refcount;
105 
106 	refcount = atomic_fetchadd_int(&udev_enum->refs, -1);
107 
108 	if (refcount == 1) {
109 		atomic_subtract_int(&udev_enum->refs, 0x400); /* in destruction */
110 		if (udev_enum->pa != NULL)
111 			prop_object_release(udev_enum->pa);
112 		if (udev_enum->ev_filt != NULL)
113 			prop_object_release(udev_enum->ev_filt);
114 
115 		while (!TAILQ_EMPTY(&udev_enum->list_entries)) {
116 			le = TAILQ_FIRST(&udev_enum->list_entries);
117 			TAILQ_REMOVE(&udev_enum->list_entries, le, link);
118 			prop_object_release(le->dict);
119 			free(le);
120 		}
121 		udev_unref(udev_enum->udev_ctx);
122 		free(udev_enum);
123 	}
124 }
125 
126 struct udev *
127 udev_enumerate_get_udev(struct udev_enumerate *udev_enum)
128 {
129 	return udev_enum->udev_ctx;
130 }
131 
132 int
133 udev_enumerate_scan_devices(struct udev_enumerate *udev_enum)
134 {
135 	prop_array_t	pa;
136 
137 	if (udev_get_fd(udev_enum->udev_ctx) == -1)
138 		return -1;
139 
140 	pa = udevd_request_devs(udev_get_fd(udev_enum->udev_ctx), udev_enum->ev_filt);
141 	if (pa == NULL)
142 		return -1;
143 
144 	prop_object_retain(pa);
145 
146 	if (udev_enum->pa != NULL)
147 		prop_object_release(udev_enum->pa);
148 
149 	udev_enum->pa = pa;
150 
151 	return 0;
152 }
153 
154 struct udev_list_entry *
155 udev_enumerate_get_list_entry(struct udev_enumerate *udev_enum)
156 {
157 	struct udev_list_entry *le;
158 	prop_object_iterator_t	iter;
159 	prop_dictionary_t	dict;
160 
161 	/* If the list is not empty, assume it was populated in an earlier call */
162 	if (!TAILQ_EMPTY(&udev_enum->list_entries))
163 		return TAILQ_FIRST(&udev_enum->list_entries);
164 
165 	iter = prop_array_iterator(udev_enum->pa);
166 	if (iter == NULL)
167 		return NULL;
168 
169 	while ((dict = prop_object_iterator_next(iter)) != NULL) {
170 		le = malloc(sizeof(struct udev_list_entry));
171 		if (le == NULL)
172 			goto out;
173 
174 		prop_object_retain(dict);
175 		le->dict = dict;
176 		le->udev_ctx = udev_enum->udev_ctx;
177 		TAILQ_INSERT_TAIL(&udev_enum->list_entries, le, link);
178 	}
179 
180 	le = TAILQ_FIRST(&udev_enum->list_entries);
181 
182 out:
183 	prop_object_iterator_release(iter);
184 	return le;
185 }
186 
187 prop_array_t
188 udev_enumerate_get_array(struct udev_enumerate *udev_enum)
189 {
190 	return udev_enum->pa;
191 }
192 
193 struct udev_list_entry *
194 udev_list_entry_get_next(struct udev_list_entry *list_entry)
195 {
196 	return TAILQ_NEXT(list_entry, link);
197 }
198 
199 prop_dictionary_t
200 udev_list_entry_get_dictionary(struct udev_list_entry *list_entry)
201 {
202 	return list_entry->dict;
203 }
204 
205 struct udev_device *
206 udev_list_entry_get_device(struct udev_list_entry *list_entry)
207 {
208 	struct udev_device *udev_dev;
209 
210 	udev_dev = udev_device_new_from_dictionary(list_entry->udev_ctx,
211 						   list_entry->dict);
212 
213 	return udev_dev;
214 }
215 
216 int
217 udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enum,
218 						const char *subsystem)
219 {
220 	int ret;
221 
222 	ret = _udev_enumerate_filter_add_match_gen(udev_enum,
223 						 EVENT_FILTER_TYPE_WILDCARD,
224 						 0,
225 						 "subsystem",
226 						 __DECONST(char *, subsystem));
227 
228 	return ret;
229 }
230 
231 int
232 udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enum,
233 						const char *subsystem)
234 {
235 	int ret;
236 
237 	ret = _udev_enumerate_filter_add_match_gen(udev_enum,
238 						 EVENT_FILTER_TYPE_WILDCARD,
239 						 1,
240 						 "subsystem",
241 						 __DECONST(char *, subsystem));
242 
243 	return ret;
244 }
245 
246 int
247 udev_enumerate_add_match_expr(struct udev_enumerate *udev_enum,
248 			      const char *key,
249 			      char *expr)
250 {
251 	int ret;
252 
253 	ret = _udev_enumerate_filter_add_match_gen(udev_enum,
254 						 EVENT_FILTER_TYPE_WILDCARD,
255 						 0,
256 						 key,
257 						 expr);
258 
259 	return ret;
260 }
261 
262 int
263 udev_enumerate_add_nomatch_expr(struct udev_enumerate *udev_enum,
264 			        const char *key,
265 			        char *expr)
266 {
267 	int ret;
268 
269 	ret = _udev_enumerate_filter_add_match_gen(udev_enum,
270 						 EVENT_FILTER_TYPE_WILDCARD,
271 						 1,
272 						 key,
273 						 expr);
274 
275 	return ret;
276 }
277 
278 int
279 udev_enumerate_add_match_regex(struct udev_enumerate *udev_enum,
280 			      const char *key,
281 			      char *expr)
282 {
283 	int ret;
284 
285 	ret = _udev_enumerate_filter_add_match_gen(udev_enum,
286 						 EVENT_FILTER_TYPE_REGEX,
287 						 0,
288 						 key,
289 						 expr);
290 
291 	return ret;
292 }
293 
294 int
295 udev_enumerate_add_nomatch_regex(struct udev_enumerate *udev_enum,
296 			        const char *key,
297 			        char *expr)
298 {
299 	int ret;
300 
301 	ret = _udev_enumerate_filter_add_match_gen(udev_enum,
302 						 EVENT_FILTER_TYPE_REGEX,
303 						 1,
304 						 key,
305 						 expr);
306 
307 	return ret;
308 }
309 
310 int
311 _udev_enumerate_filter_add_match_gen(struct udev_enumerate *udev_enum,
312 				     int type,
313 				     int neg,
314 				     const char *key,
315 				     char *expr)
316 {
317 	prop_array_t		pa;
318 	int error;
319 
320 	if (udev_enum->ev_filt == NULL) {
321 		pa = prop_array_create_with_capacity(5);
322 		if (pa == NULL)
323 			return -1;
324 
325 		udev_enum->ev_filt = pa;
326 	}
327 
328 	error = _udev_filter_add_match_gen(udev_enum->ev_filt, type, neg, key, expr);
329 
330 	return error;
331 }
332