X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Ffirst_network_device.c;h=3f34cf2ed1bc031221389278f1d9ccca62d22f4e;hb=62d0905c7f636254161076a835a17e72251f8460;hp=c160027e1e8b98ef931668a2bcfa83d3d4b16a07;hpb=b219f47f394e536198997578e4cce3c539b84b6d;p=i3%2Fi3status diff --git a/src/first_network_device.c b/src/first_network_device.c index c160027..3f34cf2 100644 --- a/src/first_network_device.c +++ b/src/first_network_device.c @@ -1,39 +1,175 @@ -// vim:ts=8:expandtab +// vim:ts=4:sw=4:expandtab +#include #include #include #include +#ifdef __OpenBSD__ +#include +#include +#include +#include +#include +#include +#endif #include "i3status.h" -const char *first_eth_interface(const net_type_t type) { - static char *interface = NULL; - struct ifaddrs *ifaddr, *addrp; - struct stat stbuf; - static char path[1024]; - - getifaddrs(&ifaddr); - - if (ifaddr == NULL) - return NULL; - - free(interface); - interface = NULL; - for (addrp = ifaddr; - addrp != NULL; - addrp = addrp->ifa_next) { - if (strncasecmp("lo", addrp->ifa_name, strlen("lo")) == 0) - continue; - // Skip this interface if it is a wireless interface. - snprintf(path, sizeof(path), "/sys/class/net/%s/wireless", addrp->ifa_name); - const bool is_wireless = (stat(path, &stbuf) == 0); - if (( is_wireless && type == NET_TYPE_ETHERNET) || - (!is_wireless && type == NET_TYPE_WIRELESS)) - continue; - interface = strdup(addrp->ifa_name); - break; +#ifdef __linux__ +#define LOOPBACK_DEV "lo" +#else +#define LOOPBACK_DEV "lo0" +#endif + +static bool sysfs_devtype(char *dest, size_t n, const char *ifnam) { + FILE *fp; + char buf[1024]; + + snprintf(buf, sizeof(buf), "/sys/class/net/%s/uevent", ifnam); + if ((fp = fopen(buf, "r")) == NULL) + return false; + + dest[0] = '\0'; + + while (fgets(buf, sizeof(buf), fp)) { + size_t slen; + char *s; + + slen = strlen(buf); + /* Line is too long to fit in the buffer */ + if (buf[slen - 1] != '\n' && !feof(fp)) + break; + if ((s = strchr(buf, '='))) { + if (strncmp(buf, "DEVTYPE", s - buf)) + continue; + buf[slen - 1] = '\0'; + strncpy(dest, ++s, n); + break; } + } + fclose(fp); + return true; +} + +static bool is_virtual(const char *ifname) { + char path[1024]; + char *target = NULL; + bool is_virtual = false; + + snprintf(path, sizeof(path), "/sys/class/net/%s", ifname); + if ((target = realpath(path, NULL))) { + if (BEGINS_WITH(target, "/sys/devices/virtual/")) { + is_virtual = true; + } + } + + free(target); + return is_virtual; +} + +static net_type_t iface_type(const char *ifname) { +#ifdef __linux__ + char devtype[32]; + + if (!sysfs_devtype(devtype, sizeof(devtype), ifname)) + return NET_TYPE_OTHER; + + /* Default to Ethernet when no devtype is available */ + if (!devtype[0]) + return NET_TYPE_ETHERNET; - freeifaddrs(ifaddr); - return interface; + if (strcmp(devtype, "wlan") == 0) + return NET_TYPE_WIRELESS; + + if (strcmp(devtype, "wwan") == 0) + return NET_TYPE_OTHER; + + return NET_TYPE_OTHER; +#elif __OpenBSD__ + /* + *First determine if the device is a wireless device by trying two ioctl(2) + * commands against it. If either succeeds we can be sure it's a wireless + * device. + * Otherwise we try another ioctl(2) to determine the interface media. If that + * fails it's not an ethernet device eiter. + */ + struct ifreq ifr; + struct ieee80211_bssid bssid; + struct ieee80211_nwid nwid; + struct ifmediareq ifmr; + + int s, ibssid, inwid; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + return NET_TYPE_OTHER; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_data = (caddr_t)&nwid; + (void)strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + inwid = ioctl(s, SIOCG80211NWID, (caddr_t)&ifr); + + memset(&bssid, 0, sizeof(bssid)); + strlcpy(bssid.i_name, ifname, sizeof(bssid.i_name)); + ibssid = ioctl(s, SIOCG80211BSSID, &bssid); + + if (ibssid == 0 || inwid == 0) { + close(s); + return NET_TYPE_WIRELESS; + } + + (void)memset(&ifmr, 0, sizeof(ifmr)); + (void)strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + close(s); + return NET_TYPE_OTHER; + } else { + close(s); + return NET_TYPE_ETHERNET; + } +#else +#error Missing implementation to determine interface type. +#endif } +const char *first_eth_interface(const net_type_t type) { + static char *interface = NULL; + struct ifaddrs *ifaddr, *addrp; + net_type_t iftype; + + getifaddrs(&ifaddr); + + if (ifaddr == NULL) + return NULL; + + free(interface); + interface = NULL; + for (addrp = ifaddr; + addrp != NULL; + addrp = addrp->ifa_next) { + if (strncasecmp(LOOPBACK_DEV, addrp->ifa_name, strlen(LOOPBACK_DEV)) == 0) + continue; + if (addrp->ifa_addr == NULL) + continue; +#ifdef __OpenBSD__ + if (addrp->ifa_addr->sa_family != AF_LINK) + continue; +#else + // Skip PF_PACKET addresses (MAC addresses), as they are present on any + // ethernet interface. + if (addrp->ifa_addr->sa_family != AF_INET && + addrp->ifa_addr->sa_family != AF_INET6) + continue; +#endif /* !__OpenBSD__ */ + // Skip this interface if it is a wireless interface. + iftype = iface_type(addrp->ifa_name); + if (iftype != type) + continue; + if (is_virtual(addrp->ifa_name)) + continue; + interface = strdup(addrp->ifa_name); + break; + } + + freeifaddrs(ifaddr); + return interface; +}