xref: /dragonfly/lib/libdevattr/devattr.c (revision c9c5aa9e)
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/wait.h>
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <sys/poll.h>
39 #include <sys/queue.h>
40 #include <sys/un.h>
41 #include <cpu/inttypes.h>
42 
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <libgen.h>
47 #include <regex.h>
48 #include <signal.h>
49 #include <stdarg.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <syslog.h>
54 #include <unistd.h>
55 
56 #include <libprop/proplib.h>
57 #include <sys/udev.h>
58 #define LIBDEVATTR_INTERNAL
59 #include "devattr.h"
60 
61 struct udev {
62 	int	gp_fd;
63 	int	monitor_fd;
64 	int	refs;
65 
66 	void	*userdata;
67 };
68 
69 struct udev *
70 udev_ref(struct udev *udev_ctx)
71 {
72 	atomic_add_int(&udev_ctx->refs, 1);
73 
74 	return udev_ctx;
75 }
76 
77 void
78 udev_unref(struct udev *udev_ctx)
79 {
80 	int refcount;
81 
82 	refcount = atomic_fetchadd_int(&udev_ctx->refs, -1);
83 
84 	if (refcount == 1) {
85 		atomic_subtract_int(&udev_ctx->refs, 0x400); /* in destruction */
86 		if (udev_ctx->gp_fd != -1)
87 			close (udev_ctx->gp_fd);
88 		if (udev_ctx->monitor_fd != -1)
89 			close (udev_ctx->monitor_fd);
90 
91 		free(udev_ctx);
92 	}
93 }
94 
95 struct udev *
96 udev_new(void)
97 {
98 	struct udev *udev_ctx;
99 	int ret, s;
100 
101 	ret = conn_local_server(LISTEN_SOCKET_FILE, SOCK_STREAM, 0, &s);
102 	if (ret < 0)
103 		return NULL;
104 
105 	udev_ctx = malloc(sizeof(struct udev));
106 
107 	udev_ctx->refs = 1;
108 	udev_ctx->gp_fd = s;
109 	udev_ctx->monitor_fd = -1;
110 	udev_ctx->userdata = NULL;
111 
112 	return udev_ctx;
113 }
114 
115 const char *udev_get_dev_path(struct udev *udev_ctx __unused)
116 {
117 	return "/dev";
118 }
119 
120 void *
121 udev_get_userdata(struct udev *udev_ctx)
122 {
123 	return udev_ctx->userdata;
124 }
125 
126 void
127 udev_set_userdata(struct udev *udev_ctx, void *userdata)
128 {
129 	udev_ctx->userdata = userdata;
130 }
131 
132 int
133 udev_get_fd(struct udev *udev_ctx)
134 {
135 	return udev_ctx->gp_fd;
136 }
137 
138 int
139 send_xml(int s, char *xml)
140 {
141 	ssize_t r,n;
142 	size_t sz;
143 
144 	sz = strlen(xml) + 1;
145 
146 	r = send(s, &sz, sizeof(sz), 0);
147 	if (r <= 0)
148 		return r;
149 
150 	r = 0;
151 	while (r < (ssize_t)sz) {
152 		n = send(s, xml+r, sz-r, 0);
153 		if (n <= 0)
154 			return n;
155 		r += n;
156 	}
157 
158 	return r;
159 }
160 
161 int
162 read_xml(int s, char **buf)
163 {
164 	char *xml;
165 	size_t sz;
166 	int n, r;
167 
168 	*buf = NULL;
169 
170 	n = recv(s, &sz, sizeof(sz), MSG_WAITALL);
171 	if ((n <= 0) || (sz > 12*1024*1024)) /* Arbitrary limit */
172 		return n;
173 
174 	xml = malloc(sz+2);
175 	r = 0;
176 	while (r < (ssize_t)sz) {
177 		n = recv(s, xml+r, sz-r, MSG_WAITALL);
178 		if (n <= 0) {
179 			free(xml);
180 			return n;
181 		}
182 		r += n;
183 	}
184 
185 	*buf = xml;
186 	return r;
187 }
188 
189 int
190 _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str)
191 {
192 	prop_string_t	ps;
193 
194 	ps = prop_string_create_cstring(str);
195 	if (ps == NULL)
196 		return ENOMEM;
197 
198 	if (prop_dictionary_set(dict, key, ps) == false) {
199 		prop_object_release(ps);
200 		return ENOMEM;
201 	}
202 
203 	prop_object_release(ps);
204 	return 0;
205 }
206 
207 int
208 _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val)
209 {
210 	prop_number_t	pn;
211 
212 	pn = prop_number_create_integer(val);
213 	if (pn == NULL)
214 		return ENOMEM;
215 
216 	if (prop_dictionary_set(dict, key, pn) == false) {
217 		prop_object_release(pn);
218 		return ENOMEM;
219 	}
220 
221 	prop_object_release(pn);
222 	return 0;
223 }
224 
225 int
226 _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val)
227 {
228 	prop_number_t	pn;
229 
230 	pn = prop_number_create_unsigned_integer(val);
231 	if (pn == NULL)
232 		return ENOMEM;
233 
234 	if (prop_dictionary_set(dict, key, pn) == false) {
235 		prop_object_release(pn);
236 		return ENOMEM;
237 	}
238 
239 	prop_object_release(pn);
240 	return 0;
241 }
242 
243 int
244 conn_local_server(const char *sockfile, int socktype, int nonblock __unused,
245 		  int *retsock)
246 {
247 	int s;
248 	struct sockaddr_un serv_addr;
249 
250 	*retsock = -1;
251 	if ((s = socket(AF_UNIX, socktype, 0)) < 0)
252 		return -1;
253 
254 	memset(&serv_addr, 0, sizeof(serv_addr));
255 	serv_addr.sun_family = AF_UNIX;
256 	strncpy(serv_addr.sun_path, sockfile, SOCKFILE_NAMELEN);
257 	serv_addr.sun_path[SOCKFILE_NAMELEN - 1] = '\0';
258 
259 	*retsock = s;
260 	return connect(s, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
261 }
262 
263 prop_dictionary_t
264 udevd_get_command_dict(char *command)
265 {
266 	prop_dictionary_t	dict;
267 	int	error;
268 
269 	dict = prop_dictionary_create();
270 	if (dict == NULL)
271 		return NULL;
272 
273 	if ((error = _udev_dict_set_cstr(dict, "command", command)))
274 		goto error_out;
275 
276 	return dict;
277 
278 error_out:
279 	prop_object_release(dict);
280 	return NULL;
281 }
282 
283 prop_array_t
284 udevd_request_devs(int s, prop_array_t filters)
285 {
286 	prop_array_t	pa;
287 	prop_dictionary_t	dict;
288 	char *xml;
289 
290 	int n;
291 
292 	dict = udevd_get_command_dict(__DECONST(char *, "getdevs"));
293 	if (dict == NULL)
294 		return NULL;
295 
296 	/* Add filters to message, if available */
297 	if (filters != NULL) {
298 		if (prop_dictionary_set(dict, "filters", filters) == false) {
299 			prop_object_release(dict);
300 			return NULL;
301 		}
302 	}
303 
304 	xml = prop_dictionary_externalize(dict);
305 	prop_object_release(dict);
306 	if (xml == NULL)
307 		return NULL;
308 
309 	n = send_xml(s, xml);
310 	free(xml);
311 
312 	if (n <= 0)
313 		return NULL;
314 
315 	if ((n = read_xml(s, &xml)) <= 0)
316 		return NULL;
317 
318 	xml[n+1] = '\0';
319 	pa = prop_array_internalize(xml);
320 	free(xml);
321 	return (pa);
322 }
323