chiark / gitweb /
fbb21d04eaeb849c7c3c2f71814c203a79ca4f05
[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 struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path)
44 {
45         struct udev_monitor *udev_monitor;
46         struct sockaddr_un saddr;
47         socklen_t addrlen;
48         const int on = 1;
49
50         if (udev == NULL)
51                 return NULL;
52         if (socket_path == NULL)
53                 return NULL;
54         udev_monitor = malloc(sizeof(struct udev_monitor));
55         if (udev_monitor == NULL)
56                 return NULL;
57         memset(udev_monitor, 0x00, sizeof(struct udev_monitor));
58         udev_monitor->refcount = 1;
59         udev_monitor->udev = udev;
60
61         memset(&saddr, 0x00, sizeof(saddr));
62         saddr.sun_family = AF_LOCAL;
63         strcpy(saddr.sun_path, socket_path);
64         addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path);
65
66         /* translate leading '@' to abstract namespace */
67         if (saddr.sun_path[0] == '@')
68                 saddr.sun_path[0] = '\0';
69
70         udev_monitor->socket = socket(AF_LOCAL, SOCK_DGRAM, 0);
71         if (udev_monitor->socket == -1) {
72                 log_err(udev, "error getting socket: %s\n", strerror(errno));
73                 free(udev_monitor);
74                 return NULL;
75         }
76
77         if (bind(udev_monitor->socket, (struct sockaddr *) &saddr, addrlen) < 0) {
78                 log_err(udev, "bind failed: %s\n", strerror(errno));
79                 close(udev_monitor->socket);
80                 free(udev_monitor);
81                 return NULL;
82         }
83
84         /* enable receiving of the sender credentials */
85         setsockopt(udev_monitor->socket, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
86         log_info(udev_monitor->udev, "udev_monitor: %p created\n", udev_monitor);
87
88         return udev_monitor;
89 }
90
91 struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
92 {
93         if (udev_monitor == NULL)
94                 return NULL;
95         udev_monitor->refcount++;
96         return udev_monitor;
97 }
98
99 void udev_monitor_unref(struct udev_monitor *udev_monitor)
100 {
101         if (udev_monitor == NULL)
102                 return;
103         udev_monitor->refcount--;
104         if (udev_monitor->refcount > 0)
105                 return;
106         close(udev_monitor->socket);
107         log_info(udev_monitor->udev, "udev_monitor: %p released\n", udev_monitor);
108         free(udev_monitor);
109 }
110
111 struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
112 {
113         if (udev_monitor == NULL)
114                 return NULL;
115         return udev_monitor->udev;
116 }
117
118 int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
119 {
120         if (udev_monitor == NULL)
121                 return -1;
122         return udev_monitor->socket;
123 }
124
125 struct udev_device *udev_monitor_get_device(struct udev_monitor *udev_monitor)
126 {
127         struct udev_device *udev_device;
128         struct msghdr smsg;
129         struct cmsghdr *cmsg;
130         struct iovec iov;
131         struct ucred *cred;
132         char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
133         char buf[4096];
134         size_t bufpos;
135
136         if (udev_monitor == NULL)
137                 return NULL;
138         memset(buf, 0x00, sizeof(buf));
139         iov.iov_base = &buf;
140         iov.iov_len = sizeof(buf);
141         memset (&smsg, 0x00, sizeof(struct msghdr));
142         smsg.msg_iov = &iov;
143         smsg.msg_iovlen = 1;
144         smsg.msg_control = cred_msg;
145         smsg.msg_controllen = sizeof(cred_msg);
146
147         if (recvmsg(udev_monitor->socket, &smsg, 0) < 0) {
148                 if (errno != EINTR)
149                         log_info(udev_monitor->udev, "unable to receive message");
150                 return NULL;
151         }
152         cmsg = CMSG_FIRSTHDR(&smsg);
153         cred = (struct ucred *)CMSG_DATA (cmsg);
154
155         if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
156                 log_info(udev_monitor->udev, "no sender credentials received, message ignored");
157                 return NULL;
158         }
159
160         if (cred->uid != 0) {
161                 log_info(udev_monitor->udev, "sender uid=%d, message ignored", cred->uid);
162                 return NULL;
163         }
164
165         /* skip header */
166         bufpos = strlen(buf) + 1;
167         if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) {
168                 log_info(udev_monitor->udev, "invalid message length");
169                 return NULL;
170         }
171
172         /* check message header */
173         if (strstr(buf, "@/") == NULL) {
174                 log_info(udev_monitor->udev, "unrecognized message header");
175                 return NULL;
176         }
177
178         udev_device = device_init(udev_monitor->udev);
179         if (udev_device == NULL) {
180                 return NULL;
181         }
182
183         while (bufpos < sizeof(buf)) {
184                 char *key;
185                 size_t keylen;
186
187                 key = &buf[bufpos];
188                 keylen = strlen(key);
189                 if (keylen == 0)
190                         break;
191                 bufpos += keylen + 1;
192
193                 if (strncmp(key, "DEVPATH=", 8) == 0) {
194                         udev_device->devpath = strdup(&key[8]);
195                 } else if (strncmp(key, "SUBSYSTEM=", 10) == 0) {
196                         udev_device->subsystem = strdup(&key[10]);
197                 } else if (strncmp(key, "DEVNAME=", 8) == 0) {
198                         udev_device->devname = strdup(&key[8]);
199                 } else if (strncmp(key, "DEVLINKS=", 9) == 0) {
200                         char *slink = &key[9];
201                         char *next = strchr(slink, ' ');
202
203                         while (next != NULL) {
204                                 next[0] = '\0';
205                                 name_list_add(&udev_device->link_list, slink, 0);
206                                 slink = &next[1];
207                                 next = strchr(slink, ' ');
208                         }
209                         if (slink[0] != '\0')
210                                 name_list_add(&udev_device->link_list, slink, 0);
211                 }
212                 name_list_add(&udev_device->env_list, key, 0);
213         }
214
215         return udev_device;
216 }