Add IPv6 address when IPv4 isn't available (#247)

This commit is contained in:
Emeric Planet 2017-12-11 11:38:31 +01:00 committed by Michael Stapelberg
parent c3424e10be
commit 53fb9b4f18
6 changed files with 114 additions and 27 deletions

View File

@ -111,6 +111,9 @@ static void *scalloc(size_t size) {
} }
char *sstrdup(const char *str) { char *sstrdup(const char *str) {
if (str == NULL) {
return NULL;
}
char *result = strdup(str); char *result = strdup(str);
exit_if_null(result, "Error: out of memory (strdup())\n"); exit_if_null(result, "Error: out of memory (strdup())\n");
return result; return result;

View File

@ -215,7 +215,7 @@ void print_disk_info(yajl_gen json_gen, char *buffer, const char *path, const ch
void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char *path, const char *format, const char *format_down, const char *status_chr, const char *status_bat, const char *status_unk, const char *status_full, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity, bool hide_seconds); void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char *path, const char *format, const char *format_down, const char *status_chr, const char *status_bat, const char *status_unk, const char *status_full, int low_threshold, char *threshold_type, bool last_full_capacity, bool integer_battery_capacity, bool hide_seconds);
void print_time(yajl_gen json_gen, char *buffer, const char *title, const char *format, const char *tz, const char *locale, const char *format_time, time_t t); void print_time(yajl_gen json_gen, char *buffer, const char *title, const char *format, const char *tz, const char *locale, const char *format_time, time_t t);
void print_ddate(yajl_gen json_gen, char *buffer, const char *format, time_t t); void print_ddate(yajl_gen json_gen, char *buffer, const char *format, time_t t);
const char *get_ip_addr(const char *interface); const char *get_ip_addr(const char *interface, int family);
void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down); void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down);
void print_run_watch(yajl_gen json_gen, char *buffer, const char *title, const char *pidfile, const char *format, const char *format_down); void print_run_watch(yajl_gen json_gen, char *buffer, const char *title, const char *pidfile, const char *format, const char *format_down);
void print_path_exists(yajl_gen json_gen, char *buffer, const char *title, const char *path, const char *format, const char *format_down); void print_path_exists(yajl_gen json_gen, char *buffer, const char *title, const char *path, const char *format, const char *format_down);

View File

@ -305,8 +305,9 @@ network interface found on the system (excluding devices starting with "lo").
=== Ethernet === Ethernet
Gets the IP address and (if possible) the link speed of the given ethernet Gets the IP address and (if possible) the link speed of the given ethernet
interface. Getting the link speed requires the cap_net_admin capability. Set interface. If no IPv4 address is available and an IPv6 address is, it will be
it using +setcap cap_net_admin=ep $(which i3status)+. displayed. Getting the link speed requires the cap_net_admin capability.
Set it using +setcap cap_net_admin=ep $(which i3status)+.
The special interface name `_first_` will be replaced by the first non-wireless The special interface name `_first_` will be replaced by the first non-wireless
network interface found on the system (excluding devices starting with "lo"). network interface found on the system (excluding devices starting with "lo").

View File

