]> git.sur5r.net Git - i3/i3status/commitdiff
Merge pull request #251 from tomaskrizek/man-eth-wireless-format
authorIngo Bürk <admin@airblader.de>
Tue, 27 Mar 2018 07:10:51 +0000 (09:10 +0200)
committerGitHub <noreply@github.com>
Tue, 27 Mar 2018 07:10:51 +0000 (09:10 +0200)
man: explicitly use format_up/format_down in ethernet, wireless conf

17 files changed:
README.md
i3status.c
include/i3status.h
man/i3status.man
src/first_network_device.c
src/print_battery_info.c
src/print_eth_info.c
src/print_ip_addr.c
src/print_time.c
src/print_volume.c
src/print_wireless_info.c
testcases/018-battery-capacity/1/uevent [new file with mode: 0644]
testcases/018-battery-capacity/expected_output.txt [new file with mode: 0644]
testcases/018-battery-capacity/i3status.conf [new file with mode: 0644]
testcases/019-battery-capacity/1/uevent [new file with mode: 0644]
testcases/019-battery-capacity/expected_output.txt [new file with mode: 0644]
testcases/019-battery-capacity/i3status.conf [new file with mode: 0644]

index 16c4bd3e3614ef37c28bdb6027140d06d16a3c1c..75704ea0d84a41b5403a5e4bde97532170836334 100644 (file)
--- a/README.md
+++ b/README.md
@@ -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
 
index 558f8ab311623eff122afbe071745e45e84c950b..5088c96c8f236d80de1375e4b43d40f584165080 100644 (file)
@@ -111,6 +111,9 @@ static void *scalloc(size_t size) {
 }
 
 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;
@@ -628,6 +631,11 @@ int main(int argc, char *argv[]) {
         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
index 5042832d08bbbc0b809e25488719735a174ae1d0..9ac471d82e798cac81f78da9e27bd4a6b0e44c1f 100644 (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_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);
index 943653e981b6b7760c106f9f39b41ac3b14b7b50..01fa9ce5cd2d8b5030dfdf98e20a44587e556e69 100644 (file)
@@ -24,13 +24,12 @@ configuration files in the following order:
 
 == 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 @@ no effect when +output_format+ is set to +i3bar+ or +none+.
 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 @@ section just for this module.
 
 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 "`&amp;`", "`&lt;`",
@@ -307,8 +306,9 @@ network interface found on the system (excluding devices starting with "lo").
 === 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").
index 3f34cf2ed1bc031221389278f1d9ccca62d22f4e..1ed50a266aa8d438da68ed5d774325732ede6a62 100644 (file)
@@ -82,8 +82,6 @@ static net_type_t iface_type(const char *ifname) {
 
     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)
@@ -126,9 +124,8 @@ static net_type_t iface_type(const char *ifname) {
         close(s);
         return NET_TYPE_ETHERNET;
     }
-#else
-#error Missing implementation to determine interface type.
 #endif
+    return NET_TYPE_OTHER;
 }
 
 const char *first_eth_interface(const net_type_t type) {
index e22ca3ebae19c160fd0f07f71dde7597d776e69a..7a462f7fcf29dd0148d19f3a3189e09f24b5bc48 100644 (file)
@@ -513,13 +513,13 @@ void print_battery_info(yajl_gen json_gen, char *buffer, int number, const char
     // 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.
+    // 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)) {
+    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) {
+    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);
index e4bf15ea89d4984194310b33652210dcc4929252..b30d2b0166a0b1fd577c047eb4bf8a01f3bc4cbb 100644 (file)
@@ -2,6 +2,7 @@
 #include <string.h>
 #include <limits.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <sys/ioctl.h>
 #include <sys/types.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
  *
+ * 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';
+        }
     }
 
-    if (BEGINS_WITH(ip_address, "no IP"))
+    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;
+    }
+
+    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 @@ void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, cons
 
 out:
     END_COLOR;
+    free(ipv4_address);
+    free(ipv6_address);
     OUTPUT_FULL_TEXT(buffer);
 }
index 09b0992ac6e0b15175c49c05ab276bd1fd37af7e..f9cd1f48bd1535949abb66f8d45658217543f39c 100644 (file)
  * 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;
@@ -30,13 +35,13 @@ const char *get_ip_addr(const char *interface) {
     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 ||
            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 */
index c8da9d67087da12c3893504aa86178334913956a..3ed32b0157ca15810e830c6af38ead375ae73d63 100644 (file)
@@ -29,9 +29,9 @@ void set_timezone(const char *tz) {
         } 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) {
index 51e84f31364a8730e6fb9d6ee5394f174c2e943c..be6a1d7be2613a5463d85107a2c0d337482903b0 100644 (file)
@@ -211,6 +211,7 @@ void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *
 
 #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 @@ void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *
 
     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 @@ void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *
 
     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;
 
index 13f8184a706e2457b97798778e66213a922c2671..c3b52708f743a61f1ba218bc62b2e7bd7a0b0eac 100644 (file)
@@ -464,6 +464,21 @@ error1:
     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;
@@ -471,22 +486,48 @@ void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface,
 
     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++) {
@@ -568,5 +609,7 @@ void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface,
 
 out:
     END_COLOR;
+    free(ipv4_address);
+    free(ipv6_address);
     OUTPUT_FULL_TEXT(buffer);
 }
diff --git a/testcases/018-battery-capacity/1/uevent b/testcases/018-battery-capacity/1/uevent
new file mode 100644 (file)
index 0000000..00b1148
--- /dev/null
@@ -0,0 +1,2 @@
+POWER_SUPPLY_CHARGE_FULL=100
+POWER_SUPPLY_CHARGE_NOW=50
diff --git a/testcases/018-battery-capacity/expected_output.txt b/testcases/018-battery-capacity/expected_output.txt
new file mode 100644 (file)
index 0000000..ab07d0a
--- /dev/null
@@ -0,0 +1 @@
+50.00%
diff --git a/testcases/018-battery-capacity/i3status.conf b/testcases/018-battery-capacity/i3status.conf
new file mode 100644 (file)
index 0000000..7a3443c
--- /dev/null
@@ -0,0 +1,10 @@
+general {
+        output_format = "none"
+}
+
+order += "battery all"
+
+battery all {
+        format = "%percentage"
+        path = "testcases/018-battery-capacity/%d/uevent"
+}
diff --git a/testcases/019-battery-capacity/1/uevent b/testcases/019-battery-capacity/1/uevent
new file mode 100644 (file)
index 0000000..d004bf1
--- /dev/null
@@ -0,0 +1,2 @@
+POWER_SUPPLY_CHARGE_FULL_DESIGN=100
+POWER_SUPPLY_CHARGE_NOW=50
diff --git a/testcases/019-battery-capacity/expected_output.txt b/testcases/019-battery-capacity/expected_output.txt
new file mode 100644 (file)
index 0000000..ab07d0a
--- /dev/null
@@ -0,0 +1 @@
+50.00%
diff --git a/testcases/019-battery-capacity/i3status.conf b/testcases/019-battery-capacity/i3status.conf
new file mode 100644 (file)
index 0000000..8152d44
--- /dev/null
@@ -0,0 +1,11 @@
+general {
+        output_format = "none"
+}
+
+order += "battery all"
+
+battery all {
+        format = "%percentage"
+        path = "testcases/019-battery-capacity/%d/uevent"
+        last_full_capacity = true
+}