X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibudev%2Flibudev-monitor.c;h=e07e462af3bb861cfeb563543d66a0b441996269;hp=4406b55f97d625a44844518e1cbad9f83d0681de;hb=ec76ec1fde7da753676b18ff45926740fcd08072;hpb=bb061708d5aa83579f213bdfb67253f7027217c3 diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c index 4406b55f9..e07e462af 100644 --- a/src/libudev/libudev-monitor.c +++ b/src/libudev/libudev-monitor.c @@ -1,13 +1,21 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ +/*** + This file is part of systemd. + + Copyright 2008-2012 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ #include #include @@ -97,21 +105,6 @@ static struct udev_monitor *udev_monitor_new(struct udev *udev) return udev_monitor; } -/** - * udev_monitor_new_from_socket: - * @udev: udev library context - * @socket_path: unix socket path - * - * This function is removed from libudev and will not do anything. - * - * Returns: #NULL - **/ -_public_ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path) -{ - errno = ENOSYS; - return NULL; -} - struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd) { struct udev_monitor *udev_monitor; @@ -122,13 +115,21 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c if (name == NULL) group = UDEV_MONITOR_NONE; - else if (strcmp(name, "udev") == 0) + else if (streq(name, "udev")) group = UDEV_MONITOR_UDEV; - else if (strcmp(name, "kernel") == 0) + else if (streq(name, "kernel")) group = UDEV_MONITOR_KERNEL; else return NULL; + /* + * We do not support subscribing to uevents if no instance of udev + * is running. Uevents would otherwise broadcast the processing data + * of the host into containers, which is not acceptable. Containers + * will currently just not get any uevents. + */ + + udev_monitor = udev_monitor_new(udev); if (udev_monitor == NULL) return NULL; @@ -136,7 +137,7 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c if (fd < 0) { udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT); if (udev_monitor->sock == -1) { - err(udev, "error getting socket: %m\n"); + udev_err(udev, "error getting socket: %m\n"); free(udev_monitor); return NULL; } @@ -294,8 +295,8 @@ _public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor) /* matched, pass packet */ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); - if (i+1 >= ARRAY_SIZE(ins)) - return -1; + if (i+1 >= ELEMENTSOF(ins)) + return -E2BIG; } /* nothing matched, drop packet */ @@ -310,7 +311,7 @@ _public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor) filter.len = i; filter.filter = ins; err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); - return err; + return err < 0 ? -errno : 0; } int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender) @@ -331,9 +332,6 @@ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) int err = 0; const int on = 1; - if (udev_monitor->snl.nl.nl_family == 0) - return -EINVAL; - udev_monitor_filter_update(udev_monitor); if (!udev_monitor->bound) { @@ -356,8 +354,8 @@ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) if (err == 0) udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid; } else { - err(udev_monitor->udev, "bind failed: %m\n"); - return err; + udev_err(udev_monitor->udev, "bind failed: %m\n"); + return -errno; } /* enable receiving of sender credentials */ @@ -378,7 +376,7 @@ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) _public_ int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) { if (udev_monitor == NULL) - return -1; + return -EINVAL; return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)); } @@ -388,7 +386,7 @@ int udev_monitor_disconnect(struct udev_monitor *udev_monitor) err = close(udev_monitor->sock); udev_monitor->sock = -1; - return err; + return err < 0 ? -errno : 0; } /** @@ -415,19 +413,21 @@ _public_ struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor * the bound socket will be closed, and the resources of the monitor * will be released. * + * Returns: #NULL **/ -_public_ void udev_monitor_unref(struct udev_monitor *udev_monitor) +_public_ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) { if (udev_monitor == NULL) - return; + return NULL; udev_monitor->refcount--; if (udev_monitor->refcount > 0) - return; + return NULL; if (udev_monitor->sock >= 0) close(udev_monitor->sock); udev_list_cleanup(&udev_monitor->filter_subsystem_list); udev_list_cleanup(&udev_monitor->filter_tag_list); free(udev_monitor); + return NULL; } /** @@ -456,7 +456,7 @@ _public_ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) _public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor) { if (udev_monitor == NULL) - return -1; + return -EINVAL; return udev_monitor->sock; } @@ -472,7 +472,7 @@ static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device * const char *devtype; const char *ddevtype; - if (strcmp(dsubsys, subsys) != 0) + if (!streq(dsubsys, subsys)) continue; devtype = udev_list_entry_get_value(list_entry); @@ -481,7 +481,7 @@ static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device * ddevtype = udev_device_get_devtype(udev_device); if (ddevtype == NULL) continue; - if (strcmp(ddevtype, devtype) == 0) + if (streq(ddevtype, devtype)) goto tag; } return 0; @@ -507,6 +507,11 @@ tag: * * Only socket connections with uid=0 are accepted. * + * The monitor socket is by default set to NONBLOCK. A variant of poll() on + * the file descriptor returned by udev_monitor_get_fd() should to be used to + * wake up when new devices arrive, or alternatively the file descriptor + * switched into blocking mode. + * * The initial refcount is 1, and needs to be decremented to * release the resources of the udev device. * @@ -524,7 +529,6 @@ _public_ struct udev_device *udev_monitor_receive_device(struct udev_monitor *ud char buf[8192]; ssize_t buflen; ssize_t bufpos; - struct udev_monitor_netlink_header *nlh; retry: if (udev_monitor == NULL) @@ -536,82 +540,89 @@ retry: smsg.msg_iovlen = 1; smsg.msg_control = cred_msg; smsg.msg_controllen = sizeof(cred_msg); - - if (udev_monitor->snl.nl.nl_family != 0) { - smsg.msg_name = &snl; - smsg.msg_namelen = sizeof(snl); - } + smsg.msg_name = &snl; + smsg.msg_namelen = sizeof(snl); buflen = recvmsg(udev_monitor->sock, &smsg, 0); if (buflen < 0) { if (errno != EINTR) - dbg(udev_monitor->udev, "unable to receive message\n"); + udev_dbg(udev_monitor->udev, "unable to receive message\n"); return NULL; } if (buflen < 32 || (size_t)buflen >= sizeof(buf)) { - dbg(udev_monitor->udev, "invalid message length\n"); + udev_dbg(udev_monitor->udev, "invalid message length\n"); return NULL; } - if (udev_monitor->snl.nl.nl_family != 0) { - if (snl.nl.nl_groups == 0) { - /* unicast message, check if we trust the sender */ - if (udev_monitor->snl_trusted_sender.nl.nl_pid == 0 || - snl.nl.nl_pid != udev_monitor->snl_trusted_sender.nl.nl_pid) { - dbg(udev_monitor->udev, "unicast netlink message ignored\n"); - return NULL; - } - } else if (snl.nl.nl_groups == UDEV_MONITOR_KERNEL) { - if (snl.nl.nl_pid > 0) { - dbg(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n", - snl.nl.nl_pid); - return NULL; - } + if (snl.nl.nl_groups == 0) { + /* unicast message, check if we trust the sender */ + if (udev_monitor->snl_trusted_sender.nl.nl_pid == 0 || + snl.nl.nl_pid != udev_monitor->snl_trusted_sender.nl.nl_pid) { + udev_dbg(udev_monitor->udev, "unicast netlink message ignored\n"); + return NULL; + } + } else if (snl.nl.nl_groups == UDEV_MONITOR_KERNEL) { + if (snl.nl.nl_pid > 0) { + udev_dbg(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n", + snl.nl.nl_pid); + return NULL; } } cmsg = CMSG_FIRSTHDR(&smsg); if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - dbg(udev_monitor->udev, "no sender credentials received, message ignored\n"); + udev_dbg(udev_monitor->udev, "no sender credentials received, message ignored\n"); return NULL; } cred = (struct ucred *)CMSG_DATA(cmsg); if (cred->uid != 0) { - dbg(udev_monitor->udev, "sender uid=%d, message ignored\n", cred->uid); + udev_dbg(udev_monitor->udev, "sender uid=%d, message ignored\n", cred->uid); return NULL; } + udev_device = udev_device_new(udev_monitor->udev); + if (udev_device == NULL) + return NULL; + if (memcmp(buf, "libudev", 8) == 0) { + struct udev_monitor_netlink_header *nlh; + /* udev message needs proper version magic */ nlh = (struct udev_monitor_netlink_header *) buf; if (nlh->magic != htonl(UDEV_MONITOR_MAGIC)) { - err(udev_monitor->udev, "unrecognized message signature (%x != %x)\n", - nlh->magic, htonl(UDEV_MONITOR_MAGIC)); + udev_err(udev_monitor->udev, "unrecognized message signature (%x != %x)\n", + nlh->magic, htonl(UDEV_MONITOR_MAGIC)); + udev_device_unref(udev_device); return NULL; } - if (nlh->properties_off+32 > buflen) + if (nlh->properties_off+32 > (size_t)buflen) { + udev_device_unref(udev_device); return NULL; + } + bufpos = nlh->properties_off; + + /* devices received from udev are always initialized */ + udev_device_set_is_initialized(udev_device); } else { /* kernel message with header */ bufpos = strlen(buf) + 1; if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) { - dbg(udev_monitor->udev, "invalid message length\n"); + udev_dbg(udev_monitor->udev, "invalid message length\n"); + udev_device_unref(udev_device); return NULL; } /* check message header */ if (strstr(buf, "@/") == NULL) { - dbg(udev_monitor->udev, "unrecognized message header\n"); + udev_dbg(udev_monitor->udev, "unrecognized message header\n"); + udev_device_unref(udev_device); return NULL; } } - udev_device = udev_device_new(udev_monitor->udev); - if (udev_device == NULL) - return NULL; udev_device_set_info_loaded(udev_device); while (bufpos < buflen) { @@ -627,7 +638,7 @@ retry: } if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { - dbg(udev_monitor->udev, "missing values, invalid device\n"); + udev_dbg(udev_monitor->udev, "missing values, invalid device\n"); udev_device_unref(udev_device); return NULL; } @@ -664,9 +675,6 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_list_entry *list_entry; uint64_t tag_bloom_bits; - if (udev_monitor->snl.nl.nl_family == 0) - return -EINVAL; - blen = udev_device_get_properties_monitor_buf(udev_device, &buf); if (blen < 32) return -EINVAL; @@ -714,7 +722,7 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor, smsg.msg_name = &udev_monitor->snl_destination; smsg.msg_namelen = sizeof(struct sockaddr_nl); count = sendmsg(udev_monitor->sock, &smsg, 0); - dbg(udev_monitor->udev, "passed %zi bytes to netlink monitor %p\n", count, udev_monitor); + udev_dbg(udev_monitor->udev, "passed %zi bytes to netlink monitor %p\n", count, udev_monitor); return count; }