]>
git.ipfire.org Git - thirdparty/systemd.git/blob - udev/lib/libudev-monitor.c
2 * libudev - interface to udev device information
4 * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/socket.h>
30 #include <linux/netlink.h>
33 #include "libudev-private.h"
39 struct sockaddr_nl snl
;
40 struct sockaddr_un sun
;
45 * udev_monitor_new_from_socket:
46 * @udev: udev library context
47 * @socket_path: unix socket path
49 * Create new udev monitor, setup and connect to a specified socket. The
50 * path to a socket can point to an existing socket file, or it will be
51 * created if needed. If neccessary, the permissions adjustment as well as
52 * the later cleanup of the socket file, needs to be done by the caller.
53 * If the socket path starts with a '@' character, an abstract namespace
54 * socket will be used.
56 * The initial refcount is 1, and needs to be decremented to
57 * release the ressources of the udev monitor.
59 * Returns: a new udev monitor, or #NULL, in case of an error
61 struct udev_monitor
*udev_monitor_new_from_socket(struct udev
*udev
, const char *socket_path
)
63 struct udev_monitor
*udev_monitor
;
67 if (socket_path
== NULL
)
69 udev_monitor
= malloc(sizeof(struct udev_monitor
));
70 if (udev_monitor
== NULL
)
72 memset(udev_monitor
, 0x00, sizeof(struct udev_monitor
));
73 udev_monitor
->refcount
= 1;
74 udev_monitor
->udev
= udev
;
76 udev_monitor
->sun
.sun_family
= AF_LOCAL
;
77 strcpy(udev_monitor
->sun
.sun_path
, socket_path
);
78 udev_monitor
->addrlen
= offsetof(struct sockaddr_un
, sun_path
) + strlen(udev_monitor
->sun
.sun_path
);
80 /* translate leading '@' to abstract namespace */
81 if (udev_monitor
->sun
.sun_path
[0] == '@')
82 udev_monitor
->sun
.sun_path
[0] = '\0';
84 udev_monitor
->sock
= socket(AF_LOCAL
, SOCK_DGRAM
, 0);
85 if (udev_monitor
->sock
== -1) {
86 err(udev
, "error getting socket: %m\n");
90 info(udev
, "monitor %p created with '%s'\n", udev_monitor
, socket_path
);
94 struct udev_monitor
*udev_monitor_new_from_netlink(struct udev
*udev
)
96 struct udev_monitor
*udev_monitor
;
100 udev_monitor
= malloc(sizeof(struct udev_monitor
));
101 if (udev_monitor
== NULL
)
103 memset(udev_monitor
, 0x00, sizeof(struct udev_monitor
));
104 udev_monitor
->refcount
= 1;
105 udev_monitor
->udev
= udev
;
107 udev_monitor
->sock
= socket(PF_NETLINK
, SOCK_DGRAM
, NETLINK_KOBJECT_UEVENT
);
108 if (udev_monitor
->sock
== -1) {
109 err(udev
, "error getting socket: %m\n");
114 udev_monitor
->snl
.nl_family
= AF_NETLINK
;
115 udev_monitor
->snl
.nl_pid
= getpid();
116 udev_monitor
->snl
.nl_groups
= 1;
118 info(udev
, "monitor %p created with NETLINK_KOBJECT_UEVENT\n", udev_monitor
);
122 int udev_monitor_enable_receiving(struct udev_monitor
*udev_monitor
)
127 if (udev_monitor
->snl
.nl_family
!= 0) {
128 err
= bind(udev_monitor
->sock
, (struct sockaddr
*)&udev_monitor
->snl
, sizeof(struct sockaddr_nl
));
130 err(udev_monitor
->udev
, "bind failed: %m\n");
133 info(udev_monitor
->udev
, "monitor %p listening on netlink\n", udev_monitor
);
134 } else if (udev_monitor
->sun
.sun_family
!= 0) {
135 err
= bind(udev_monitor
->sock
, (struct sockaddr
*)&udev_monitor
->sun
, udev_monitor
->addrlen
);
137 err(udev_monitor
->udev
, "bind failed: %m\n");
140 /* enable receiving of the sender credentials */
141 setsockopt(udev_monitor
->sock
, SOL_SOCKET
, SO_PASSCRED
, &on
, sizeof(on
));
142 info(udev_monitor
->udev
, "monitor %p listening on socket\n", udev_monitor
);
149 * @udev_monitor: udev monitor
151 * Take a reference of a udev monitor.
153 * Returns: the passed udev monitor
155 struct udev_monitor
*udev_monitor_ref(struct udev_monitor
*udev_monitor
)
157 if (udev_monitor
== NULL
)
159 udev_monitor
->refcount
++;
164 * udev_monitor_unref:
165 * @udev_monitor: udev monitor
167 * Drop a reference ofa udev monitor. If the refcount reaches zero,
168 * the bound socket will be closed, and the ressources of the monitor
172 void udev_monitor_unref(struct udev_monitor
*udev_monitor
)
174 if (udev_monitor
== NULL
)
176 udev_monitor
->refcount
--;
177 if (udev_monitor
->refcount
> 0)
179 if (udev_monitor
->sock
>= 0)
180 close(udev_monitor
->sock
);
181 info(udev_monitor
->udev
, "monitor %p released\n", udev_monitor
);
186 * udev_monitor_get_udev:
187 * @udev_monitor: udev monitor
189 * Retrieve the udev library context the monitor was created with.
191 * Returns: the udev library context
193 struct udev
*udev_monitor_get_udev(struct udev_monitor
*udev_monitor
)
195 if (udev_monitor
== NULL
)
197 return udev_monitor
->udev
;
201 * udev_monitor_get_fd:
202 * @udev_monitor: udev monitor
204 * Retrieve the socket file descriptor associated with the monitor.
206 * Returns: the socket file descriptor
208 int udev_monitor_get_fd(struct udev_monitor
*udev_monitor
)
210 if (udev_monitor
== NULL
)
212 return udev_monitor
->sock
;
216 * udev_monitor_receive_device:
217 * @udev_monitor: udev monitor
219 * Receive data from the udev monitor socket, allocate a new udev
220 * device, fill in the received data, and return the device.
222 * Only socket connections with uid=0 are accepted. The caller
223 * needs to make sure, that there is data to read from the socket,
224 * the call will block until the socket becomes readable.
226 * The initial refcount is 1, and needs to be decremented to
227 * release the ressources of the udev device.
229 * Returns: a new udev device, or #NULL, in case of an error
231 struct udev_device
*udev_monitor_receive_device(struct udev_monitor
*udev_monitor
)
233 struct udev_device
*udev_device
;
236 char cred_msg
[CMSG_SPACE(sizeof(struct ucred
))];
242 if (udev_monitor
== NULL
)
244 memset(buf
, 0x00, sizeof(buf
));
246 iov
.iov_len
= sizeof(buf
);
247 memset (&smsg
, 0x00, sizeof(struct msghdr
));
250 smsg
.msg_control
= cred_msg
;
251 smsg
.msg_controllen
= sizeof(cred_msg
);
253 if (recvmsg(udev_monitor
->sock
, &smsg
, 0) < 0) {
255 info(udev_monitor
->udev
, "unable to receive message");
259 if (udev_monitor
->sun
.sun_family
!= 0) {
260 struct cmsghdr
*cmsg
= CMSG_FIRSTHDR(&smsg
);
261 struct ucred
*cred
= (struct ucred
*)CMSG_DATA (cmsg
);
263 if (cmsg
== NULL
|| cmsg
->cmsg_type
!= SCM_CREDENTIALS
) {
264 info(udev_monitor
->udev
, "no sender credentials received, message ignored");
268 if (cred
->uid
!= 0) {
269 info(udev_monitor
->udev
, "sender uid=%d, message ignored", cred
->uid
);
275 bufpos
= strlen(buf
) + 1;
276 if (bufpos
< sizeof("a@/d") || bufpos
>= sizeof(buf
)) {
277 info(udev_monitor
->udev
, "invalid message length");
281 /* check message header */
282 if (strstr(buf
, "@/") == NULL
) {
283 info(udev_monitor
->udev
, "unrecognized message header");
287 udev_device
= device_new(udev_monitor
->udev
);
288 if (udev_device
== NULL
) {
292 while (bufpos
< sizeof(buf
)) {
297 keylen
= strlen(key
);
300 bufpos
+= keylen
+ 1;
302 if (strncmp(key
, "DEVPATH=", 8) == 0) {
303 char path
[UTIL_PATH_SIZE
];
305 util_strlcpy(path
, udev_get_sys_path(udev_monitor
->udev
), sizeof(path
));
306 util_strlcat(path
, &key
[8], sizeof(path
));
307 device_set_syspath(udev_device
, path
);
308 } else if (strncmp(key
, "SUBSYSTEM=", 10) == 0) {
309 device_set_subsystem(udev_device
, &key
[10]);
310 } else if (strncmp(key
, "DEVNAME=", 8) == 0) {
311 device_set_devnode(udev_device
, &key
[8]);
312 } else if (strncmp(key
, "DEVLINKS=", 9) == 0) {
313 char *slink
= &key
[9];
314 char *next
= strchr(slink
, ' ');
316 while (next
!= NULL
) {
318 device_add_devlink(udev_device
, slink
);
320 next
= strchr(slink
, ' ');
322 if (slink
[0] != '\0')
323 device_add_devlink(udev_device
, slink
);
324 } else if (strncmp(key
, "DRIVER=", 7) == 0) {
325 device_set_driver(udev_device
, &key
[7]);
326 } else if (strncmp(key
, "ACTION=", 7) == 0) {
327 device_set_action(udev_device
, &key
[7]);
328 } else if (strncmp(key
, "MAJOR=", 6) == 0) {
329 maj
= strtoull(&key
[6], NULL
, 10);
330 } else if (strncmp(key
, "MINOR=", 6) == 0) {
331 min
= strtoull(&key
[6], NULL
, 10);
332 } else if (strncmp(key
, "DEVPATH_OLD=", 12) == 0) {
333 device_set_devpath_old(udev_device
, &key
[12]);
334 } else if (strncmp(key
, "PHYSDEVPATH=", 12) == 0) {
335 device_set_physdevpath(udev_device
, &key
[12]);
336 } else if (strncmp(key
, "SEQNUM=", 7) == 0) {
337 device_set_seqnum(udev_device
, strtoull(&key
[7], NULL
, 10));
338 } else if (strncmp(key
, "TIMEOUT=", 8) == 0) {
339 device_set_timeout(udev_device
, strtoull(&key
[8], NULL
, 10));
341 if (strncmp(key
, "PHYSDEV", 7) == 0)
343 device_add_property_from_string(udev_device
, key
);
345 device_set_devnum(udev_device
, makedev(maj
, min
));
347 device_set_info_loaded(udev_device
);