]> git.ipfire.org Git - thirdparty/systemd.git/blame - udev/lib/libudev-monitor.c
release 127
[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
43struct 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
91struct 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
99void 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
111struct 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
118int 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
125struct 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}