chiark / gitweb /
libudev: add monitor documentation
[elogind.git] / udev / lib / libudev-monitor.c
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
37 struct udev_monitor {
38         struct udev *udev;
39         int refcount;
40         int socket;
41 };
42
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  **/
60 struct 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
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  **/
116 struct 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
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  **/
133 void 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
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  **/
153 struct 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
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  **/
168 int 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
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  **/
191 struct 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 }