Home | History | Annotate | Download | only in dev
      1 /*
      2  * dhcpcd - DHCP client daemon
      3  * Copyright (c) 2006-2015 Roy Marples <roy (at) marples.name>
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     24  * SUCH DAMAGE.
     25  */
     26 
     27 #ifdef LIBUDEV_NOINIT
     28 #  define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
     29 #  warning This version of udev is too old does not support
     30 #  warning per device initialization checks.
     31 #  warning As such, dhcpcd will need to depend on the
     32 #  warning udev-settle service or similar if starting
     33 #  warning in master mode.
     34 #endif
     35 
     36 #include <libudev.h>
     37 #include <string.h>
     38 #include <syslog.h>
     39 
     40 #include "../common.h"
     41 #include "../dev.h"
     42 
     43 static const char udev_name[] = "udev";
     44 static struct udev *udev;
     45 static struct udev_monitor *monitor;
     46 
     47 static struct dev_dhcpcd dhcpcd;
     48 
     49 static int
     50 udev_listening(void)
     51 {
     52 
     53 	return monitor ? 1 : 0;
     54 }
     55 
     56 static int
     57 udev_initialized(const char *ifname)
     58 {
     59 	struct udev_device *device;
     60 	int r;
     61 
     62 	device = udev_device_new_from_subsystem_sysname(udev, "net", ifname);
     63 	if (device) {
     64 #ifndef LIBUDEV_NOINIT
     65 		r = udev_device_get_is_initialized(device);
     66 #else
     67 		r = 1;
     68 #endif
     69 		udev_device_unref(device);
     70 	} else
     71 		r = 0;
     72 	return r;
     73 }
     74 
     75 static int
     76 udev_handle_device(void *ctx)
     77 {
     78 	struct udev_device *device;
     79 	const char *subsystem, *ifname, *action;
     80 
     81 	device = udev_monitor_receive_device(monitor);
     82 	if (device == NULL) {
     83 		syslog(LOG_ERR, "libudev: received NULL device");
     84 		return -1;
     85 	}
     86 
     87 	subsystem = udev_device_get_subsystem(device);
     88 	ifname = udev_device_get_sysname(device);
     89 	action = udev_device_get_action(device);
     90 
     91 	/* udev filter documentation says "usually" so double check */
     92 	if (strcmp(subsystem, "net") == 0) {
     93 		syslog(LOG_DEBUG, "%s: libudev: %s", ifname, action);
     94 		if (strcmp(action, "add") == 0 || strcmp(action, "move") == 0)
     95 			dhcpcd.handle_interface(ctx, 1, ifname);
     96 		else if (strcmp(action, "remove") == 0)
     97 			dhcpcd.handle_interface(ctx, -1, ifname);
     98 	}
     99 
    100 	udev_device_unref(device);
    101 	return 1;
    102 }
    103 
    104 static void
    105 udev_stop(void)
    106 {
    107 
    108 	if (monitor) {
    109 		udev_monitor_unref(monitor);
    110 		monitor = NULL;
    111 	}
    112 
    113 	if (udev) {
    114 		udev_unref(udev);
    115 		udev = NULL;
    116 	}
    117 }
    118 
    119 static int
    120 udev_start(void)
    121 {
    122 	int fd;
    123 
    124 	if (udev) {
    125 		syslog(LOG_ERR, "udev: already started");
    126 		return -1;
    127 	}
    128 
    129 	syslog(LOG_DEBUG, "udev: starting");
    130 	udev = udev_new();
    131 	if (udev == NULL) {
    132 		syslog(LOG_ERR, "udev_new: %m");
    133 		return -1;
    134 	}
    135 	monitor = udev_monitor_new_from_netlink(udev, "udev");
    136 	if (monitor == NULL) {
    137 		syslog(LOG_ERR, "udev_monitor_new_from_netlink: %m");
    138 		goto bad;
    139 	}
    140 #ifndef LIBUDEV_NOFILTER
    141 	if (udev_monitor_filter_add_match_subsystem_devtype(monitor,
    142 	    "net", NULL) != 0)
    143 	{
    144 		syslog(LOG_ERR,
    145 		    "udev_monitor_filter_add_match_subsystem_devtype: %m");
    146 		goto bad;
    147 	}
    148 #endif
    149 	if (udev_monitor_enable_receiving(monitor) != 0) {
    150 		syslog(LOG_ERR, "udev_monitor_enable_receiving: %m");
    151 		goto bad;
    152 	}
    153 	fd = udev_monitor_get_fd(monitor);
    154 	if (fd == -1) {
    155 		syslog(LOG_ERR, "udev_monitor_get_fd: %m");
    156 		goto bad;
    157 	}
    158 	return fd;
    159 
    160 bad:
    161 	udev_stop();
    162 	return -1;
    163 }
    164 
    165 int
    166 dev_init(struct dev *dev, const struct dev_dhcpcd *dev_dhcpcd)
    167 {
    168 
    169 	dev->name = udev_name;
    170 	dev->initialized = udev_initialized;
    171 	dev->listening = udev_listening;
    172 	dev->handle_device = udev_handle_device;
    173 	dev->stop = udev_stop;
    174 	dev->start = udev_start;
    175 
    176 	dhcpcd = *dev_dhcpcd;
    177 
    178 	return 0;
    179 }
    180