X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev%2Flib%2Flibudev-monitor.c;h=e68bdf5bac208b1c5db84e7c269fd20dd54614c6;hp=e4cb80769cff4e86a792efe2a2c84d84169ccd1a;hb=ff109b8ded61ad1241843090dea2a367572ac375;hpb=bf8b2ae177fd016d03349b3aa881b72afd7d037d diff --git a/udev/lib/libudev-monitor.c b/udev/lib/libudev-monitor.c index e4cb80769..e68bdf5ba 100644 --- a/udev/lib/libudev-monitor.c +++ b/udev/lib/libudev-monitor.c @@ -3,18 +3,10 @@ * * Copyright (C) 2008 Kay Sievers * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * 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. */ #include @@ -37,22 +29,31 @@ struct udev_monitor { int refcount; int sock; struct sockaddr_nl snl; + struct sockaddr_nl snl_peer; struct sockaddr_un sun; socklen_t addrlen; }; +enum udev_monitor_netlink_group { + UDEV_MONITOR_KERNEL = 1, + UDEV_MONITOR_UDEV = 2, +}; + /** * udev_monitor_new_from_socket: * @udev: udev library context * @socket_path: unix socket path * - * Create new udev monitor, setup and connect to a specified socket. The - * path to a socket can point to an existing socket file, or it will be - * created if needed. If neccessary, the permissions adjustment as well as - * the later cleanup of the socket file, needs to be done by the caller. - * If the socket path starts with a '@' character, an abstract namespace + * Create new udev monitor and connect to a specified socket. The + * path to a socket either points to an existing socket file, or if + * the socket path starts with a '@' character, an abstract namespace * socket will be used. * + * A socket file will not be created. If it does not already exist, + * it will fall-back and connect to an abstract namespace socket with + * the given path. The permissions adjustment of a socket file, as + * well as the later cleanup, needs to be done by the caller. + * * The initial refcount is 1, and needs to be decremented to * release the resources of the udev monitor. * @@ -94,16 +95,54 @@ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char free(udev_monitor); return NULL; } + util_set_fd_cloexec(udev_monitor->sock); + dbg(udev, "monitor %p created with '%s'\n", udev_monitor, socket_path); return udev_monitor; } -struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev) +/** + * udev_monitor_new_from_netlink: + * @udev: udev library context + * @name: name of event source + * + * Create new udev monitor and connect to a specified event + * source. Valid sources identifiers are "udev" and "kernel". + * + * Applications should usually not connect directly to the + * "kernel" events, because the devices might not be useable + * at that time, before udev has configured them, and created + * device nodes. + * + * Accessing devices at the same time as udev, might result + * in unpredictable behavior. + * + * The "udev" events are sent out after udev has finished its + * event processing, all rules have been processed, and needed + * device nodes are created. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev monitor. + * + * Returns: a new udev monitor, or #NULL, in case of an error + **/ +struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) { struct udev_monitor *udev_monitor; + unsigned int group; if (udev == NULL) return NULL; + + if (name == NULL) + return NULL; + if (strcmp(name, "kernel") == 0) + group = UDEV_MONITOR_KERNEL; + else if (strcmp(name, "udev") == 0) + group = UDEV_MONITOR_UDEV; + else + return NULL; + udev_monitor = calloc(1, sizeof(struct udev_monitor)); if (udev_monitor == NULL) return NULL; @@ -116,12 +155,14 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev) free(udev_monitor); return NULL; } + util_set_fd_cloexec(udev_monitor->sock); udev_monitor->snl.nl_family = AF_NETLINK; - udev_monitor->snl.nl_pid = getpid(); - udev_monitor->snl.nl_groups = 1; + udev_monitor->snl.nl_groups = group; + udev_monitor->snl_peer.nl_family = AF_NETLINK; + udev_monitor->snl_peer.nl_groups = UDEV_MONITOR_UDEV; - dbg(udev, "monitor %p created with NETLINK_KOBJECT_UEVENT\n", udev_monitor); + dbg(udev, "monitor %p created with NETLINK_KOBJECT_UEVENT (%u)\n", udev_monitor, group); return udev_monitor; } @@ -130,23 +171,22 @@ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) int err; const int on = 1; - if (udev_monitor->snl.nl_family != 0) { - err = bind(udev_monitor->sock, (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl)); - if (err < 0) { - err(udev_monitor->udev, "bind failed: %m\n"); - return err; - } - dbg(udev_monitor->udev, "monitor %p listening on netlink\n", udev_monitor); - } else if (udev_monitor->sun.sun_family != 0) { - err = bind(udev_monitor->sock, (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen); - if (err < 0) { - err(udev_monitor->udev, "bind failed: %m\n"); - return err; - } - /* enable receiving of the sender credentials */ - setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - dbg(udev_monitor->udev, "monitor %p listening on socket\n", udev_monitor); + if (udev_monitor->sun.sun_family != 0) + err = bind(udev_monitor->sock, + (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen); + else if (udev_monitor->snl.nl_family != 0) + err = bind(udev_monitor->sock, + (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl)); + else + return -EINVAL; + + if (err < 0) { + err(udev_monitor->udev, "bind failed: %m\n"); + return err; } + + /* enable receiving of sender credentials */ + setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); return 0; } @@ -177,7 +217,7 @@ struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor) * udev_monitor_unref: * @udev_monitor: udev monitor * - * Drop a reference ofa udev monitor. If the refcount reaches zero, + * Drop a reference of a udev monitor. If the refcount reaches zero, * the bound socket will be closed, and the resources of the monitor * will be released. * @@ -247,6 +287,9 @@ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monito struct msghdr smsg; struct iovec iov; char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + struct cmsghdr *cmsg; + struct sockaddr_nl snl; + struct ucred *cred; char buf[4096]; size_t bufpos; int devpath_set = 0; @@ -266,37 +309,50 @@ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monito smsg.msg_control = cred_msg; smsg.msg_controllen = sizeof(cred_msg); + if (udev_monitor->snl.nl_family != 0) { + smsg.msg_name = &snl; + smsg.msg_namelen = sizeof snl; + } + if (recvmsg(udev_monitor->sock, &smsg, 0) < 0) { if (errno != EINTR) - info(udev_monitor->udev, "unable to receive message"); + info(udev_monitor->udev, "unable to receive message\n"); return NULL; } - if (udev_monitor->sun.sun_family != 0) { - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&smsg); - struct ucred *cred = (struct ucred *)CMSG_DATA (cmsg); - - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - info(udev_monitor->udev, "no sender credentials received, message ignored"); + if (udev_monitor->snl.nl_family != 0) { + if (snl.nl_groups == 0) { + info(udev_monitor->udev, "unicast netlink message ignored\n"); return NULL; } - - if (cred->uid != 0) { - info(udev_monitor->udev, "sender uid=%d, message ignored", cred->uid); + if ((snl.nl_groups == UDEV_MONITOR_KERNEL) && (snl.nl_pid > 0)) { + info(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n", snl.nl_pid); return NULL; } } + cmsg = CMSG_FIRSTHDR(&smsg); + if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { + info(udev_monitor->udev, "no sender credentials received, message ignored\n"); + return NULL; + } + + cred = (struct ucred *)CMSG_DATA(cmsg); + if (cred->uid != 0) { + info(udev_monitor->udev, "sender uid=%d, message ignored\n", cred->uid); + return NULL; + } + /* skip header */ bufpos = strlen(buf) + 1; if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) { - info(udev_monitor->udev, "invalid message length"); + info(udev_monitor->udev, "invalid message length\n"); return NULL; } /* check message header */ if (strstr(buf, "@/") == NULL) { - info(udev_monitor->udev, "unrecognized message header"); + info(udev_monitor->udev, "unrecognized message header\n"); return NULL; } @@ -389,9 +445,20 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_devi len = udev_device_get_properties_monitor_buf(udev_device, &buf); if (len < 32) return -1; - count = sendto(udev_monitor->sock, - buf, len, 0, - (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen); - info(udev_monitor->udev, "passed %zi bytes to monitor %p, \n", count, udev_monitor); + if (udev_monitor->sun.sun_family != 0) + count = sendto(udev_monitor->sock, + buf, len, 0, + (struct sockaddr *)&udev_monitor->sun, + udev_monitor->addrlen); + else if (udev_monitor->snl.nl_family != 0) + /* no destination besides the muticast group, we will always get ECONNREFUSED */ + count = sendto(udev_monitor->sock, + buf, len, 0, + (struct sockaddr *)&udev_monitor->snl_peer, + sizeof(struct sockaddr_nl)); + else + return -1; + + info(udev_monitor->udev, "passed %zi bytes to monitor %p\n", count, udev_monitor); return count; }