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/>.
30 #include <sys/socket.h>
34 #include "libudev-private.h"
44 * udev_monitor_new_from_socket:
45 * @udev: udev library context
46 * @socket_path: unix socket path
48 * Create new udev monitor, setup and connect to a specified socket. The
49 * path to a socket can point to an existing socket file, or it will be
50 * created if needed. If neccessary, the permissions adjustment as well as
51 * the later cleanup of the socket file, needs to be done by the caller.
52 * If the socket path starts with a '@' character, an abstract namespace
53 * socket will be used.
55 * The initial refcount is 1, and needs to be decremented to
56 * release the ressources of the udev monitor.
58 * Returns: a new udev monitor, or #NULL, in case of an error
60 struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path)
62 struct udev_monitor *udev_monitor;
63 struct sockaddr_un saddr;
69 if (socket_path == NULL)
71 udev_monitor = malloc(sizeof(struct udev_monitor));
72 if (udev_monitor == NULL)
74 memset(udev_monitor, 0x00, sizeof(struct udev_monitor));
75 udev_monitor->refcount = 1;
76 udev_monitor->udev = udev;
78 memset(&saddr, 0x00, sizeof(saddr));
79 saddr.sun_family = AF_LOCAL;
80 strcpy(saddr.sun_path, socket_path);
81 addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path);
83 /* translate leading '@' to abstract namespace */
84 if (saddr.sun_path[0] == '@')
85 saddr.sun_path[0] = '\0';
87 udev_monitor->socket = socket(AF_LOCAL, SOCK_DGRAM, 0);
88 if (udev_monitor->socket == -1) {
89 log_err(udev, "error getting socket: %s\n", strerror(errno));
94 if (bind(udev_monitor->socket, (struct sockaddr *) &saddr, addrlen) < 0) {
95 log_err(udev, "bind failed: %s\n", strerror(errno));
96 close(udev_monitor->socket);
101 /* enable receiving of the sender credentials */
102 setsockopt(udev_monitor->socket, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
103 log_info(udev_monitor->udev, "udev_monitor: %p created\n", udev_monitor);
110 * @udev_monitor: udev monitor
112 * Take a reference of a udev monitor.
114 * Returns: the passed udev monitor
116 struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
118 if (udev_monitor == NULL)
120 udev_monitor->refcount++;
125 * udev_monitor_unref:
126 * @udev_monitor: udev monitor
128 * Drop a reference of a udev monitor. If the refcount reaches zero,
129 * the bound socket will be closed, and the ressources of the monitor
133 void udev_monitor_unref(struct udev_monitor *udev_monitor)
135 if (udev_monitor == NULL)
137 udev_monitor->refcount--;
138 if (udev_monitor->refcount > 0)
140 close(udev_monitor->socket);
141 log_info(udev_monitor->udev, "udev_monitor: %p released\n", udev_monitor);
146 * udev_monitor_get_udev:
147 * @udev_monitor: udev monitor
149 * Retrieve the udev library context the monitor was created with.
151 * Returns: the udev library context
153 struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
155 if (udev_monitor == NULL)
157 return udev_monitor->udev;
161 * udev_monitor_get_fd:
162 * @udev_monitor: udev monitor
164 * Retrieve the socket file descriptor associated with the monitor.
166 * Returns: the socket file descriptor
168 int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
170 if (udev_monitor == NULL)
172 return udev_monitor->socket;
176 * udev_monitor_get_device:
177 * @udev_monitor: udev monitor
179 * Retrieve data from the udev monitor socket, allocate a new udev
180 * device, fill in the received data, and return the device.
182 * Only socket connections with uid=0 are accepted. The caller
183 * needs to make sure, that there is data to read from the socket,
184 * the call will block until the socket becomes readable.
186 * The initial refcount is 1, and needs to be decremented to
187 * release the ressources of the udev device.
189 * Returns: a new udev device, or #NULL, in case of an error
191 struct udev_device *udev_monitor_get_device(struct udev_monitor *udev_monitor)
193 struct udev_device *udev_device;
195 struct cmsghdr *cmsg;
198 char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
202 if (udev_monitor == NULL)
204 memset(buf, 0x00, sizeof(buf));
206 iov.iov_len = sizeof(buf);
207 memset (&smsg, 0x00, sizeof(struct msghdr));
210 smsg.msg_control = cred_msg;
211 smsg.msg_controllen = sizeof(cred_msg);
213 if (recvmsg(udev_monitor->socket, &smsg, 0) < 0) {
215 log_info(udev_monitor->udev, "unable to receive message");
218 cmsg = CMSG_FIRSTHDR(&smsg);
219 cred = (struct ucred *)CMSG_DATA (cmsg);
221 if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
222 log_info(udev_monitor->udev, "no sender credentials received, message ignored");
226 if (cred->uid != 0) {
227 log_info(udev_monitor->udev, "sender uid=%d, message ignored", cred->uid);
232 bufpos = strlen(buf) + 1;
233 if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) {
234 log_info(udev_monitor->udev, "invalid message length");
238 /* check message header */
239 if (strstr(buf, "@/") == NULL) {
240 log_info(udev_monitor->udev, "unrecognized message header");
244 udev_device = device_init(udev_monitor->udev);
245 if (udev_device == NULL) {
249 while (bufpos < sizeof(buf)) {
254 keylen = strlen(key);
257 bufpos += keylen + 1;
259 if (strncmp(key, "DEVPATH=", 8) == 0) {
260 udev_device->devpath = strdup(&key[8]);
261 } else if (strncmp(key, "SUBSYSTEM=", 10) == 0) {
262 udev_device->subsystem = strdup(&key[10]);
263 } else if (strncmp(key, "DEVNAME=", 8) == 0) {
264 udev_device->devname = strdup(&key[8]);
265 } else if (strncmp(key, "DEVLINKS=", 9) == 0) {
266 char *slink = &key[9];
267 char *next = strchr(slink, ' ');
269 while (next != NULL) {
271 name_list_add(&udev_device->link_list, slink, 0);
273 next = strchr(slink, ' ');
275 if (slink[0] != '\0')
276 name_list_add(&udev_device->link_list, slink, 0);
278 name_list_add(&udev_device->env_list, key, 0);