]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add udev support for interface arrival and departure.
authorRoy Marples <roy@marples.name>
Mon, 9 Sep 2013 16:14:06 +0000 (16:14 +0000)
committerRoy Marples <roy@marples.name>
Mon, 9 Sep 2013 16:14:06 +0000 (16:14 +0000)
This is because udev likes to rename the interface, which it can't
do if dhcpcd grabs it first.

configure
dev/udev.c [new file with mode: 0644]
dev/udev.h [new file with mode: 0644]
if-linux.c

index 69a5ad8067485e8e2953ddcc9ccc3860f747d12e..7b091f17571f7ef86f918df9b170d5504d7c4327 100755 (executable)
--- a/configure
+++ b/configure
@@ -9,6 +9,7 @@ ARC4RANDOM=
 CLOSEFROM=
 GETLINE=
 STRLCPY=
+UDEV=
 OS=
 BUILD=
 HOST=
@@ -57,8 +58,9 @@ for x do
        --without-getline) GETLINE=no;;
        --without-strlcpy) STRLCPY=no;;
         --without-posix_spawn) POSIX_SPAWN=no;;
-       --without-pollts) POLLTS=xno;;
+       --without-pollts) POLLTS=no;;
        --with-pollts) POLLTS=$var;;
+       --without-udev) UDEV=no;;
        --serviceexists) SERVICEEXISTS=$var;;
        --servicecmd) SERVICECMD=$var;;
        --servicestatus) SERVICESTATUS=$var;;
@@ -599,6 +601,42 @@ pselect)
        ;;
 esac
 
+if [ "$UDEV" != no ]; then
+       printf "Checking for libudev ... "
+       LIBUDEV_CFLAGS=$(pkg-config --cflags libudev 2>/dev/null)
+       LIBUDEV_LIBS=$(pkg-config --libs libudev 2>/dev/null)
+fi
+if [ "$UDEV" != no -a -n "$LIBUDEV_LIBS" ]; then
+       echo "yes"
+       echo "SRCS+=    dev/udev.c" >>$CONFIG_MK
+       if [ -n "$LIBUDEV_CFLAGS" ]; then
+               echo "CFLAGS+=          $LIBUDEV_CFLAGS" >>$CONFIG_MK
+       fi
+       echo "CPPFLAGS+=        -DLIBUDEV" >>$CONFIG_MK
+       echo "LDADD+=           $LIBUDEV_LIBS" >>$CONFIG_MK
+
+       printf "Checking udev_monitor_filter_add_match_subsystem_devtype ..."
+       cat <<EOF >_udev.c
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+#include <libudev.h>
+#include <stdlib.h>
+int main(void) {
+       udev_monitor_filter_add_match_subsystem_devtype(NULL, NULL, NULL);
+       return 0;
+}
+EOF
+       if $XCC $LIBUDEV_CFLAGS _udev.c -o _udev $LIBUDEV_LIBS2 2>/dev/null
+       then
+               echo "CPPFLAGS+=                -DLIBUDEV_FILTER" >>$CONFIG_MK
+               echo " yes"
+       else
+               echo " no"
+       fi
+       rm -f _udev.c _udev
+elif [ "$UDEV" != no ]; then
+       echo "no"
+fi
+
 if [ -z "$SERVICECMD" ]; then
        printf "Checking for OpenRC ... "
        if [ -x /sbin/rc-service ]; then
