aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKay Sievers <kay.sievers@suse.de>2005-06-05 04:38:10 +0200
committerKay Sievers <kay.sievers@suse.de>2005-06-05 04:38:10 +0200
commit88f4b6485e2a551de55fd9d39aeef89debf3121c (patch)
treeaf4ad7d85ea983d4e4f19d4bdfc2e0073e2a4b80
parent5e65ab9a191268fec7cddf6b7d8c0fefd2a6b920 (diff)
downloadudev-88f4b6485e2a551de55fd9d39aeef89debf3121c.tar.gz
udevd: listen for netlink events
After the first valid netlink-event all event with a serial number received on the udevsend socket will be ignored. Signed-off-by: Kay Sievers <kay.sievers@suse.de>
-rw-r--r--udev.c10
-rw-r--r--udevd.c186
2 files changed, 160 insertions, 36 deletions
diff --git a/udev.c b/udev.c
index ce9b42f3..2be4c357 100644
--- a/udev.c
+++ b/udev.c
@@ -54,8 +54,10 @@ void log_message(int priority, const char *format, ...)
}
#endif
-/* decide if we should manage the whole hotplug event
- * for now look if the kernel calls udevsend instead of /sbin/hotplug
+/* Decide if we should manage the whole uevent, including multiplexing
+ * of the hotplug directories.
+ * For now look if the kernel calls udevsend instead of /sbin/hotplug,
+ * or the uevent-helper in /proc/sys/kernel/hotplug is empty.
*/
static int manage_hotplug_event(void) {
char helper[256];
@@ -70,13 +72,15 @@ static int manage_hotplug_event(void) {
if (fd < 0)
return 0;
- len = read(fd, helper, 256);
+ len = read(fd, helper, sizeof(helper)-1);
close(fd);
if (len < 0)
return 0;
helper[len] = '\0';
+ if (helper[0] == '\0' || helper[0] == '\n')
+ return 1;
if (strstr(helper, "udevsend"))
return 1;
diff --git a/udevd.c b/udevd.c
index 91033b06..68e3a75f 100644
--- a/udevd.c
+++ b/udevd.c
@@ -38,6 +38,7 @@
#include <sys/un.h>
#include <sys/sysinfo.h>
#include <sys/stat.h>
+#include <linux/netlink.h>
#include "list.h"
#include "udev_libc_wrapper.h"
@@ -49,6 +50,7 @@
/* global variables*/
static int udevsendsock;
+static int ueventsock;
static pid_t sid;
static int pipefds[2];
@@ -126,7 +128,8 @@ static void msg_queue_insert(struct hotplug_msg *msg)
break;
if (loop_msg->seqnum == msg->seqnum) {
- info("ignoring duplicate message seq %llu", msg->seqnum);
+ dbg("ignoring duplicate message seq %llu", msg->seqnum);
+ free(msg);
return;
}
}
@@ -154,6 +157,8 @@ static void execute_udev(struct hotplug_msg *msg)
switch (pid) {
case 0:
/* child */
+ if (ueventsock != -1)
+ close(ueventsock);
close(udevsendsock);
logging_close();
@@ -416,13 +421,60 @@ recheck:
}
}
+static struct hotplug_msg *get_msg_from_envbuf(const char *buf, int buf_size)
+{
+ int bufpos;
+ int i;
+ struct hotplug_msg *msg;
+
+ msg = malloc(sizeof(struct hotplug_msg) + buf_size);
+ if (msg == NULL)
+ return NULL;
+ memset(msg, 0x00, sizeof(struct hotplug_msg) + buf_size);
+
+ /* copy environment buffer and reconstruct envp */
+ memcpy(msg->envbuf, buf, buf_size);
+ bufpos = 0;
+ for (i = 0; (bufpos < buf_size) && (i < HOTPLUG_NUM_ENVP-2); i++) {
+ int keylen;
+ char *key;
+
+ key = &msg->envbuf[bufpos];
+ keylen = strlen(key);
+ msg->envp[i] = key;
+ bufpos += keylen + 1;
+ dbg("add '%s' to msg.envp[%i]", msg->envp[i], i);
+
+ /* remember some keys for further processing */
+ if (strncmp(key, "ACTION=", 7) == 0)
+ msg->action = &key[7];
+
+ if (strncmp(key, "DEVPATH=", 8) == 0)
+ msg->devpath = &key[8];
+
+ if (strncmp(key, "SUBSYSTEM=", 10) == 0)
+ msg->subsystem = &key[10];
+
+ if (strncmp(key, "SEQNUM=", 7) == 0)
+ msg->seqnum = strtoull(&key[7], NULL, 10);
+
+ if (strncmp(key, "PHYSDEVPATH=", 12) == 0)
+ msg->physdevpath = &key[12];
+
+ if (strncmp(key, "TIMEOUT=", 8) == 0)
+ msg->timeout = strtoull(&key[8], NULL, 10);
+ }
+ msg->envp[i++] = "UDEVD_EVENT=1";
+ msg->envp[i] = NULL;
+
+ return msg;
+}
+
/* receive the udevsend message and do some sanity checks */
static struct hotplug_msg *get_udevsend_msg(void)
{
static struct udevsend_msg usend_msg;
struct hotplug_msg *msg;
- int bufpos;
- int i;
ssize_t size;
struct msghdr smsg;
struct cmsghdr *cmsg;
@@ -467,46 +519,60 @@ static struct hotplug_msg *get_udevsend_msg(void)
envbuf_size = size - offsetof(struct udevsend_msg, envbuf);
dbg("envbuf_size=%i", envbuf_size);
- msg = malloc(sizeof(struct hotplug_msg) + envbuf_size);
+ msg = get_msg_from_envbuf(usend_msg.envbuf, envbuf_size);
if (msg == NULL)
return NULL;
- memset(msg, 0x00, sizeof(struct hotplug_msg) + envbuf_size);
-
- /* copy environment buffer and reconstruct envp */
- memcpy(msg->envbuf, usend_msg.envbuf, envbuf_size);
- bufpos = 0;
- for (i = 0; (bufpos < envbuf_size) && (i < HOTPLUG_NUM_ENVP-2); i++) {
- int keylen;
- char *key;
+ return msg;
+}
- key = &msg->envbuf[bufpos];
- keylen = strlen(key);
- msg->envp[i] = key;
- bufpos += keylen + 1;
- dbg("add '%s' to msg.envp[%i]", msg->envp[i], i);
+/* receive the kernel user event message and do some sanity checks */
+static struct hotplug_msg *get_uevent_msg(void)
+{
+ struct hotplug_msg *msg;
+ int bufpos;
+ ssize_t size;
+ static char buffer[HOTPLUG_BUFFER_SIZE + 512];
+ char *pos;
- /* remember some keys for further processing */
- if (strncmp(key, "ACTION=", 7) == 0)
- msg->action = &key[7];
+ size = recv(ueventsock, &buffer, sizeof(buffer), 0);
+ if (size < 0) {
+ if (errno != EINTR)
+ dbg("unable to receive udevsend message");
+ return NULL;
+ }
- if (strncmp(key, "DEVPATH=", 8) == 0)
- msg->devpath = &key[8];
+ if ((size_t)size > sizeof(buffer)-1)
+ size = sizeof(buffer)-1;
+ buffer[size] = '\0';
+ dbg("uevent_size=%i", size);
- if (strncmp(key, "SUBSYSTEM=", 10) == 0)
- msg->subsystem = &key[10];
+ /* start of event payload */
+ bufpos = strlen(buffer)+1;
+ msg = get_msg_from_envbuf(&buffer[bufpos], size-bufpos);
+ if (msg == NULL)
+ return NULL;
- if (strncmp(key, "SEQNUM=", 7) == 0)
- msg->seqnum = strtoull(&key[7], NULL, 10);
+ /* validate message */
+ pos = strchr(buffer, '@');
+ if (pos == NULL) {
+ dbg("invalid uevent '%s'", buffer);
+ free(msg);
+ return NULL;
+ }
+ pos[0] = '\0';
- if (strncmp(key, "PHYSDEVPATH=", 12) == 0)
- msg->physdevpath = &key[12];
+ if (msg->action == NULL) {
+ dbg("no ACTION in payload found, skip event '%s'", buffer);
+ free(msg);
+ return NULL;
+ }
- if (strncmp(key, "TIMEOUT=", 8) == 0)
- msg->timeout = strtoull(&key[8], NULL, 10);
+ if (strcmp(msg->action, buffer) != 0) {
+ dbg("ACTION in payload does not match uevent, skip event '%s'", buffer);
+ free(msg);
+ return NULL;
}
- msg->envp[i++] = "UDEVD_EVENT=1";
- msg->envp[i] = NULL;
return msg;
}
@@ -622,6 +688,34 @@ static int init_udevsend_socket(void)
return 0;
}
+static int init_uevent_socket(void)
+{
+ struct sockaddr_nl snl;
+ int retval;
+
+ memset(&snl, 0x00, sizeof(struct sockaddr_nl));
+ snl.nl_family = AF_NETLINK;
+ snl.nl_pid = getpid();
+ snl.nl_groups = 0xffffffff;
+
+ ueventsock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if (ueventsock == -1) {
+ dbg("error getting socket, %s", strerror(errno));
+ return -1;
+ }
+
+ retval = bind(ueventsock, (struct sockaddr *) &snl,
+ sizeof(struct sockaddr_nl));
+ if (retval < 0) {
+ dbg("bind failed, %s", strerror(errno));
+ close(ueventsock);
+ ueventsock = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
int main(int argc, char *argv[], char *envp[])
{
struct sysinfo info;
@@ -631,6 +725,7 @@ int main(int argc, char *argv[], char *envp[])
struct sigaction act;
fd_set readfds;
const char *udevd_expected_seqnum;
+ int uevent_active = 0;
logging_init("udevd");
udev_init_config();
@@ -716,6 +811,10 @@ int main(int argc, char *argv[], char *envp[])
sigaction(SIGALRM, &act, NULL);
sigaction(SIGCHLD, &act, NULL);
+ if (init_uevent_socket() < 0) {
+ dbg("uevent socket not available");
+ }
+
if (init_udevsend_socket() < 0) {
if (errno == EADDRINUSE)
dbg("another udevd running, exit");
@@ -745,6 +844,8 @@ int main(int argc, char *argv[], char *envp[])
FD_ZERO(&readfds);
FD_SET(udevsendsock, &readfds);
+ if (ueventsock != -1)
+ FD_SET(ueventsock, &readfds);
FD_SET(pipefds[0], &readfds);
maxsockplus = udevsendsock+1;
while (1) {
@@ -761,8 +862,27 @@ int main(int argc, char *argv[], char *envp[])
if (FD_ISSET(udevsendsock, &workreadfds)) {
msg = get_udevsend_msg();
- if (msg)
+ if (msg) {
+ /* discard kernel messages if netlink is active */
+ if (uevent_active && msg->seqnum != 0) {
+ dbg("skip kernel udevsend message, netlink is active");
+ free(msg);
+ continue;
+ }
+ msg_queue_insert(msg);
+ }
+ }
+
+ if (FD_ISSET(ueventsock, &workreadfds)) {
+ msg = get_uevent_msg();
+ if (msg) {
msg_queue_insert(msg);
+ /* disable udevsend with first netlink message */
+ if (!uevent_active) {
+ info("netlink message received, disable kernel udevsend messages");
+ uevent_active = 1;
+ }
+ }
}
if (FD_ISSET(pipefds[0], &workreadfds))