]> git.ipfire.org Git - thirdparty/systemd.git/blame - udev/lib/libudev-monitor.c
remove FAQ
[thirdparty/systemd.git] / udev / lib / libudev-monitor.c
CommitLineData
ba6929f6
KS
1/*
2 * libudev - interface to udev device information
3 *
4 * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
20#include "config.h"
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <stddef.h>
25#include <unistd.h>
26#include <errno.h>
27#include <string.h>
28#include <dirent.h>
29#include <sys/stat.h>
30#include <sys/socket.h>
31#include <sys/un.h>
32
33#include "libudev.h"
34#include "libudev-private.h"
35#include "../udev.h"
36
37struct udev_monitor {
38 struct udev *udev;
39 int refcount;
40 int socket;
41};
42
7d8787b3
KS
43/**
44 * udev_monitor_new_from_socket:
45 * @udev: udev library context
46 * @socket_path: unix socket path
47 *
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.
54 *
55 * The initial refcount is 1, and needs to be decremented to
56 * release the ressources of the udev monitor.
57 *
58 * Returns: a new udev monitor, or #NULL, in case of an error
59 **/
ba6929f6
KS
60struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path)
61{
62 struct udev_monitor *udev_monitor;
63 struct sockaddr_un saddr;
64 socklen_t addrlen;
65 const int on = 1;
66
67 if (udev == NULL)
68 return NULL;
69 if (socket_path == NULL)
70 return NULL;
71 udev_monitor = malloc(sizeof(struct udev_monitor));
72 if (udev_monitor == NULL)
73 return NULL;
74 memset(udev_monitor, 0x00, sizeof(struct udev_monitor));
75 udev_monitor->refcount = 1;
76 udev_monitor->udev = udev;
77
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);
82
83 /* translate leading '@' to abstract namespace */
84 if (saddr.sun_path[0] == '@')
85 saddr.sun_path[0] = '\0';
86
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));
90 free(udev_monitor);
91 return NULL;
92 }
93
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);
97 free(udev_monitor);
98 return NULL;
99 }
100
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);
104
105 return udev_monitor;
106}
107
7d8787b3
KS
108/**
109 * udev_monitor_ref:
110 * @udev_monitor: udev monitor
111 *
112 * Take a reference of a udev monitor.
113 *
114 * Returns: the passed udev monitor
115 **/
ba6929f6
KS
116struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
117{
118 if (udev_monitor == NULL)
119 return NULL;
120 udev_monitor->refcount++;
121 return udev_monitor;
122}
123
7d8787b3
KS
124/**
125 * udev_monitor_unref:
126 * @udev_monitor: udev monitor
127 *
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
130 * will be released.
131 *
132 **/
ba6929f6
KS
133void udev_monitor_unref(struct udev_monitor *udev_monitor)
134{
135 if (udev_monitor == NULL)
136 return;
137 udev_monitor->refcount--;
138 if (udev_monitor->refcount > 0)
139 return;
140 close(udev_monitor->socket);
141 log_info(udev_monitor->udev, "udev_monitor: %p released\n", udev_monitor);
142 free(udev_monitor);
143}
144
7d8787b3
KS
145/**
146 * udev_monitor_get_udev:
147 * @udev_monitor: udev monitor
148 *
149 * Retrieve the udev library context the device was created with.
150 *
151 * Returns: the udev library context
152 **/
ba6929f6
KS
153struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
154{
155 if (udev_monitor == NULL)
156 return NULL;
157 return udev_monitor->udev;
158}
159
7d8787b3
KS
160/**
161 * udev_monitor_get_fd:
162 * @udev_monitor: udev monitor
163 *
164 * Retrieve the socket file descriptor associated with the monitor.
165 *
166 * Returns: the socket file descriptor
167 **/
ba6929f6
KS
168int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
169{
170 if (udev_monitor == NULL)
171 return -1;
172 return udev_monitor->socket;
173}
174
7d8787b3
KS
175/**
176 * udev_monitor_get_device:
177 * @udev_monitor: udev monitor
178 *
179 * Retrieve data from the udev monitor socket, allocate a new udev
180 * device, and fill in the received data, and return the device.
181 *
182 * Only socket connections with uid=0 are accepted. The caller
183 * need to make sure, that there is data to read from the socket,
184 * the call will block until the socket becomes readable.
185 *
186 * The initial refcount is 1, and needs to be decremented to
187 * release the ressources of the udev device.
188 *
189 * Returns: a new udev device, or #NULL, in case of an error
190 **/
ba6929f6
KS
191struct udev_device *udev_monitor_get_device(struct udev_monitor *udev_monitor)
192{
193 struct udev_device *udev_device;
194 struct msghdr smsg;
195 struct cmsghdr *cmsg;
196 struct iovec iov;
197 struct ucred *cred;
198 char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
199 char buf[4096];
200 size_t bufpos;
201
202 if (udev_monitor == NULL)
203 return NULL;
204 memset(buf, 0x00, sizeof(buf));
205 iov.iov_base = &buf;
206 iov.iov_len = sizeof(buf);
207 memset (&smsg, 0x00, sizeof(struct msghdr));
208 smsg.msg_iov = &iov;
209 smsg.msg_iovlen = 1;
210 smsg.msg_control = cred_msg;
211 smsg.msg_controllen = sizeof(cred_msg);
212
213 if (recvmsg(udev_monitor->socket, &smsg, 0) < 0) {
214 if (errno != EINTR)
215 log_info(udev_monitor->udev, "unable to receive message");
216 return NULL;
217 }
218 cmsg = CMSG_FIRSTHDR(&smsg);
219 cred = (struct ucred *)CMSG_DATA (cmsg);
220
221 if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
222 log_info(udev_monitor->udev, "no sender credentials received, message ignored");
223 return NULL;
224 }
225
226 if (cred->uid != 0) {
227 log_info(udev_monitor->udev, "sender uid=%d, message ignored", cred->uid);
228 return NULL;
229 }
230
231 /* skip header */
232 bufpos = strlen(buf) + 1;
233 if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) {
234 log_info(udev_monitor->udev, "invalid message length");
235 return NULL;
236 }
237
238 /* check message header */
239 if (strstr(buf, "@/") == NULL) {
240 log_info(udev_monitor->udev, "unrecognized message header");
241 return NULL;
242 }
243
244 udev_device = device_init(udev_monitor->udev);
245 if (udev_device == NULL) {
246 return NULL;
247 }
248
249 while (bufpos < sizeof(buf)) {
250 char *key;
251 size_t keylen;
252
253 key = &buf[bufpos];
254 keylen = strlen(key);
255 if (keylen == 0)
256 break;
257 bufpos += keylen + 1;
258
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, ' ');
268
269 while (next != NULL) {
270 next[0] = '\0';
271 name_list_add(&udev_device->link_list, slink, 0);
272 slink = &next[1];
273 next = strchr(slink, ' ');
274 }
275 if (slink[0] != '\0')
276 name_list_add(&udev_device->link_list, slink, 0);
277 }
278 name_list_add(&udev_device->env_list, key, 0);
279 }
280
281 return udev_device;
282}