diff --git a/dev/udev.c b/dev/udev.c
new file mode 100644 (file)
index 0000000..207253e
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+
+#include <libudev.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "../common.h"
+#include "../dhcpcd.h"
+#include "../eloop.h"
+#include "udev.h"
+
+static struct udev_monitor *monitor;
+
+static void
+libudev_handledata(__unused void *arg)
+{
+       struct udev_device *device;
+       const char *ifname, *action;
+
+       device = udev_monitor_receive_device(monitor);
+       if (device == NULL) {
+               syslog(LOG_DEBUG, "libudev: received NULL device");
+               return;
+       }
+
+       /* udev filter documentation says "usually" so double check */
+       action = udev_device_get_subsystem(device);
+       if (strcmp(action, "net"))
+               return;
+
+       ifname = udev_device_get_sysname(device);
+       action = udev_device_get_action(device);
+       if (strcmp(action, "add") == 0)
+               handle_interface(1, ifname);
+       else if (strcmp(action, "remove") == 0)
+               handle_interface(-1, ifname);
+}
+
+int
+libudev_listening(void)
+{
+
+       return monitor == NULL ? 0 : 1;
+}
+
+void
+libudev_stop(void)
+{
+       struct udev *udev;
+
+       if (monitor) {
+               udev = udev_monitor_get_udev(monitor);
+               udev_unref(udev);
+               udev_monitor_unref(monitor);
+               monitor = NULL;
+       }
+}
+
+int
+libudev_start(void)
+{
+       struct udev *udev;
+       int fd;
+
+       syslog(LOG_DEBUG, "libudev: starting");
+       udev = udev_new();
+       if (udev == NULL) {
+               syslog(LOG_ERR, "udev_new: %m");
+               return -1;
+       }
+       monitor = udev_monitor_new_from_netlink(udev, "udev");
+       if (monitor == NULL) {
+               syslog(LOG_ERR, "udev_monitor_new_from_netlink: %m");
+               goto bad;
+       }
+#ifdef LIBUDEV_FILTER
+       if (udev_monitor_filter_add_match_subsystem_devtype(monitor,
+           "net", NULL) != 0)
+       {
+               syslog(LOG_ERR,
+                   "udev_monitor_filter_add_match_subsystem_devtype: %m");
+               goto bad;
+       }
+#endif
+       if (udev_monitor_enable_receiving(monitor) != 0) {
+               syslog(LOG_ERR, "udev_monitor_enable_receiving: %m");
+               goto bad;
+       }
+       fd = udev_monitor_get_fd(monitor);
+       if (fd == -1) {
+               syslog(LOG_ERR, "udev_monitor_get_fd: %m");
+               goto bad;
+       }
+       if (eloop_event_add(fd, libudev_handledata, NULL) == -1) {
+               syslog(LOG_ERR, "%s: eloop_event_add: %m", __func__);
+               goto bad;
+       }
+
+       atexit(libudev_stop);
+
+       return fd;
+bad:
+
+       libudev_stop();
+       return -1;
+}
diff --git a/dev/udev.h b/dev/udev.h
new file mode 100644 (file)
index 0000000..a1702ff
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2013 Roy Marples <roy@marples.name>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef LIBUDEV_H
+#define LIBUDEV_H
+
+int libudev_listening(void);
+int libudev_start(void);
+void libudev_stop(void);
+
+#endif
index 6c893e282df6145d5d13010db32cd679084e17aa..6758a25a2b02f4da36537e3389a6674f5ebd3f42 100644 (file)
@@ -61,6 +61,9 @@
 #include "ipv4.h"
 #include "ipv6.h"
 #include "net.h"
+#ifdef LIBUDEV
+#include "dev/udev.h"
+#endif
 
 static int sock_fd;
 static struct sockaddr_nl sock_nl;
@@ -122,6 +125,7 @@ _open_link_socket(struct sockaddr_nl *nl)
        if (bind(fd, (struct sockaddr *)nl, sizeof(*nl)) == -1)
                return -1;
        set_cloexec(fd);
+
        return fd;
 }
 
@@ -141,6 +145,14 @@ open_link_socket(void)
 {
        struct sockaddr_nl snl;
 
+#ifdef LIBUDEV
+       /* If we have libudev, we need to listen to it for interface
+        * arrivals and departures so we get the interface name udev wants
+        * to give it.
+        * If we fail to work, we fall back to listening to the kernel. */
+       libudev_start();
+#endif
+
        memset(&snl, 0, sizeof(snl));
        snl.nl_groups = RTMGRP_LINK;
 
@@ -411,6 +423,13 @@ link_netlink(struct nlmsghdr *nlm)
        struct ifinfomsg *ifi;
        char ifn[IF_NAMESIZE + 1];
 
+#ifdef LIBUDEV
+       if (nlm->nlmsg_type == RTM_NEWLINK || nlm->nlmsg_type == RTM_DELLINK) {
+               if (libudev_listening())
+                       return 1;
+       }
+#endif
+
        len = link_route(nlm);
        if (len != 0)
                return len;