xref: /dragonfly/sbin/udevd/test_udevd.c (revision abf903a5)
1 #include <sys/types.h>
2 #include <sys/device.h>
3 #include <sys/wait.h>
4 #include <sys/socket.h>
5 #include <sys/poll.h>
6 #include <sys/queue.h>
7 #include <sys/un.h>
8 
9 #include <err.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <libgen.h>
13 #include <signal.h>
14 #include <stdarg.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <syslog.h>
19 #include <unistd.h>
20 
21 #include <libprop/proplib.h>
22 #include <sys/udev.h>
23 
24 #define	LISTEN_SOCKET_FILE	"/tmp/udevd.socket"
25 #define SOCKFILE_NAMELEN	strlen(LISTEN_SOCKET_FILE)+1
26 
27 int conn_local_server(const char *sockfile, int socktype, int nonblock,
28 		  int *retsock);
29 prop_dictionary_t udevd_get_command_dict(char *command);
30 void udevd_request_devs(int s);
31 
32 struct udev {
33 	int	gp_fd;
34 	int	monitor_fd;
35 	int	refs;
36 
37 	void	*userdata;
38 };
39 
40 struct udev_enumerate {
41 	struct udev	*udev_ctx;
42 	prop_array_t	pa;
43 	int	refs;
44 	TAILQ_HEAD(, udev_list_entry)	list_entries;
45 };
46 
47 struct udev_list_entry {
48 	prop_dictionary_t	dict;
49 	TAILQ_ENTRY(udev_list_entry)	link;
50 };
51 
52 struct udev_monitor {
53 	struct udev	*udev_ctx;
54 	prop_array_t	ev_filt;
55 	int	socket;
56 	int	user_socket; /* maybe... one day... */
57 	int	refs;
58 };
59 
60 struct udev_device {
61 	struct udev	*udev_ctx;
62 	prop_dictionary_t	dict;
63 	int	ev_type;
64 	int	refs;
65 };
66 
67 struct udev *
68 udev_ref(struct udev *udev_ctx)
69 {
70 	atomic_add_int(&udev_ctx->refs, 1);
71 
72 	return udev_ctx;
73 }
74 
75 void
76 udev_unref(struct udev *udev_ctx)
77 {
78 	int refcount;
79 
80 	refcount = atomic_fetchadd_int(&udev_ctx->refs, -1);
81 
82 	if (refcount == 1) {
83 		atomic_subtract_int(&udev_ctx->refs, 0x400); /* in destruction */
84 		if (udev_ctx->gp_fd != -1)
85 			close (udev_ctx->gp_fd);
86 		if (udev_ctx->monitor_fd != -1)
87 			close (udev_ctx->monitor_fd);
88 
89 		free(udev_ctx);
90 	}
91 }
92 
93 struct udev *
94 udev_new()
95 {
96 	struct udev *udev_ctx;
97 
98 	udev_ctx = malloc(sizeof(struct udev));
99 
100 	udev_ctx->refs = 1;
101 	udev_ctx->gp_fd = -1;
102 	udev_ctx->monitor_fd = -1;
103 	udev_ctx->userdata = NULL;
104 
105 	return udev_ctx;
106 }
107 
108 const char *udev_get_dev_path(struct udev *udev_ctx __unused)
109 {
110 	return "/dev";
111 }
112 
113 void *
114 udev_get_userdata(struct udev *udev_ctx)
115 {
116 	return udev_ctx->userdata;
117 }
118 
119 void
120 udev_set_userdata(struct udev *udev_ctx, void *userdata)
121 {
122 	udev_ctx->userdata = userdata;
123 }
124 
125 struct udev_enumerate *
126 udev_enumerate_new(struct udev *udev_ctx)
127 {
128 	struct udev_enumerate *udev_enum;
129 
130 	udev_enum = malloc(sizeof(struct udev_enumerate));
131 
132 	udev_enum->refs = 1;
133 	udev_enum->pa = NULL;
134 	TAILQ_INIT(&udev_enum->list_entries);
135 	udev_enum->udev_ctx = udev_ref(udev_ctx);
136 }
137 
138 struct udev_enumerate *
139 udev_enumerate_ref(struct udev_enumerate *udev_enum)
140 {
141 	atomic_add_int(&udev_enum->refs, 1);
142 
143 	return udev_enum;
144 }
145 
146 void
147 udev_enumerate_unref(struct udev_enumerate *udev_enum)
148 {
149 	struct udev_list_entry	*le;
150 	int refcount;
151 
152 	refcount = atomic_fetchadd_int(&udev_enum->refs, -1);
153 
154 	if (refcount == 1) {
155 		atomic_subtract_int(&udev_enum->refs, 0x400); /* in destruction */
156 		if (udev_enum->pa != NULL)
157 			prop_object_release(udev_enum->pa);
158 
159 		while (!TAILQ_EMPTY(&udev_enum->list_entries)) {
160 			le = TAILQ_FIRST(&udev_enum->list_entries);
161 			TAILQ_REMOVE(&udev_enum->list_entries, le, link);
162 			prop_object_release(le->dict);
163 			free(le);
164 		}
165 		udev_unref(udev_enum->udev_ctx);
166 		free(udev_enum);
167 	}
168 }
169 
170 struct udev *
171 udev_enumerate_get_udev(struct udev_enumerate *udev_enum)
172 {
173 	return udev_enum->udev_ctx;
174 }
175 
176 int
177 udev_enumerate_scan_devices(struct udev_enumerate *udev_enum)
178 {
179 	prop_array_t	pa;
180 
181 	if (udev_enum->udev_ctx->gp_fd == -1)
182 		return -1;
183 
184 	pa = udevd_request_devs(udev_enum->udev_ctx->gp_fd);
185 	if (pa == NULL)
186 		return -1;
187 
188 	prop_object_retain(pa);
189 
190 	if (udev_enum->pa != NULL)
191 		prop_object_release(udev_enum->pa);
192 
193 	udev_enum->iter = NULL;
194 	udev_enum->pa = pa;
195 
196 	return 0;
197 }
198 
199 struct udev_list_entry *
200 udev_enumerate_get_list_entry(struct udev_enumerate *udev_enum)
201 {
202 	struct udev_list_entry *le;
203 	prop_object_iterator_t	iter;
204 
205 	/* If the list is not empty, assume it was populated in an earlier call */
206 	if (!TAILQ_EMPTY(&udev_enum->list_entries))
207 		return TAILQ_FIRST(&udev_enum->list_entries);
208 
209 	iter = prop_array_iterator(udev_enum->pa);
210 	if (iter == NULL)
211 		return NULL;
212 
213 	while ((dict = prop_object_iterator_next(iter)) != NULL) {
214 		le = malloc(sizeof(struct udev_list_entry));
215 		if (le == NULL)
216 			goto out;
217 
218 		prop_object_retain(dict);
219 		le->dict = dict;
220 		TAILQ_INSERT_TAIL(&udev_enum->list_entries, le, link);
221 	}
222 
223 	le = TAILQ_FIRST(&udev_enum->list_entries);
224 
225 out:
226 	prop_object_iterator_release(iter);
227 	return le;
228 }
229 
230 prop_array_t
231 udev_enumerate_get_array(struct udev_enumerate *udev_enum)
232 {
233 	return udev_enum->pa;
234 }
235 
236 struct udev_list_entry *
237 udev_list_entry_get_next(struct udev_list_entry *list_entry)
238 {
239 	return TAILQ_NEXT(list_entry, link);
240 }
241 
242 prop_dictionary_t
243 udev_list_entry_get_dictionary(struct udev_list_entry *list_entry)
244 {
245 	return list_entry->dict;
246 }
247 
248 #define	udev_list_entry_foreach(list_entry, first_entry) \
249 	for(list_entry = first_entry; \
250 	    list_entry != NULL; \
251 	    list_entry = udev_list_entry_get_next(list_entry))
252 
253 
254 
255 
256 
257 
258 struct udev_monitor *
259 udev_monitor_new(struct udev *udev_ctx)
260 {
261 	struct udev_monitor *udev_monitor;
262 	int ret, s;
263 
264 	ret = conn_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0, &s);
265 	if (ret < 0)
266 		return NULL;
267 
268 	udev_monitor = malloc(sizeof(struct udev_monitor));
269 	if (udev_monitor == NULL)
270 		return NULL;
271 
272 	udev_monitor->refs = 1;
273 	udev_monitor->ev_filt = NULL;
274 	udev_monitor->socket = s;
275 	udev_monitor->user_socket = 1;
276 	udev_monitor->udev_ctx = udev_ref(udev_ctx);
277 
278 	return udev_monitor;
279 }
280 
281 
282 struct udev_monitor *
283 udev_monitor_ref(struct udev_monitor *udev_monitor)
284 {
285 	atomic_add_int(&udev_monitor->refs, 1);
286 
287 	return udev_monitor;
288 }
289 
290 void
291 udev_monitor_unref(struct udev_monitor *udev_monitor)
292 {
293 	int refcount;
294 
295 	refcount = atomic_fetchadd_int(&udev_monitor->refs, -1);
296 
297 	if (refcount == 1) {
298 		atomic_subtract_int(&udev_monitor->refs, 0x400); /* in destruction */
299 		if (udev_monitor->ev_filt != NULL)
300 			prop_object_release(udev_monitor->ev_filt);
301 
302 		if (udev_monitor->socket != -1)
303 			close(udev_monitor->socket);
304 		if (udev_monitor->user_socket != -1)
305 			close(udev_monitor->user_socket);
306 
307 		udev_unref(udev_monitor->udev_ctx);
308 		free(udev_monitor);
309 	}
310 }
311 
312 struct udev *
313 udev_monitor_get_udev(struct udev_monitor *udev_monitor)
314 {
315 	return udev_monitor->udev_ctx;
316 }
317 
318 int
319 udev_monitor_get_fd(struct udev_monitor *udev_monitor)
320 {
321 	return udev_monitor->socket;
322 }
323 
324 struct udev_device *
325 udev_monitor_receive_device(struct udev_monitor *udev_monitor)
326 {
327 	struct udev_device *udev_dev;
328 	prop_dictionary_t dict;
329 	prop_number_t	pn;
330 	char *xml;
331 	int n, evtype;
332 
333 	xml = malloc(12*1024*1024);
334 	if (xml == NULL)
335 		return NULL;
336 
337 	if ((n = read_xml(udev_monitor->socket, xml, 12*1024*1024)) <= 0) {
338 		free(xml);
339 		return NULL;
340 	}
341 
342 	xml[n+1] = '\0';
343 	dict = prop_dictionary_internalize(xml);
344 	free(xml);
345 	if (dict == NULL)
346 		return NULL;
347 
348 	pn = prop_dictionary_get(dict, "evtype");
349 	if (pn == NULL) {
350 		prop_object_release(dict);
351 		return NULL;
352 	}
353 
354 	udev_dev = malloc(sizeof(struct udev_dev));
355 	if (udev_dev == NULL) {
356 		prop_object_release(dict);
357 		return NULL;
358 	}
359 
360 	udev_dev->refs = 1;
361 	udev_dev->ev_type = prop_number_integer_value(pn);
362 	udev_dev->dict = prop_dictionary_get(dict, "evdict");
363 	if (udev_dev->dict == NULL) {
364 		free(udev_dev);
365 		return NULL;
366 	}
367 	udev_dev->udev_ctx = udev_ref(udev_monitor->udev_ctx);
368 
369 out:
370 	return udev_dev;
371 }
372 
373 int
374 udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
375 {
376 	prop_dictionary_t	dict;
377 	char *xml;
378 	/* ->socket, ->user_socket, ->ev_filt */
379 
380 	dict = udevd_get_command_dict(__DECONST(char *, "monitor"));
381 	if (dict == NULL)
382 		return -1;
383 
384 	/* Add event filters to message, if available */
385 	if (udev_monitor->ev_filt != NULL) {
386 		if (prop_dictionary_set(dict, "filters",
387 		    udev_monitor->ev_filt) == false) {
388 			prop_object_release(dict);
389 			return -1;
390 		}
391 	}
392 
393 	xml = prop_dictionary_externalize(dict);
394 	prop_object_release(dict);
395 	if (xml == NULL)
396 		return -1;
397 
398 	n = send_xml(udev_monitor->socket, xml);
399 	free(xml);
400 	if (n <= 0)
401 		return NULL;
402 
403 	return 0;
404 }
405 
406 int
407 udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor,
408 						const char *subsystem,
409 						const char *devtype __unused)
410 {
411 	int ret;
412 
413 	ret = _udev_monitor_filter_add_match_gen(udev_monitor,
414 						 EVENT_FILTER_TYPE_WILDCARD,
415 						 0,
416 						 "subsystem",
417 						 subsystem);
418 
419 	return ret;
420 }
421 
422 int
423 udev_monitor_filter_add_match_expr(struct udev_monitor *udev_monitor,
424 			      	   const char *key,
425 			      	   char *expr)
426 {
427 	int ret;
428 
429 	ret = _udev_monitor_filter_add_match_gen(udev_monitor,
430 						 EVENT_FILTER_TYPE_WILDCARD,
431 						 0,
432 						 key,
433 						 expr);
434 
435 	return ret;
436 }
437 
438 int
439 udev_monitor_filter_add_nomatch_expr(struct udev_monitor *udev_monitor,
440 			      	     const char *key,
441 			      	     char *expr)
442 {
443 	int ret;
444 
445 	ret = _udev_monitor_filter_add_match_gen(udev_monitor,
446 						 EVENT_FILTER_TYPE_WILDCARD,
447 						 1,
448 						 key,
449 						 expr);
450 
451 	return ret;
452 }
453 
454 int
455 udev_monitor_filter_add_match_regex(struct udev_monitor *udev_monitor,
456 			      	   const char *key,
457 			      	   char *expr)
458 {
459 	int ret;
460 
461 	ret = _udev_monitor_filter_add_match_gen(udev_monitor,
462 						 EVENT_FILTER_TYPE_REGEX,
463 						 0,
464 						 key,
465 						 expr);
466 
467 	return ret;
468 }
469 
470 int
471 udev_monitor_filter_add_nomatch_regex(struct udev_monitor *udev_monitor,
472 			      	     const char *key,
473 			      	     char *expr)
474 {
475 	int ret;
476 
477 	ret = _udev_monitor_filter_add_match_gen(udev_monitor,
478 						 EVENT_FILTER_TYPE_REGEX,
479 						 1,
480 						 key,
481 						 expr);
482 
483 	return ret;
484 }
485 
486 int
487 _udev_monitor_filter_add_match_gen(struct udev_monitor *udev_monitor,
488 				   int type,
489 				   int neg,
490 				   const char *key,
491 				   char *expr)
492 {
493 	prop_array_t		pa;
494 	prop_dictionary_t	dict;
495 	int error;
496 
497 	if (subsystem == NULL)
498 		return NULL;
499 
500 	dict = prop_dictionary_create();
501 	if (dict == NULL)
502 		return -1;
503 
504 	error = _udev_dict_set_cstr(dict, "key", key);
505 	if (error != 0)
506 		goto error_out;
507 	error = _udev_dict_set_int(dict, "type", type);
508 	if (error != 0)
509 		goto error_out;
510 	error = _udev_dict_set_int(dict, "expr", expr);
511 	if (error != 0)
512 		goto error_out;
513 
514 	if (neg) {
515 		error = _udev_dict_set_int(dict, "negative", 1);
516 		if (error != 0)
517 			goto error_out;
518 	}
519 
520 	if (udev_monitor->ev_filt == NULL) {
521 		pa = prop_array_create();
522 		if (pa == NULL)
523 			goto error_out;
524 
525 		udev_monitor->ev_filt = pa;
526 	}
527 
528 	if (prop_array_add(udev_monitor->ev_filt, dict) == false)
529 		goto error_out;
530 
531 	return 0;
532 
533 error_out:
534 	prop_object_release(dict);
535 	return -1;
536 }
537 
538 struct udev_device *
539 udev_device_ref(struct udev_device *udev_device)
540 {
541 	atomic_add_int(&udev_device->refs, 1);
542 
543 	return udev_device;
544 }
545 
546 void
547 udev_device_unref(struct udev_device *udev_device)
548 {
549 	int refcount;
550 
551 	refcount = atomic_fetchadd_int(&udev_device->refs, -1);
552 
553 	if (refcount == 1) {
554 		atomic_subtract_int(&udev_device->refs, 0x400); /* in destruction */
555 		if (udev_device->dict != NULL)
556 			prop_object_release(udev_device->dict);
557 
558 		udev_unref(udev_device->udev_ctx);
559 		free(udev_device);
560 	}
561 }
562 
563 prop_dictionary_t
564 udev_device_get_dictionary(struct udev_device *udev_device)
565 {
566 	return udev_device->dict;
567 }
568 
569 struct udev *
570 udev_device_get_udev(struct udev_device *udev_device)
571 {
572 	return udev_device->udev_ctx;
573 }
574 
575 int
576 send_xml(int s, char *xml)
577 {
578 	ssize_t r,n;
579 	size_t sz;
580 
581 	sz = strlen(xml) + 1;
582 
583 	r = send(s, &sz, sizeof(sz), 0);
584 	if (r <= 0)
585 		return r;
586 
587 	r = 0;
588 	while (r < (ssize_t)sz) {
589 		n = send(s, xml+r, sz-r, 0);
590 		if (n <= 0)
591 			return n;
592 		r += n;
593 	}
594 
595 	return r;
596 }
597 
598 int
599 read_xml(int s, char *buf, size_t buf_sz)
600 {
601 	size_t sz;
602 	int n, r;
603 
604 	n = recv(s, &sz, sizeof(sz), MSG_WAITALL);
605 	if (n <= 0)
606 		return n;
607 
608 	r = 0;
609 	while ((r < (ssize_t)sz) && (r < (ssize_t)buf_sz)) {
610 		n = recv(s, buf+r, sz-r, MSG_WAITALL);
611 		if (n <= 0)
612 			return n;
613 		r += n;
614 	}
615 
616 	return r;
617 }
618 
619 
620 
621 static int
622 _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str)
623 {
624 	prop_string_t	ps;
625 
626 	ps = prop_string_create_cstring(str);
627 	if (ps == NULL)
628 		return ENOMEM;
629 
630 	if (prop_dictionary_set(dict, key, ps) == false) {
631 		prop_object_release(ps);
632 		return ENOMEM;
633 	}
634 
635 	prop_object_release(ps);
636 	return 0;
637 }
638 
639 static int
640 _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val)
641 {
642 	prop_number_t	pn;
643 
644 	pn = prop_number_create_integer(val);
645 	if (pn == NULL)
646 		return ENOMEM;
647 
648 	if (prop_dictionary_set(dict, key, pn) == false) {
649 		prop_object_release(pn);
650 		return ENOMEM;
651 	}
652 
653 	prop_object_release(pn);
654 	return 0;
655 }
656 
657 static int
658 _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val)
659 {
660 	prop_number_t	pn;
661 
662 	pn = prop_number_create_unsigned_integer(val);
663 	if (pn == NULL)
664 		return ENOMEM;
665 
666 	if (prop_dictionary_set(dict, key, pn) == false) {
667 		prop_object_release(pn);
668 		return ENOMEM;
669 	}
670 
671 	prop_object_release(pn);
672 	return 0;
673 }
674 
675 int
676 conn_local_server(const char *sockfile, int socktype, int nonblock,
677 		  int *retsock)
678 {
679 	int s;
680 	struct sockaddr_un serv_addr;
681 
682 	*retsock = -1;
683 	if ((s = socket(AF_UNIX, socktype, 0)) < 0)
684 		return -1;
685 
686 	memset(&serv_addr, 0, sizeof(serv_addr));
687 	serv_addr.sun_family = AF_UNIX;
688 	strncpy(serv_addr.sun_path, sockfile, SOCKFILE_NAMELEN);
689 	serv_addr.sun_path[SOCKFILE_NAMELEN - 1] = '\0';
690 
691 	if (nonblock && unblock_descriptor(s) < 0) {
692 		close(s);
693 		return -1;
694 	}
695 
696 	*retsock = s;
697 	return connect(s, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
698 }
699 
700 prop_dictionary_t
701 udevd_get_command_dict(char *command)
702 {
703 	prop_dictionary_t	dict;
704 	int	error;
705 
706 	dict = prop_dictionary_create();
707 	if (dict == NULL)
708 		return NULL;
709 
710 	if ((error = _udev_dict_set_cstr(dict, "command", command)))
711 		goto error_out;
712 
713 	return dict;
714 
715 error_out:
716 	prop_object_release(dict);
717 	return NULL;
718 }
719 
720 prop_array_t
721 udevd_request_devs(int s)
722 {
723 	prop_array_t	pa;
724 	prop_dictionary_t	dict;
725 	char *xml;
726 
727 	int n, t;
728 
729 	dict = udevd_get_command_dict(__DECONST(char *, "getdevs"));
730 	if (dict == NULL)
731 		return NULL;
732 
733 	xml = prop_dictionary_externalize(dict);
734 	prop_object_release(dict);
735 	if (xml == NULL)
736 		return NULL;
737 
738 	n = send_xml(s, xml);
739 	free(xml);
740 
741 	if (n <= 0)
742 		return NULL;
743 
744 	xml = malloc(12*1024*1024); /* generous 12 MB */
745 	if ((n = read_xml(s, xml, 12*1024*1024)) <= 0) {
746 		free(xml);
747 		return NULL;
748 	}
749 
750 	xml[n+1] = '\0';
751 	pa = prop_array_internalize(xml);
752 	free(xml);
753 	return (pa);
754 }
755 
756 
757 
758 int
759 main(void)
760 {
761 	int ret, s;
762 
763 	ret = conn_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0, &s);
764 	if (ret < 0)
765 		err(1, "conn_local_server");
766 
767 	udevd_request_devs(s);
768 
769 	return 0;
770 }
771