]>
Commit | Line | Data |
---|---|---|
d59f11e1 | 1 | /* |
55e9959b | 2 | * libudev - interface to udev device information |
d59f11e1 | 3 | * |
55e9959b | 4 | * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org> |
d59f11e1 | 5 | * |
4061ab9f KS |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
d59f11e1 KS |
10 | */ |
11 | ||
d59f11e1 KS |
12 | #include <errno.h> |
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <stddef.h> | |
16 | #include <string.h> | |
17 | #include <unistd.h> | |
18 | #include <sys/types.h> | |
ff2c503d | 19 | #include <sys/poll.h> |
d59f11e1 KS |
20 | #include <sys/socket.h> |
21 | #include <sys/un.h> | |
22 | ||
d59f11e1 KS |
23 | #include "libudev.h" |
24 | #include "libudev-private.h" | |
25 | ||
540f4669 | 26 | /* wire protocol magic must match */ |
b692a750 | 27 | #define UDEV_CTRL_MAGIC 0xdead1dea |
d59f11e1 KS |
28 | |
29 | enum udev_ctrl_msg_type { | |
30 | UDEV_CTRL_UNKNOWN, | |
31 | UDEV_CTRL_SET_LOG_LEVEL, | |
32 | UDEV_CTRL_STOP_EXEC_QUEUE, | |
33 | UDEV_CTRL_START_EXEC_QUEUE, | |
34 | UDEV_CTRL_RELOAD_RULES, | |
35 | UDEV_CTRL_SET_ENV, | |
87d55ff6 | 36 | UDEV_CTRL_SET_CHILDREN_MAX, |
ff2c503d KS |
37 | UDEV_CTRL_PING, |
38 | UDEV_CTRL_EXIT, | |
d59f11e1 KS |
39 | }; |
40 | ||
b692a750 KS |
41 | struct udev_ctrl_msg_wire { |
42 | char version[16]; | |
43 | unsigned int magic; | |
d59f11e1 KS |
44 | enum udev_ctrl_msg_type type; |
45 | union { | |
46 | int intval; | |
47 | char buf[256]; | |
48 | }; | |
49 | }; | |
50 | ||
51 | struct udev_ctrl_msg { | |
52 | int refcount; | |
ff2c503d | 53 | struct udev_ctrl_connection *conn; |
b692a750 | 54 | struct udev_ctrl_msg_wire ctrl_msg_wire; |
d59f11e1 KS |
55 | }; |
56 | ||
57 | struct udev_ctrl { | |
58 | int refcount; | |
59 | struct udev *udev; | |
60 | int sock; | |
61 | struct sockaddr_un saddr; | |
62 | socklen_t addrlen; | |
ff2c503d KS |
63 | bool connected; |
64 | }; | |
65 | ||
66 | struct udev_ctrl_connection { | |
67 | int refcount; | |
68 | struct udev_ctrl *uctrl; | |
69 | int sock; | |
d59f11e1 KS |
70 | }; |
71 | ||
fc1de713 | 72 | static struct udev_ctrl *udev_ctrl_new(struct udev *udev) |
d59f11e1 KS |
73 | { |
74 | struct udev_ctrl *uctrl; | |
75 | ||
b29a5e4a | 76 | uctrl = calloc(1, sizeof(struct udev_ctrl)); |
d59f11e1 KS |
77 | if (uctrl == NULL) |
78 | return NULL; | |
d59f11e1 KS |
79 | uctrl->refcount = 1; |
80 | uctrl->udev = udev; | |
fc1de713 KS |
81 | return uctrl; |
82 | } | |
83 | ||
84 | struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path) | |
85 | { | |
86 | struct udev_ctrl *uctrl; | |
87 | ||
88 | uctrl = udev_ctrl_new(udev); | |
89 | if (uctrl == NULL) | |
90 | return NULL; | |
d59f11e1 | 91 | |
ff2c503d | 92 | uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET, 0); |
d59f11e1 | 93 | if (uctrl->sock < 0) { |
659353f5 | 94 | err(udev, "error getting socket: %m\n"); |
d59f11e1 KS |
95 | udev_ctrl_unref(uctrl); |
96 | return NULL; | |
97 | } | |
98 | ||
99 | uctrl->saddr.sun_family = AF_LOCAL; | |
100 | strcpy(uctrl->saddr.sun_path, socket_path); | |
101 | uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path); | |
102 | /* translate leading '@' to abstract namespace */ | |
103 | if (uctrl->saddr.sun_path[0] == '@') | |
104 | uctrl->saddr.sun_path[0] = '\0'; | |
fc1de713 KS |
105 | return uctrl; |
106 | } | |
107 | ||
108 | struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) | |
109 | { | |
110 | struct udev_ctrl *uctrl; | |
111 | ||
112 | uctrl = udev_ctrl_new(udev); | |
113 | if (uctrl == NULL) | |
114 | return NULL; | |
115 | uctrl->sock = fd; | |
d59f11e1 KS |
116 | |
117 | return uctrl; | |
118 | } | |
119 | ||
120 | int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) | |
121 | { | |
122 | int err; | |
fc1de713 KS |
123 | |
124 | if (uctrl->addrlen > 0) { | |
125 | err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); | |
126 | if (err < 0) { | |
ff2c503d | 127 | err = -errno; |
fc1de713 KS |
128 | err(uctrl->udev, "bind failed: %m\n"); |
129 | return err; | |
130 | } | |
ff2c503d KS |
131 | err = listen(uctrl->sock, 0); |
132 | if (err < 0) { | |
133 | err = -errno; | |
134 | err(uctrl->udev, "listen failed: %m\n"); | |
135 | return err; | |
136 | } | |
d59f11e1 | 137 | } |
d59f11e1 KS |
138 | return 0; |
139 | } | |
140 | ||
141 | struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) | |
142 | { | |
143 | return uctrl->udev; | |
144 | } | |
145 | ||
146 | struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) | |
147 | { | |
148 | if (uctrl == NULL) | |
149 | return NULL; | |
150 | uctrl->refcount++; | |
151 | return uctrl; | |
152 | } | |
153 | ||
ff2c503d | 154 | struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) |
d59f11e1 KS |
155 | { |
156 | if (uctrl == NULL) | |
ff2c503d | 157 | return NULL; |
d59f11e1 KS |
158 | uctrl->refcount--; |
159 | if (uctrl->refcount > 0) | |
ff2c503d | 160 | return uctrl; |
d59f11e1 KS |
161 | if (uctrl->sock >= 0) |
162 | close(uctrl->sock); | |
163 | free(uctrl); | |
ff2c503d | 164 | return NULL; |
d59f11e1 KS |
165 | } |
166 | ||
167 | int udev_ctrl_get_fd(struct udev_ctrl *uctrl) | |
168 | { | |
169 | if (uctrl == NULL) | |
170 | return -1; | |
171 | return uctrl->sock; | |
172 | } | |
173 | ||
ff2c503d KS |
174 | struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) |
175 | { | |
176 | struct udev_ctrl_connection *conn; | |
177 | const int on = 1; | |
178 | ||
179 | conn = calloc(1, sizeof(struct udev_ctrl_connection)); | |
180 | if (conn == NULL) | |
181 | return NULL; | |
182 | conn->refcount = 1; | |
183 | conn->uctrl = uctrl; | |
184 | ||
185 | conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC); | |
186 | if (conn->sock < 0) { | |
187 | free(conn); | |
188 | return NULL; | |
189 | } | |
190 | ||
191 | /* enable receiving of the sender credentials */ | |
192 | setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); | |
193 | udev_ctrl_ref(uctrl); | |
194 | return conn; | |
195 | } | |
196 | ||
197 | struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn) | |
198 | { | |
199 | if (conn == NULL) | |
200 | return NULL; | |
201 | conn->refcount++; | |
202 | return conn; | |
203 | } | |
204 | ||
205 | struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) | |
206 | { | |
207 | if (conn == NULL) | |
208 | return NULL; | |
209 | conn->refcount--; | |
210 | if (conn->refcount > 0) | |
211 | return conn; | |
212 | if (conn->sock >= 0) | |
213 | close(conn->sock); | |
214 | udev_ctrl_unref(conn->uctrl); | |
215 | free(conn); | |
216 | return NULL; | |
217 | } | |
218 | ||
219 | static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) | |
d59f11e1 | 220 | { |
b692a750 | 221 | struct udev_ctrl_msg_wire ctrl_msg_wire; |
ff2c503d | 222 | int err = 0; |
d59f11e1 | 223 | |
b692a750 KS |
224 | memset(&ctrl_msg_wire, 0x00, sizeof(struct udev_ctrl_msg_wire)); |
225 | strcpy(ctrl_msg_wire.version, "udev-" VERSION); | |
226 | ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; | |
7b3a52f4 | 227 | ctrl_msg_wire.type = type; |
d59f11e1 KS |
228 | |
229 | if (buf != NULL) | |
065db052 | 230 | util_strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); |
d59f11e1 | 231 | else |
7b3a52f4 | 232 | ctrl_msg_wire.intval = intval; |
d59f11e1 | 233 | |
ff2c503d KS |
234 | if (!uctrl->connected) { |
235 | if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) { | |
236 | err = -errno; | |
237 | goto out; | |
238 | } | |
239 | uctrl->connected = true; | |
240 | } | |
241 | if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) { | |
242 | err = -errno; | |
243 | goto out; | |
d59f11e1 | 244 | } |
ff2c503d KS |
245 | |
246 | /* wait for peer message handling or disconnect */ | |
247 | for (;;) { | |
248 | struct pollfd pfd[1]; | |
249 | int r; | |
250 | ||
251 | pfd[0].fd = uctrl->sock; | |
252 | pfd[0].events = POLLIN; | |
253 | r = poll(pfd, 1, timeout * 1000); | |
254 | if (r < 0) { | |
255 | if (errno == EINTR) | |
256 | continue; | |
257 | err = -errno; | |
258 | break; | |
259 | } | |
260 | ||
261 | if (r > 0 && pfd[0].revents & POLLERR) { | |
262 | err = -EIO; | |
263 | break; | |
264 | } | |
265 | ||
266 | if (r == 0) | |
267 | err = -ETIMEDOUT; | |
268 | break; | |
269 | } | |
270 | out: | |
d59f11e1 KS |
271 | return err; |
272 | } | |
273 | ||
ff2c503d KS |
274 | int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout) |
275 | { | |
276 | return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); | |
277 | } | |
278 | ||
279 | int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) | |
d59f11e1 | 280 | { |
ff2c503d | 281 | return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); |
d59f11e1 KS |
282 | } |
283 | ||
ff2c503d | 284 | int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout) |
d59f11e1 | 285 | { |
ff2c503d | 286 | return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); |
d59f11e1 KS |
287 | } |
288 | ||
ff2c503d | 289 | int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl, int timeout) |
d59f11e1 | 290 | { |
ff2c503d | 291 | return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL, timeout); |
d59f11e1 KS |
292 | } |
293 | ||
ff2c503d | 294 | int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) |
d59f11e1 | 295 | { |
ff2c503d | 296 | return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); |
d59f11e1 KS |
297 | } |
298 | ||
ff2c503d | 299 | int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout) |
d59f11e1 | 300 | { |
ff2c503d | 301 | return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); |
d59f11e1 KS |
302 | } |
303 | ||
ff2c503d | 304 | int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) |
d59f11e1 | 305 | { |
ff2c503d | 306 | return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); |
d59f11e1 KS |
307 | } |
308 | ||
ff2c503d | 309 | int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) |
bb38678e | 310 | { |
ff2c503d | 311 | return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); |
bb38678e SJR |
312 | } |
313 | ||
ff2c503d | 314 | struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) |
d59f11e1 | 315 | { |
ff2c503d | 316 | struct udev *udev = conn->uctrl->udev; |
d59f11e1 KS |
317 | struct udev_ctrl_msg *uctrl_msg; |
318 | ssize_t size; | |
319 | struct msghdr smsg; | |
320 | struct cmsghdr *cmsg; | |
321 | struct iovec iov; | |
322 | struct ucred *cred; | |
323 | char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; | |
324 | ||
b29a5e4a | 325 | uctrl_msg = calloc(1, sizeof(struct udev_ctrl_msg)); |
d59f11e1 KS |
326 | if (uctrl_msg == NULL) |
327 | return NULL; | |
d59f11e1 | 328 | uctrl_msg->refcount = 1; |
ff2c503d | 329 | uctrl_msg->conn = conn; |
80707e9a | 330 | udev_ctrl_connection_ref(conn); |
d59f11e1 | 331 | |
7b3a52f4 | 332 | iov.iov_base = &uctrl_msg->ctrl_msg_wire; |
b692a750 | 333 | iov.iov_len = sizeof(struct udev_ctrl_msg_wire); |
d59f11e1 KS |
334 | memset(&smsg, 0x00, sizeof(struct msghdr)); |
335 | smsg.msg_iov = &iov; | |
336 | smsg.msg_iovlen = 1; | |
337 | smsg.msg_control = cred_msg; | |
338 | smsg.msg_controllen = sizeof(cred_msg); | |
ff2c503d | 339 | size = recvmsg(conn->sock, &smsg, 0); |
d59f11e1 | 340 | if (size < 0) { |
ff2c503d | 341 | err(udev, "unable to receive user udevd message: %m\n"); |
d59f11e1 KS |
342 | goto err; |
343 | } | |
344 | cmsg = CMSG_FIRSTHDR(&smsg); | |
345 | cred = (struct ucred *) CMSG_DATA(cmsg); | |
346 | ||
347 | if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { | |
ff2c503d | 348 | err(udev, "no sender credentials received, message ignored\n"); |
d59f11e1 KS |
349 | goto err; |
350 | } | |
351 | ||
352 | if (cred->uid != 0) { | |
ff2c503d | 353 | err(udev, "sender uid=%i, message ignored\n", cred->uid); |
d59f11e1 KS |
354 | goto err; |
355 | } | |
356 | ||
b692a750 | 357 | if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { |
ff2c503d | 358 | err(udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic); |
d59f11e1 KS |
359 | goto err; |
360 | } | |
361 | ||
ff2c503d | 362 | dbg(udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type); |
d59f11e1 KS |
363 | return uctrl_msg; |
364 | err: | |
365 | udev_ctrl_msg_unref(uctrl_msg); | |
366 | return NULL; | |
367 | } | |
368 | ||
369 | struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg) | |
370 | { | |
371 | if (ctrl_msg == NULL) | |
372 | return NULL; | |
373 | ctrl_msg->refcount++; | |
374 | return ctrl_msg; | |
375 | } | |
376 | ||
ff2c503d | 377 | struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) |
d59f11e1 KS |
378 | { |
379 | if (ctrl_msg == NULL) | |
ff2c503d | 380 | return NULL; |
d59f11e1 KS |
381 | ctrl_msg->refcount--; |
382 | if (ctrl_msg->refcount > 0) | |
ff2c503d KS |
383 | return ctrl_msg; |
384 | dbg(ctrl_msg->conn->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg); | |
385 | udev_ctrl_connection_unref(ctrl_msg->conn); | |
d59f11e1 | 386 | free(ctrl_msg); |
ff2c503d | 387 | return NULL; |
d59f11e1 KS |
388 | } |
389 | ||
390 | int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) | |
391 | { | |
7b3a52f4 KS |
392 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL) |
393 | return ctrl_msg->ctrl_msg_wire.intval; | |
d59f11e1 KS |
394 | return -1; |
395 | } | |
396 | ||
397 | int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) | |
398 | { | |
7b3a52f4 | 399 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) |
d59f11e1 KS |
400 | return 1; |
401 | return -1; | |
402 | } | |
403 | ||
404 | int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) | |
405 | { | |
7b3a52f4 | 406 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) |
d59f11e1 KS |
407 | return 1; |
408 | return -1; | |
409 | } | |
410 | ||
411 | int udev_ctrl_get_reload_rules(struct udev_ctrl_msg *ctrl_msg) | |
412 | { | |
7b3a52f4 | 413 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD_RULES) |
d59f11e1 KS |
414 | return 1; |
415 | return -1; | |
416 | } | |
417 | ||
418 | const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) | |
419 | { | |
7b3a52f4 KS |
420 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) |
421 | return ctrl_msg->ctrl_msg_wire.buf; | |
d59f11e1 KS |
422 | return NULL; |
423 | } | |
424 | ||
87d55ff6 | 425 | int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) |
d59f11e1 | 426 | { |
87d55ff6 | 427 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) |
7b3a52f4 | 428 | return ctrl_msg->ctrl_msg_wire.intval; |
d59f11e1 KS |
429 | return -1; |
430 | } | |
bb38678e | 431 | |
ff2c503d | 432 | int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) |
bb38678e | 433 | { |
ff2c503d KS |
434 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) |
435 | return 1; | |
436 | return -1; | |
437 | } | |
438 | ||
439 | int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) | |
440 | { | |
441 | if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) | |
442 | return 1; | |
bb38678e SJR |
443 | return -1; |
444 | } |