@ -2,6 +2,7 @@
#include <string.h> #include <string.h>
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
@ -118,25 +119,60 @@ static int print_eth_speed(char *outwalk, const char *interface) {
/* /*
* Combines ethernet IP addresses and speed (if requested) for displaying * Combines ethernet IP addresses and speed (if requested) for displaying
* *
* Table summarizing what is the decision to prefer IPv4 or IPv6
* based their values.
*
* | ipv4_address | ipv6_address | Chosen IP | Color |
* |--------------|--------------|-----------|-------------------|
* | NULL | NULL | None | bad (red) |
* | NULL | no IP | IPv6 | degraded (orange) |
* | NULL | ::1/128 | IPv6 | ok (green) |
* | no IP | NULL | IPv4 | degraded |
* | no IP | no IP | IPv4 | degraded |
* | no IP | ::1/128 | IPv6 | ok |
* | 127.0.0.1 | NULL | IPv4 | ok |
* | 127.0.0.1 | no IP | IPv4 | ok |
* | 127.0.0.1 | ::1/128 | IPv4 | ok |
*/ */
void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down) { void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down) {
const char *walk; const char *walk;
const char *ip_address = get_ip_addr(interface);
char *outwalk = buffer; char *outwalk = buffer;
INSTANCE(interface); INSTANCE(interface);
if (ip_address == NULL) { char *ipv4_address = sstrdup(get_ip_addr(interface, AF_INET));
char *ipv6_address = sstrdup(get_ip_addr(interface, AF_INET6));
/*
* Removing '%' and following characters from IPv6 since the interface identifier is redundant,
* as the output already includes the interface name.
*/
if (ipv6_address != NULL) {
char *prct_ptr = strstr(ipv6_address, "%");
if (prct_ptr != NULL) {
*prct_ptr = '\0';
}
}
bool prefer_ipv4 = true;
if (ipv4_address == NULL) {
if (ipv6_address == NULL) {
START_COLOR("color_bad"); START_COLOR("color_bad");
outwalk += sprintf(outwalk, "%s", format_down); outwalk += sprintf(outwalk, "%s", format_down);
goto out; goto out;
} else {
prefer_ipv4 = false;
}
} else if (BEGINS_WITH(ipv4_address, "no IP") && ipv6_address != NULL && !BEGINS_WITH(ipv6_address, "no IP")) {
prefer_ipv4 = false;
} }
if (BEGINS_WITH(ip_address, "no IP")) const char *ip_address = (prefer_ipv4) ? ipv4_address : ipv6_address;
if (BEGINS_WITH(ip_address, "no IP")) {
START_COLOR("color_degraded"); START_COLOR("color_degraded");
else } else {
START_COLOR("color_good"); START_COLOR("color_good");
}
for (walk = format_up; *walk != '\0'; walk++) { for (walk = format_up; *walk != '\0'; walk++) {
if (*walk != '%') { if (*walk != '%') {
*(outwalk++) = *walk; *(outwalk++) = *walk;
@ -154,5 +190,7 @@ void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, cons
out: out:
END_COLOR; END_COLOR;
free(ipv4_address);
free(ipv6_address);
OUTPUT_FULL_TEXT(buffer); OUTPUT_FULL_TEXT(buffer);
} }

View File

@ -17,9 +17,14 @@
* interface is up and running but hasn't got an IP address yet * interface is up and running but hasn't got an IP address yet
* *
*/ */
const char *get_ip_addr(const char *interface) { const char *get_ip_addr(const char *interface, int family) {
static char part[512]; static char part[512];
socklen_t len = sizeof(struct sockaddr_in); socklen_t len = 0;
if (family == AF_INET)
len = sizeof(struct sockaddr_in);
else if (family == AF_INET6)
len = sizeof(struct sockaddr_in6);
memset(part, 0, sizeof(part)); memset(part, 0, sizeof(part));
struct ifaddrs *ifaddr, *addrp; struct ifaddrs *ifaddr, *addrp;
@ -30,13 +35,13 @@ const char *get_ip_addr(const char *interface) {
if (ifaddr == NULL) if (ifaddr == NULL)
return NULL; return NULL;
/* Skip until we are at the AF_INET address of interface */ /* Skip until we are at the input family address of interface */
for (addrp = ifaddr; for (addrp = ifaddr;
(addrp != NULL && (addrp != NULL &&
(strcmp(addrp->ifa_name, interface) != 0 || (strcmp(addrp->ifa_name, interface) != 0 ||
addrp->ifa_addr == NULL || addrp->ifa_addr == NULL ||
addrp->ifa_addr->sa_family != AF_INET)); addrp->ifa_addr->sa_family != family));
addrp = addrp->ifa_next) { addrp = addrp->ifa_next) {
/* Check if the interface is down */ /* Check if the interface is down */

View File

@ -464,6 +464,21 @@ error1:
return 0; return 0;
} }
/* Table summarizing what is the decision to prefer IPv4 or IPv6
* based their values.
*
* | ipv4_address | ipv6_address | Chosen IP | Color |
* |--------------|--------------|-----------|-------------------|
* | NULL | NULL | None | bad (red) |
* | NULL | no IP | IPv6 | degraded (orange) |
* | NULL | ::1/128 | IPv6 | ok (green) |
* | no IP | NULL | IPv4 | degraded |
* | no IP | no IP | IPv4 | degraded |
* | no IP | ::1/128 | IPv6 | ok |
* | 127.0.0.1 | NULL | IPv4 | ok |
* | 127.0.0.1 | no IP | IPv4 | ok |
* | 127.0.0.1 | ::1/128 | IPv4 | ok |
*/
void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down) { void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down) {
const char *walk; const char *walk;
char *outwalk = buffer; char *outwalk = buffer;
@ -471,22 +486,45 @@ void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface,
INSTANCE(interface); INSTANCE(interface);
const char *ip_address = get_ip_addr(interface); char *ipv4_address = sstrdup(get_ip_addr(interface, AF_INET));
if (ip_address == NULL) { char *ipv6_address = sstrdup(get_ip_addr(interface, AF_INET6));
// Removing '%' and following characters from IPv6
if (ipv6_address != NULL) {
char *prct_ptr = strstr(ipv6_address, "%");
if (prct_ptr != NULL) {
*prct_ptr = '\0';
}
}
bool prefer_ipv4 = true;
if (ipv4_address == NULL) {
if (ipv6_address == NULL) {
START_COLOR("color_bad"); START_COLOR("color_bad");
outwalk += sprintf(outwalk, "%s", format_down); outwalk += sprintf(outwalk, "%s", format_down);
goto out; goto out;
} else {
prefer_ipv4 = false;
}
} else if (BEGINS_WITH(ipv4_address, "no IP") && ipv6_address != NULL && !BEGINS_WITH(ipv6_address, "no IP")) {
prefer_ipv4 = false;
} }
if (get_wireless_info(interface, &info)) { const char *ip_address = (prefer_ipv4) ? ipv4_address : ipv6_address;
if (!get_wireless_info(interface, &info)) {
walk = format_down;
START_COLOR("color_bad");
} else {
walk = format_up; walk = format_up;
if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY)
START_COLOR((info.quality < info.quality_average ? "color_degraded" : "color_good")); START_COLOR((info.quality < info.quality_average ? "color_degraded" : "color_good"));
else else {
START_COLOR((BEGINS_WITH(ip_address, "no IP") ? "color_degraded" : "color_good")); if (BEGINS_WITH(ip_address, "no IP")) {
START_COLOR("color_degraded");
} else { } else {
walk = format_down; START_COLOR("color_good");
START_COLOR("color_bad"); }
}
} }
for (; *walk != '\0'; walk++) { for (; *walk != '\0'; walk++) {
@ -568,5 +606,7 @@ void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface,
out: out:
END_COLOR; END_COLOR;
free(ipv4_address);
free(ipv6_address);
OUTPUT_FULL_TEXT(buffer); OUTPUT_FULL_TEXT(buffer);
} }