diff -Nru i3status-2.11/CHANGELOG i3status-2.12/CHANGELOG --- i3status-2.11/CHANGELOG 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/CHANGELOG 2018-05-11 09:10:46.000000000 +0000 @@ -1,3 +1,30 @@ +2018-05-11 i3status 2.12 + + • fix NULL value on OpenBSD when there is no acpitz0 + • show IP address when address has a label + • man: explicitly use format_up/format_down in ethernet, wireless conf + • man: remove SLOC comment + • treat zero battery capacity as "not available" + • add IPv6 address when IPv4 isn't available + • call tzset at each time print to pick up time zone changes + • doc: fixed minor typos + • be more cautious about handling invalid battery measurements. + • Makefile: make PKG_CONFIG overridable + • fix CPU unit tests + • use local timezone for battery %emptytime + • fix memory leak/use BEGINS_WITH macro + • _first_: add check for virtual ethernet devices + • add battery capacity parsing + • multiple CPU support for cpu_usage + • remove useless checks (we use char, not unsigned char) to fix compilation warnings + • disk: sanitize trailing slashes + • plug an fd leak in the OpenBSD-specific code for + • suppress printing :00 seconds of remaining battery lifetime, as apm(4)'s + estimate only has a granularity of minutes. + • fix the deciKelvin to Celsius conversion on FreeBSD + • make first_eth_interface() work on OpenBSD + • include sys/select.h on OpenBSD + 2017-01-21 i3status 2.11 • re-add forgotten wakeup call from SIGUSR1 handler diff -Nru i3status-2.11/debian/changelog i3status-2.12/debian/changelog --- i3status-2.11/debian/changelog 2017-12-05 16:16:32.000000000 +0000 +++ i3status-2.12/debian/changelog 2018-05-11 09:45:41.000000000 +0000 @@ -1,8 +1,15 @@ -i3status (2.11-1build1) bionic; urgency=medium +i3status (2.12-1) unstable; urgency=medium - * No-change rebuild against libconfuse2 + * Move the repository to salsa + * New upstream version 2.12 + * drop patches (applied upstream) + * bump dh compat level + * switch to secure debian/copyright URI + * bump standards-version (no changes necessary) + * switch to secure URI in debian/watch + * Priority: extra is now optional - -- Steve Langasek Tue, 05 Dec 2017 16:16:32 +0000 + -- Michael Stapelberg Fri, 11 May 2018 11:45:41 +0200 i3status (2.11-1) unstable; urgency=medium diff -Nru i3status-2.11/debian/compat i3status-2.12/debian/compat --- i3status-2.11/debian/compat 2017-01-21 14:42:57.000000000 +0000 +++ i3status-2.12/debian/compat 2018-05-11 09:45:41.000000000 +0000 @@ -1 +1 @@ -9 +11 diff -Nru i3status-2.11/debian/control i3status-2.12/debian/control --- i3status-2.11/debian/control 2017-12-05 16:16:32.000000000 +0000 +++ i3status-2.12/debian/control 2018-05-11 09:45:41.000000000 +0000 @@ -1,9 +1,8 @@ Source: i3status Section: utils -Priority: extra -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Michael Stapelberg -Build-Depends: debhelper (>= 9), +Priority: optional +Maintainer: Michael Stapelberg +Build-Depends: debhelper (>= 11), libiw-dev [linux-any], libconfuse-dev, asciidoc, @@ -15,10 +14,10 @@ libnl-genl-3-dev [linux-any], libpulse-dev, pkg-config -Standards-Version: 3.9.8 +Standards-Version: 4.1.4 Homepage: https://i3wm.org/i3status -Vcs-Git: https://anonscm.debian.org/git/users/stapelberg/i3status.git -Vcs-Browser: https://anonscm.debian.org/cgit/users/stapelberg/i3status.git/ +Vcs-Git: https://salsa.debian.org/stapelberg/i3status.git +Vcs-Browser: https://salsa.debian.org/stapelberg/i3status Package: i3status Architecture: any diff -Nru i3status-2.11/debian/copyright i3status-2.12/debian/copyright --- i3status-2.11/debian/copyright 2017-01-21 14:42:57.000000000 +0000 +++ i3status-2.12/debian/copyright 2018-05-11 09:45:41.000000000 +0000 @@ -1,4 +1,4 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: i3status Upstream-Contact: Michael Stapelberg Source: https://i3wm.org/i3status diff -Nru i3status-2.11/debian/rules i3status-2.12/debian/rules --- i3status-2.11/debian/rules 2017-01-21 14:42:57.000000000 +0000 +++ i3status-2.12/debian/rules 2018-05-11 09:45:41.000000000 +0000 @@ -6,9 +6,6 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all -override_dh_auto_build: - $(MAKE) - override_dh_install: $(MAKE) DESTDIR=$(CURDIR)/debian/i3status/ install dh_install diff -Nru i3status-2.11/debian/watch i3status-2.12/debian/watch --- i3status-2.11/debian/watch 2017-01-21 14:42:57.000000000 +0000 +++ i3status-2.12/debian/watch 2018-05-11 09:45:41.000000000 +0000 @@ -1,3 +1,3 @@ version=3 opts=pgpsigurlmangle=s/$/.asc/ \ - http://i3wm.org/i3status/ .*/i3status/i3status-(\d[\d_.-]+)\.tar\.bz2$ + https://i3wm.org/i3status/ .*/i3status/i3status-(\d[\d_.-]+)\.tar\.bz2$ diff -Nru i3status-2.11/i3status.c i3status-2.12/i3status.c --- i3status-2.11/i3status.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/i3status.c 2018-05-11 09:10:46.000000000 +0000 @@ -111,6 +111,9 @@ } char *sstrdup(const char *str) { + if (str == NULL) { + return NULL; + } char *result = strdup(str); exit_if_null(result, "Error: out of memory (strdup())\n"); return result; @@ -422,6 +425,7 @@ CFG_STR("format", "%usage", CFGF_NONE), CFG_STR("format_above_threshold", NULL, CFGF_NONE), CFG_STR("format_above_degraded_threshold", NULL, CFGF_NONE), + CFG_STR("path", "/proc/stat", CFGF_NONE), CFG_FLOAT("max_threshold", 95, CFGF_NONE), CFG_FLOAT("degraded_threshold", 90, CFGF_NONE), CFG_CUSTOM_ALIGN_OPT, @@ -627,6 +631,11 @@ die("Could not create socket\n"); int interval = cfg_getint(cfg_general, "interval"); + if (interval <= 0) { + die("Invalid interval attribute found in section %s, line %d: %d\n" + "Expected positive integer\n", + cfg_general->name, cfg_general->line, interval); + } /* One memory page which each plugin can use to buffer output. * Even though it’s unclean, we just assume that the user will not @@ -751,7 +760,7 @@ CASE_SEC("cpu_usage") { SEC_OPEN_MAP("cpu_usage"); - print_cpu_usage(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "format_above_threshold"), cfg_getstr(sec, "format_above_degraded_threshold"), cfg_getfloat(sec, "max_threshold"), cfg_getfloat(sec, "degraded_threshold")); + print_cpu_usage(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "format_above_threshold"), cfg_getstr(sec, "format_above_degraded_threshold"), cfg_getstr(sec, "path"), cfg_getfloat(sec, "max_threshold"), cfg_getfloat(sec, "degraded_threshold")); SEC_CLOSE_MAP; } } diff -Nru i3status-2.11/I3STATUS_VERSION i3status-2.12/I3STATUS_VERSION --- i3status-2.11/I3STATUS_VERSION 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/I3STATUS_VERSION 2018-05-11 09:10:46.000000000 +0000 @@ -1 +1 @@ -2.11 (2017-01-21) +2.12 (2018-05-11) diff -Nru i3status-2.11/include/i3status.h i3status-2.12/include/i3status.h --- i3status-2.11/include/i3status.h 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/include/i3status.h 2018-05-11 09:10:46.000000000 +0000 @@ -215,12 +215,12 @@ 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_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_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_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const char *path, const char *format, const char *format_above_threshold, int); -void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const float max_threshold, const float degraded_threshold); +void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const char *path, const float max_threshold, const float degraded_threshold); void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down); void print_load(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const float max_threshold); void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *fmt_muted, const char *device, const char *mixer, int mixer_idx); diff -Nru i3status-2.11/Makefile i3status-2.12/Makefile --- i3status-2.11/Makefile 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/Makefile 2018-05-11 09:10:46.000000000 +0000 @@ -14,6 +14,7 @@ endif endif +PKG_CONFIG ?= pkg-config CFLAGS+=-Wall -Wshadow -Wpointer-arith -Wcast-qual -Wsign-compare CFLAGS+=-g CFLAGS+=-std=gnu99 @@ -40,8 +41,8 @@ ifeq ($(OS),Linux) CPPFLAGS+=-DLINUX CPPFLAGS+=-D_GNU_SOURCE -CFLAGS += $(shell pkg-config --cflags libnl-genl-3.0) -LIBS += $(shell pkg-config --libs libnl-genl-3.0) +CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-genl-3.0) +LIBS += $(shell $(PKG_CONFIG) --libs libnl-genl-3.0) LIBS+=-lasound endif diff -Nru i3status-2.11/man/asciidoc.conf i3status-2.12/man/asciidoc.conf --- i3status-2.11/man/asciidoc.conf 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/man/asciidoc.conf 2018-05-11 09:10:46.000000000 +0000 @@ -7,7 +7,7 @@ {mantitle} {manvolnum} i3status -v2.11 +v2.12 i3 Manual diff -Nru i3status-2.11/man/i3status.man i3status-2.12/man/i3status.man --- i3status-2.11/man/i3status.man 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/man/i3status.man 2018-05-11 09:10:46.000000000 +0000 @@ -1,7 +1,7 @@ i3status(1) =========== Michael Stapelberg -v2.11, January 2017 +v2.12, May 2018 == NAME @@ -24,13 +24,12 @@ == DESCRIPTION -i3status is a small program (about 1500 SLOC) for generating a status bar for -i3bar, dzen2, xmobar, lemonbar or similar programs. It is designed to be very -efficient by issuing a very small number of system calls, as one generally -wants to update such a status line every second. This ensures that even under -high load, your status bar is updated correctly. Also, it saves a bit of energy -by not hogging your CPU as much as spawning the corresponding amount of shell -commands would. +i3status is a small program for generating a status bar for i3bar, dzen2, +xmobar, lemonbar or similar programs. It is designed to be very efficient by +issuing a very small number of system calls, as one generally wants to update +such a status line every second. This ensures that even under high load, your +status bar is updated correctly. Also, it saves a bit of energy by not hogging +your CPU as much as spawning the corresponding amount of shell commands would. == CONFIGURATION @@ -142,7 +141,7 @@ The +interval+ directive specifies the time in seconds for which i3status will sleep before printing the next status line. -Using +output_format+ you can chose which format strings i3status should +Using +output_format+ you can choose which format strings i3status should use in its output. Currently available are: i3bar:: @@ -178,14 +177,14 @@ If you don't fancy the vertical separators between modules i3status/i3bar uses by default, you can employ the +separator+ directive to configure how -modules are separated. You can either disable the default separator altogether +modules are separated. You can also disable the default separator altogether by setting it to the empty string. You might then define separation as part of a module's format string. This is your only option when using the i3bar output format as the separator is drawn by i3bar directly otherwise. For the other output formats, the provided non-empty string will be automatically enclosed with the necessary coloring bits if color support is enabled. -i3bar supports Pango markup, allowing your format strings to specify font +i3bar supports Pango markup, allowing your format strings to specify font, color, size, etc. by setting the +markup+ directive to "pango". Note that the ampersand ("&"), less-than ("<"), greater-than (">"), single-quote ("'"), and double-quote (""") characters need to be replaced with "`&`", "`<`", @@ -300,20 +299,25 @@ *Example order*: +wireless wlan0+ -*Example format*: +W: (%quality at %essid, %bitrate / %frequency) %ip+ +*Example format_up*: +W: (%quality at %essid, %bitrate / %frequency) %ip+ + +*Example format_down*: +W: down+ === 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 -it using +setcap cap_net_admin=ep $(which i3status)+. +interface. If no IPv4 address is available and an IPv6 address is, it will be +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 network interface found on the system (excluding devices starting with "lo"). *Example order*: +ethernet eth0+ -*Example format*: +E: %ip (%speed)+ +*Example format_up*: +E: %ip (%speed)+ + +*Example format_down*: +E: down+ === Battery @@ -405,13 +409,16 @@ It is possible to define a degraded_threshold that will color the load value yellow in case the CPU average over the last interval is getting -higher than the configured threshold. Defaults to 90. The output format +higher than the configured threshold. Defaults to 90. The output format when above degraded threshold can be customized with format_above_degraded_threshold. +For displaying the Nth CPU usage, you can use the %cpu format string, +starting from %cpu0. This feature is currently not supported in FreeBSD. + *Example order*: +cpu_usage+ -*Example format*: +%usage+ +*Example format*: +all: %usage CPU_0: %cpu0 CPU_1: %cpu1+ *Example max_threshold*: +75+ diff -Nru i3status-2.11/README.md i3status-2.12/README.md --- i3status-2.11/README.md 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/README.md 2018-05-11 09:10:46.000000000 +0000 @@ -2,12 +2,12 @@ ## Description -i3status is a small program (about 1500 SLOC) for generating a status bar for -i3bar, dzen2, xmobar or similar programs. It is designed to be very efficient by -issuing a very small number of system calls, as one generally wants to update -such a status line every second. This ensures that even under high load, your -status bar is updated correctly. Also, it saves a bit of energy by not hogging -your CPU as much as spawning the corresponding amount of shell commands would. +i3status is a small program for generating a status bar for i3bar, dzen2, xmobar +or similar programs. It is designed to be very efficient by issuing a very small +number of system calls, as one generally wants to update such a status line +every second. This ensures that even under high load, your status bar is updated +correctly. Also, it saves a bit of energy by not hogging your CPU as much as +spawning the corresponding amount of shell commands would. ## Development diff -Nru i3status-2.11/src/first_network_device.c i3status-2.12/src/first_network_device.c --- i3status-2.11/src/first_network_device.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/first_network_device.c 2018-05-11 09:10:46.000000000 +0000 @@ -3,9 +3,23 @@ #include #include #include +#ifdef __OpenBSD__ +#include +#include +#include +#include +#include +#include +#endif #include "i3status.h" +#ifdef __OpenBSD__ +#define LOOPBACK_DEV "lo0" +#else +#define LOOPBACK_DEV "lo" +#endif + static bool sysfs_devtype(char *dest, size_t n, const char *ifnam) { FILE *fp; char buf[1024]; @@ -36,12 +50,72 @@ 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 __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 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; @@ -52,6 +126,8 @@ return NET_TYPE_OTHER; return NET_TYPE_OTHER; +#endif + return NET_TYPE_OTHER; } const char *first_eth_interface(const net_type_t type) { @@ -69,19 +145,26 @@ for (addrp = ifaddr; addrp != NULL; addrp = addrp->ifa_next) { - if (strncasecmp("lo", addrp->ifa_name, strlen("lo")) == 0) + 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; } diff -Nru i3status-2.11/src/output.c i3status-2.12/src/output.c --- i3status-2.11/src/output.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/output.c 2018-05-11 09:10:46.000000000 +0000 @@ -111,12 +111,11 @@ default: if ((0x1 <= *text && *text <= 0x8) || (0xb <= *text && *text <= 0xc) || - (0xe <= *text && *text <= 0x1f) || - (0x7f <= *text && *text <= 0x84) || - (0x86 <= *text && *text <= 0x9f)) + (0xe <= *text && *text <= 0x1f)) { *buffer += sprintf(*buffer, "&#x%x;", *text); - else + } else { *(*buffer)++ = *text; + } break; } } diff -Nru i3status-2.11/src/print_battery_info.c i3status-2.12/src/print_battery_info.c --- i3status-2.11/src/print_battery_info.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/print_battery_info.c 2018-05-11 09:10:46.000000000 +0000 @@ -138,9 +138,13 @@ if (BEGINS_WITH(last, "POWER_SUPPLY_ENERGY_NOW=")) { watt_as_unit = true; batt_info->remaining = atoi(walk + 1); + batt_info->percentage_remaining = -1; } else if (BEGINS_WITH(last, "POWER_SUPPLY_CHARGE_NOW=")) { watt_as_unit = false; batt_info->remaining = atoi(walk + 1); + batt_info->percentage_remaining = -1; + } else if (BEGINS_WITH(last, "POWER_SUPPLY_CAPACITY=") && batt_info->remaining == -1) { + batt_info->percentage_remaining = atoi(walk + 1); } else if (BEGINS_WITH(last, "POWER_SUPPLY_CURRENT_NOW=")) batt_info->present_rate = abs(atoi(walk + 1)); else if (BEGINS_WITH(last, "POWER_SUPPLY_VOLTAGE_NOW=")) @@ -171,11 +175,19 @@ * POWER_SUPPLY_CHARGE_NOW is the unit of measurement. The energy is * given in mWh, the charge in mAh. So calculate every value given in * ampere to watt */ - if (!watt_as_unit && voltage != -1) { - batt_info->present_rate = (((float)voltage / 1000.0) * ((float)batt_info->present_rate / 1000.0)); - batt_info->remaining = (((float)voltage / 1000.0) * ((float)batt_info->remaining / 1000.0)); - batt_info->full_design = (((float)voltage / 1000.0) * ((float)batt_info->full_design / 1000.0)); - batt_info->full_last = (((float)voltage / 1000.0) * ((float)batt_info->full_last / 1000.0)); + if (!watt_as_unit && voltage >= 0) { + if (batt_info->present_rate > 0) { + batt_info->present_rate = (((float)voltage / 1000.0) * ((float)batt_info->present_rate / 1000.0)); + } + if (batt_info->remaining > 0) { + batt_info->remaining = (((float)voltage / 1000.0) * ((float)batt_info->remaining / 1000.0)); + } + if (batt_info->full_design > 0) { + batt_info->full_design = (((float)voltage / 1000.0) * ((float)batt_info->full_design / 1000.0)); + } + if (batt_info->full_last > 0) { + batt_info->full_last = (((float)voltage / 1000.0) * ((float)batt_info->full_last / 1000.0)); + } } #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) int state; @@ -482,7 +494,7 @@ /* These OSes report battery stats in whole percent. */ integer_battery_capacity = true; #endif -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__OpenBSD__) /* These OSes report battery time in minutes. */ hide_seconds = true; #endif @@ -495,8 +507,19 @@ return; } - int full = (last_full_capacity ? batt_info.full_last : batt_info.full_design); - if (full < 0 && batt_info.percentage_remaining < 0) { + // *Choose* a measure of the 'full' battery. It is whichever is better of + // the battery's (hardware-given) design capacity (batt_info.full_design) + // and the battery's last known good charge (batt_info.full_last). + // We prefer the design capacity, but use the last capacity if we don't have it, + // or if we are asked to (last_full_capacity == true); but similarly we use + // the design capacity if we don't have the last capacity. + // If we don't have either then both full_design and full_last <= 0, + // which implies full <= 0, which bails out on the following line. + int full = batt_info.full_design; + if (full <= 0 || (last_full_capacity && batt_info.full_last > 0)) { + full = batt_info.full_last; + } + if (full <= 0 && batt_info.remaining < 0 && batt_info.percentage_remaining < 0) { /* We have no physical measurements and no estimates. Nothing * much we can report, then. */ OUTPUT_FULL_TEXT(format_down); @@ -598,6 +621,7 @@ } else if (BEGINS_WITH(walk + 1, "emptytime")) { if (batt_info.seconds_remaining >= 0) { time_t empty_time = time(NULL) + batt_info.seconds_remaining; + set_timezone(NULL); /* Use local time. */ struct tm *empty_tm = localtime(&empty_time); if (hide_seconds) diff -Nru i3status-2.11/src/print_cpu_temperature.c i3status-2.12/src/print_cpu_temperature.c --- i3status-2.11/src/print_cpu_temperature.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/print_cpu_temperature.c 2018-05-11 09:10:46.000000000 +0000 @@ -13,7 +13,7 @@ #include #include #include -#define TZ_ZEROC 2732 +#define TZ_ZEROC 2731 #define TZ_KELVTOC(x) (((x)-TZ_ZEROC) / 10), abs(((x)-TZ_ZEROC) % 10) #define TZ_AVG(x) ((x)-TZ_ZEROC) / 10 #endif @@ -112,7 +112,7 @@ /* 'path' is the node within the full path (defaults to acpitz0). */ if (BEGINS_WITH(sensordev.xname, thermal_zone)) { mib[3] = SENSOR_TEMP; - /* Limit to temo0, but should retrieve from a full path... */ + /* Limit to temp0, but should retrieve from a full path... */ for (numt = 0; numt < 1 /*sensordev.maxnumt[SENSOR_TEMP]*/; numt++) { mib[4] = numt; if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) { @@ -205,7 +205,7 @@ /* * Reads the CPU temperature from /sys/class/thermal/thermal_zone%d/temp (or - * the user provided path) and returns the temperature in degree celcius. + * the user provided path) and returns the temperature in degree celsius. * */ void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const char *path, const char *format, const char *format_above_threshold, int max_threshold) { @@ -216,6 +216,8 @@ bool colorful_output = false; char *thermal_zone; temperature_t temperature; + temperature.raw_value = 0; + sprintf(temperature.formatted_value, "%.2f", 0.0); if (path == NULL) asprintf(&thermal_zone, THERMAL_ZONE, zone); diff -Nru i3status-2.11/src/print_cpu_usage.c i3status-2.12/src/print_cpu_usage.c --- i3status-2.11/src/print_cpu_usage.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/print_cpu_usage.c 2018-05-11 09:10:46.000000000 +0000 @@ -33,36 +33,68 @@ #include "i3status.h" -static int prev_total = 0; -static int prev_idle = 0; +struct cpu_usage { + int user; + int nice; + int system; + int idle; + int total; +}; + +static int cpu_count = 0; +static struct cpu_usage prev_all = {0, 0, 0, 0, 0}; +static struct cpu_usage *prev_cpus = NULL; +static struct cpu_usage *curr_cpus = NULL; /* * Reads the CPU utilization from /proc/stat and returns the usage as a * percentage. * */ -void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const float max_threshold, const float degraded_threshold) { +void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const char *path, const float max_threshold, const float degraded_threshold) { const char *selected_format = format; const char *walk; char *outwalk = buffer; - int curr_user = 0, curr_nice = 0, curr_system = 0, curr_idle = 0, curr_total; + struct cpu_usage curr_all = {0, 0, 0, 0, 0}; int diff_idle, diff_total, diff_usage; bool colorful_output = false; #if defined(LINUX) - static char statpath[512]; - char buf[1024]; - strcpy(statpath, "/proc/stat"); - if (!slurp(statpath, buf, sizeof(buf)) || - sscanf(buf, "cpu %d %d %d %d", &curr_user, &curr_nice, &curr_system, &curr_idle) != 4) + + // Detecting if CPU count has changed + int curr_cpu_count = sysconf(_SC_NPROCESSORS_ONLN); + if (curr_cpu_count != cpu_count) { + cpu_count = curr_cpu_count; + free(prev_cpus); + prev_cpus = (struct cpu_usage *)calloc(cpu_count, sizeof(struct cpu_usage)); + free(curr_cpus); + curr_cpus = (struct cpu_usage *)calloc(cpu_count, sizeof(struct cpu_usage)); + } + + char buf[4096]; + if (!slurp(path, buf, sizeof(buf))) goto error; + // Parsing all cpu values using strtok + if (strtok(buf, "\n") == NULL) + goto error; + char *buf_itr = NULL; + for (int cpu_idx = 0; cpu_idx < cpu_count; cpu_idx++) { + buf_itr = strtok(NULL, "\n"); + int curr_cpu_idx = -1; + if (!buf_itr || sscanf(buf_itr, "cpu%d %d %d %d %d", &curr_cpu_idx, &curr_cpus[cpu_idx].user, &curr_cpus[cpu_idx].nice, &curr_cpus[cpu_idx].system, &curr_cpus[cpu_idx].idle) != 5 || curr_cpu_idx != cpu_idx) + goto error; + curr_cpus[cpu_idx].total = curr_cpus[cpu_idx].user + curr_cpus[cpu_idx].nice + curr_cpus[cpu_idx].system + curr_cpus[cpu_idx].idle; + curr_all.user += curr_cpus[cpu_idx].user; + curr_all.nice += curr_cpus[cpu_idx].nice; + curr_all.system += curr_cpus[cpu_idx].system; + curr_all.idle += curr_cpus[cpu_idx].idle; + curr_all.total += curr_cpus[cpu_idx].total; + } - curr_total = curr_user + curr_nice + curr_system + curr_idle; - diff_idle = curr_idle - prev_idle; - diff_total = curr_total - prev_total; + diff_idle = curr_all.idle - prev_all.idle; + diff_total = curr_all.total - prev_all.total; diff_usage = (diff_total ? (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 : 0); - prev_total = curr_total; - prev_idle = curr_idle; + prev_all = curr_all; #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) @@ -84,16 +116,15 @@ goto error; #endif - curr_user = cp_time[CP_USER]; - curr_nice = cp_time[CP_NICE]; - curr_system = cp_time[CP_SYS]; - curr_idle = cp_time[CP_IDLE]; - curr_total = curr_user + curr_nice + curr_system + curr_idle; - diff_idle = curr_idle - prev_idle; - diff_total = curr_total - prev_total; + curr_all.user = cp_time[CP_USER]; + curr_all.nice = cp_time[CP_NICE]; + curr_all.system = cp_time[CP_SYS]; + curr_all.idle = cp_time[CP_IDLE]; + curr_all.total = curr_all.user + curr_all.nice + curr_all.system + curr_all.idle; + diff_idle = curr_all.idle - prev_all.idle; + diff_total = curr_all.total - prev_all.total; diff_usage = (diff_total ? (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 : 0); - prev_total = curr_total; - prev_idle = curr_idle; + prev_all = curr_all; #else goto error; #endif @@ -120,8 +151,32 @@ outwalk += sprintf(outwalk, "%02d%s", diff_usage, pct_mark); walk += strlen("usage"); } +#if defined(LINUX) + if (BEGINS_WITH(walk + 1, "cpu")) { + int number = 0; + sscanf(walk + 1, "cpu%d", &number); + if (number < 0 || number >= cpu_count) { + fprintf(stderr, "provided CPU number '%d' above detected number of CPU %d\n", number, cpu_count); + } else { + int cpu_diff_idle = curr_cpus[number].idle - prev_cpus[number].idle; + int cpu_diff_total = curr_cpus[number].total - prev_cpus[number].total; + int cpu_diff_usage = (cpu_diff_total ? (1000 * (cpu_diff_total - cpu_diff_idle) / cpu_diff_total + 5) / 10 : 0); + outwalk += sprintf(outwalk, "%02d%s", cpu_diff_usage, pct_mark); + } + int padding = 1; + int step = 10; + while (step < number) { + step *= 10; + padding++; + } + walk += strlen("cpu") + padding; + } +#endif } + for (int i = 0; i < cpu_count; i++) + prev_cpus[i] = curr_cpus[i]; + if (colorful_output) END_COLOR; diff -Nru i3status-2.11/src/print_disk_info.c i3status-2.12/src/print_disk_info.c --- i3status-2.11/src/print_disk_info.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/print_disk_info.c 2018-05-11 09:10:46.000000000 +0000 @@ -141,16 +141,20 @@ * we consider the device not mounted. */ mounted = false; } else { + char *sanitized = sstrdup(path); + if (strlen(sanitized) > 1 && sanitized[strlen(sanitized) - 1] == '/') + sanitized[strlen(sanitized) - 1] = '\0'; FILE *mntentfile = setmntent("/etc/mtab", "r"); struct mntent *m; while ((m = getmntent(mntentfile)) != NULL) { - if (strcmp(m->mnt_dir, path) == 0) { + if (strcmp(m->mnt_dir, sanitized) == 0) { mounted = true; break; } } endmntent(mntentfile); + free(sanitized); } #endif diff -Nru i3status-2.11/src/print_eth_info.c i3status-2.12/src/print_eth_info.c --- i3status-2.11/src/print_eth_info.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/print_eth_info.c 2018-05-11 09:10:46.000000000 +0000 @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -118,25 +119,60 @@ /* * 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) { const char *walk; - const char *ip_address = get_ip_addr(interface); char *outwalk = buffer; INSTANCE(interface); - if (ip_address == NULL) { - START_COLOR("color_bad"); - outwalk += sprintf(outwalk, "%s", format_down); - goto out; + 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"); + outwalk += sprintf(outwalk, "%s", format_down); + 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"); - else + } else { START_COLOR("color_good"); - + } for (walk = format_up; *walk != '\0'; walk++) { if (*walk != '%') { *(outwalk++) = *walk; @@ -154,5 +190,7 @@ out: END_COLOR; + free(ipv4_address); + free(ipv6_address); OUTPUT_FULL_TEXT(buffer); } diff -Nru i3status-2.11/src/print_ip_addr.c i3status-2.12/src/print_ip_addr.c --- i3status-2.11/src/print_ip_addr.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/print_ip_addr.c 2018-05-11 09:10:46.000000000 +0000 @@ -17,30 +17,36 @@ * 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]; - 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)); struct ifaddrs *ifaddr, *addrp; bool found = false; + int interface_len = strlen(interface); getifaddrs(&ifaddr); if (ifaddr == 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; (addrp != NULL && - (strcmp(addrp->ifa_name, interface) != 0 || + (strncmp(addrp->ifa_name, interface, interface_len) != 0 || addrp->ifa_addr == NULL || - addrp->ifa_addr->sa_family != AF_INET)); + addrp->ifa_addr->sa_family != family)); addrp = addrp->ifa_next) { /* Check if the interface is down */ - if (strcmp(addrp->ifa_name, interface) != 0) + if (strncmp(addrp->ifa_name, interface, interface_len) != 0) continue; found = true; if ((addrp->ifa_flags & IFF_RUNNING) == 0) { diff -Nru i3status-2.11/src/print_time.c i3status-2.12/src/print_time.c --- i3status-2.11/src/print_time.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/print_time.c 2018-05-11 09:10:46.000000000 +0000 @@ -29,9 +29,9 @@ } else { unsetenv("TZ"); } - tzset(); current_timezone = tz; } + tzset(); } 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) { diff -Nru i3status-2.11/src/print_volume.c i3status-2.12/src/print_volume.c --- i3status-2.11/src/print_volume.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/print_volume.c 2018-05-11 09:10:46.000000000 +0000 @@ -211,6 +211,7 @@ #if defined(__OpenBSD__) int oclass_idx = -1, master_idx = -1, master_mute_idx = -1; + int master_next = AUDIO_MIXER_LAST; mixer_devinfo_t devinfo, devinfo2; mixer_ctrl_t vinfo; @@ -228,12 +229,17 @@ devinfo2.index = 0; while (ioctl(mixfd, AUDIO_MIXER_DEVINFO, &devinfo2) >= 0) { - if ((devinfo2.type == AUDIO_MIXER_VALUE) && (devinfo2.mixer_class == oclass_idx) && (strncmp(devinfo2.label.name, AudioNmaster, MAX_AUDIO_DEV_LEN) == 0)) + if ((devinfo2.type == AUDIO_MIXER_VALUE) && (devinfo2.mixer_class == oclass_idx) && (strncmp(devinfo2.label.name, AudioNmaster, MAX_AUDIO_DEV_LEN) == 0)) { master_idx = devinfo2.index; + master_next = devinfo2.next; + } if ((devinfo2.type == AUDIO_MIXER_ENUM) && (devinfo2.mixer_class == oclass_idx) && (strncmp(devinfo2.label.name, AudioNmute, MAX_AUDIO_DEV_LEN) == 0)) - master_mute_idx = devinfo2.index; + if (master_next == devinfo2.index) + master_mute_idx = devinfo2.index; + if (master_next != AUDIO_MIXER_LAST) + master_next = devinfo2.next; devinfo2.index++; } @@ -246,6 +252,7 @@ vinfo.dev = master_idx; vinfo.type = AUDIO_MIXER_VALUE; + vinfo.un.value.num_channels = devinfo.un.v.num_channels; if (ioctl(mixfd, AUDIO_MIXER_READ, &vinfo) == -1) goto out; diff -Nru i3status-2.11/src/print_wireless_info.c i3status-2.12/src/print_wireless_info.c --- i3status-2.11/src/print_wireless_info.c 2017-01-21 15:00:31.000000000 +0000 +++ i3status-2.12/src/print_wireless_info.c 2018-05-11 09:10:46.000000000 +0000 @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -463,6 +464,21 @@ 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) { const char *walk; char *outwalk = buffer; @@ -470,22 +486,48 @@ INSTANCE(interface); - const char *ip_address = get_ip_addr(interface); - if (ip_address == NULL) { - START_COLOR("color_bad"); - outwalk += sprintf(outwalk, "%s", format_down); - goto out; + 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"); + outwalk += sprintf(outwalk, "%s", format_down); + 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; if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) START_COLOR((info.quality < info.quality_average ? "color_degraded" : "color_good")); - else - START_COLOR((BEGINS_WITH(ip_address, "no IP") ? "color_degraded" : "color_good")); - } else { - walk = format_down; - START_COLOR("color_bad"); + else { + if (BEGINS_WITH(ip_address, "no IP")) { + START_COLOR("color_degraded"); + } else { + START_COLOR("color_good"); + } + } } for (; *walk != '\0'; walk++) { @@ -567,5 +609,7 @@ out: END_COLOR; + free(ipv4_address); + free(ipv6_address); OUTPUT_FULL_TEXT(buffer); }