diff options
-rwxr-xr-x | capibarra | 256 |
1 files changed, 136 insertions, 120 deletions
@@ -1,6 +1,7 @@ #!/usr/bin/env perl # -# Dependencies: iproute2 iw volctl +# Dependencies (Linux): iproute2 iw volctl +# Dependencies (OpenBSD): (none) # ################################################################################ @@ -8,7 +9,9 @@ use strict; use warnings; use feature 'signatures'; +use Data::Dumper; use Time::HiRes qw<gettimeofday>; +use List::Util qw<sum>; # Turn off buffering $| = 1; @@ -32,7 +35,7 @@ sub color_thresholds($x, $warn, $err) { } sub txt_onoff($txt, $on) { - return $on ? $txt : "${txt}X"; + return $on ? $txt : "(${txt})"; } ################################################################################ @@ -40,9 +43,11 @@ sub txt_onoff($txt, $on) { sub show_time_delta($section, $time0, $computed) { my (undef, $time) = gettimeofday(); + $time = $time0 if $time < $time0; my $diff = ($time - $time0) / 1000; my $extra = $computed ? ' (COMPUTED)' : ''; - printf STDERR "Spent $diff milliseconds @ $section$extra\n"; + printf STDERR "Spent %.2f milliseconds @ $section$extra\n", $diff; + return $diff; } @@ -59,31 +64,31 @@ my %linux_mods; $linux_mods{'CPU'} = { period => 5, - init => sub { + default_opts => {'warn5' => 1, 'err5' => 3}, + mkcompute => sub($opts) { open(my $loadf, '<', '/proc/loadavg'); - return { state => $loadf }; - }, - compute => sub($loadf) { - read($loadf, my $loads, 14); - seek($loadf, 0, 0); - my @loads = split ' ', $loads; - return { - text => sprintf ("CPU %s/%s/%s", @loads), - color => color_thresholds($loads[1], 1, 3), - state => $loadf, + return sub { + read($loadf, my $loads, 14); + seek($loadf, 0, 0); + my @loads = split ' ', $loads; + return { + text => sprintf ("CPU %s/%s/%s", @loads), + color => color_thresholds($loads[1], $opts->{warn5}, $opts->{err5}), + }; }; - } + }, }; $linux_mods{'MEM'} = { period => 5, - compute => sub { + default_opts => {'warn' => 70, 'err' => 90}, + mkcompute => sub($opts) { my $mused = `free | awk '/^Mem:/ {printf("%d", 100 * (\$3/\$2))}'`; return { text => sprintf("MEM %s%%", $mused), - color => color_thresholds($mused, 70, 90), + color => color_thresholds($mused, $opts->{warn}, $opts->{err}), }; - } + }, }; sub render_iface_linux($is_wireless, $name, $status, $addr) { @@ -107,32 +112,31 @@ $linux_mods{'NET'} = { push @wireless, [1, $if, $status, $addr] if (substr($if, 0, 2) eq 'wl'); } return { text => join ' | ', map { render_iface_linux(@$_) } @wired, @wireless }; - } + }, }; $linux_mods{'BAT'} = { period => 3, - init => sub { + default_opts => {'warn' => 30, 'err' => 10}, + mkcompute => sub($opts) { my $BATDIR = glob '/sys/class/power_supply/BAT?'; open(my $bcapf, '<', "$BATDIR/capacity"); open(my $bstatf, '<', "$BATDIR/status"); - return { state => [$bcapf, $bstatf] }; - }, - compute => sub($state) { - my ($bcapf, $bstatf) = @$state; - my $batlevel = <$bcapf>; - chomp $batlevel; - seek($bcapf, 0, 0); - my $batcharging = <$bstatf> eq "Charging\n" ? 'โก' : ''; - seek($bstatf, 0, 0); - return { - text => sprintf("๐%s%s", $batlevel, $batcharging), - color => color_thresholds(100 - $batlevel, 70, 90), - state => $state, + return sub { + my $batlevel = <$bcapf>; + chomp $batlevel; + seek($bcapf, 0, 0); + my $batcharging = <$bstatf> eq "Charging\n" ? 'โก' : ''; + seek($bstatf, 0, 0); + return { + text => sprintf("๐%s%s", $batlevel, $batcharging), + color => color_thresholds(100 - $batlevel, 100-$opts->{warn}, 100-$opts->{err}), + }; }; - } + }, }; +# TODO: remove volctl dep $linux_mods{'AUDIO'} = { period => 2, compute => sub { @@ -140,7 +144,7 @@ $linux_mods{'AUDIO'} = { txt_onoff('๐', do {`~/.local/bin/volctl vol-on`; $? == 0}), txt_onoff('๐ค', do {`~/.local/bin/volctl mic-on`; $? == 0}) }; - } + }, }; $MODS{'Linux'} = \%linux_mods; @@ -153,81 +157,90 @@ my %openbsd_mods; $openbsd_mods{'CPU'} = { period => 5, - init => sub($args) { - my $warn5 = $args->{warn5} || 1; - my $err5 = $args->{err5} || 3; - return { state => [$warn5, $err5] }; - }, - compute => sub($state) { - my $loads = `sysctl -n vm.loadavg`; - chomp $loads; - my @loads = split ' ', $loads; - return { - text => sprintf ("CPU %s/%s/%s", @loads), - color => color_thresholds($loads[1], @$state[0], @$state[1]), - state => $state, + default_opts => {'warn5' => 1, 'err5' => 3}, + mkcompute => sub($opts) { + return sub { + my $loads = `sysctl -n vm.loadavg`; + chomp $loads; + my @loads = split ' ', $loads; + return { + text => sprintf ("CPU %s/%s/%s", @loads), + color => color_thresholds($loads[1], $opts->{warn5}, $opts->{err5}), + }; }; - } + }, }; $openbsd_mods{'MEM'} = { period => 5, - compute => sub { - my $uvmexp = `vmstat -s`; - my ($mtotal) = $uvmexp =~ /([0-9]+) pages managed/; - my ($mused) = $uvmexp =~ /([0-9]+) pages active/; - $mused = 100 * $mused / $mtotal; - return { - text => sprintf("MEM %d%%", $mused), - color => color_thresholds($mused, 70, 90), + default_opts => {'warn' => 70, 'err' => 90}, + mkcompute => sub($opts) { + return sub { + my $uvmexp = `vmstat -s`; + my ($mtotal) = $uvmexp =~ /([0-9]+) pages managed/; + my ($mused) = $uvmexp =~ /([0-9]+) pages active/; + $mused = 100 * $mused / $mtotal; + return { + text => sprintf("MEM %d%%", $mused), + color => color_thresholds($mused, $opts->{warn}, $opts->{err}), + }; }; - } + }, }; sub render_iface_openbsd($ifaces, $ifname) { my $iface = $ifaces->{$ifname}; my $extra = $iface->{'ip'} ? " $iface->{'ip'}" : ''; my $is_wireless = exists $iface->{'wifi'}; - my $is_up = $iface->{'up'}; - if ($is_wireless and $is_up) { + if (exists $iface->{'wifi_ssid'}) { $extra .= ' (' . $iface->{'wifi_ssid'} . ', ' . $iface->{'wifi_signal'} . ')'; } - return txt_onoff($is_wireless ? '๐ถ' : '๐', $is_up) . $extra; + return txt_onoff($is_wireless ? '๐ถ' : '๐', $iface->{'up'}) . $extra; } $openbsd_mods{'NET'} = { period => 4, - compute => sub { + mkcompute => sub { my @wired = (); my @wireless = (); - my %ifaces; my $ifname; for (`ifconfig`) { if (/^([a-z]+[0-9]+):/) { $ifname = $1; - $ifaces{$ifname} = {}; - $ifaces{$ifname}{'up'} = /UP/; - } elsif (/media: (.*)/) { - $ifaces{$ifname}{'media'} = $1; - } elsif (/ieee80211:.* join "([^"]*)".* ([0-9]+%)/) { - $ifaces{$ifname}{'wifi'} = 1; - $ifaces{$ifname}{'wifi_ssid'} = $1; - $ifaces{$ifname}{'wifi_signal'} = $2; - } elsif (/inet ([0-9.]+)/) { - $ifaces{$ifname}{'ip'} = $1; + } elsif (/media: (Ethernet|IEEE802.11)/) { + if ($1 eq 'Ethernet') { + push @wired, $ifname; + } else { + push @wireless, $ifname; + } } } - foreach my $ifname (keys %ifaces) { - next if $ifname =~ /^lo[0-9]+/; - my $iface = $ifaces{$ifname}; - next unless $iface->{'ip'} || $iface->{'media'}; - if ($iface->{'wifi'}) { - push @wireless, $ifname; - } else { - push @wired, $ifname; + + return sub { + my %ifaces; + my $ifname; + for (`ifconfig`) { + if (/^([a-z]+[0-9]+):/) { + if (grep { $_ eq $1 } @wired, @wireless) { + $ifname = $1; + $ifaces{$ifname}{'up'} = /UP/; + } else { + $ifname = undef; + } + } + next unless defined($ifname); + if (/ieee80211:/) { + $ifaces{$ifname}{'wifi'} = 1; + if (/join "([^"]*)".* ([0-9]+%)/) { + $ifaces{$ifname}{'wifi_ssid'} = $1; + $ifaces{$ifname}{'wifi_signal'} = $2; + } + } elsif (/inet ([0-9.]+)/) { + $ifaces{$ifname}{'ip'} = $1; + } } - } - return { text => join ' ยท ', map { render_iface_openbsd(\%ifaces, $_) } sort(@wired), @wireless }; - } + return { text => join ' ยท ', map { render_iface_openbsd(\%ifaces, $_) } @wired, @wireless }; + }; + }, }; sub obsd_get_sysctl($mib) { @@ -236,22 +249,19 @@ sub obsd_get_sysctl($mib) { } $openbsd_mods{'BAT'} = { period => 3, - init => sub($args) { - my $warn = $args->{warn} || 30; - my $err = $args->{err} || 10; - return { state => [$warn, $err] }; - }, - compute => sub($state) { - my $batcharging = obsd_get_sysctl('hw.sensors.acpibat0.raw0') eq "0" ? 'โก' : ''; - my $batmax = obsd_get_sysctl('hw.sensors.acpibat0.watthour0'); - my $batcur = obsd_get_sysctl('hw.sensors.acpibat0.watthour3'); - my $batlevel = 100 * $batcur / $batmax; - return { - text => sprintf("๐%d%%%s", $batlevel, $batcharging), - color => color_thresholds(100-$batlevel, 100-@$state[0], 100-@$state[1]), - state => $state, + default_opts => {'warn' => 30, 'err' => 10}, + mkcompute => sub($opts) { + return sub { + my $batcharging = obsd_get_sysctl('hw.sensors.acpibat0.raw0') eq "0" ? 'โก' : ''; + my $batmax = obsd_get_sysctl('hw.sensors.acpibat0.watthour0'); + my $batcur = obsd_get_sysctl('hw.sensors.acpibat0.watthour3'); + my $batlevel = 100 * $batcur / $batmax; + return { + text => sprintf("๐%d%%%s", $batlevel, $batcharging), + color => color_thresholds(100-$batlevel, 100-$opts->{warn}, 100-$opts->{err}), + }; }; - } + }, }; $openbsd_mods{'AUDIO'} = { @@ -264,7 +274,7 @@ $openbsd_mods{'AUDIO'} = { txt_onoff('๐', $muted == 0), txt_onoff('๐ค', $micmuted == 0) }; - } + }, }; $MODS{'OpenBSD'} = \%openbsd_mods; @@ -291,10 +301,16 @@ $MODS{'_common'} = \%common_mods; my $OS = `uname -s`; chomp $OS; my %os_mods = ( %{$MODS{$OS}}, %{$MODS{'_common'}} ); +my @user_mods; -# my @mods = ('CPU', 'MEM', 'NET', 'BAT', 'AUDIO', 'DATE'); -my @mods = @ARGV; -my %cache; +my $profile = 0; +foreach my $arg (@ARGV) { + if ($arg eq '--profile') { + $profile = 1; + } else { + push @user_mods, $arg; + } +} sub render_section($result) { my $color = $result->{color}; @@ -304,31 +320,28 @@ sub render_section($result) { my $separator = render_section({text => 'ยท'}); -my $profile = 0; -foreach my $arg (@ARGV) { - if ($arg eq '--profile') { - $profile = 1; - } +# Remove opts from @user_mods +# Call each module's mkcompute subroutine if it exists +foreach my $modspec (@user_mods) { + my ($modname, @modopts) = split(',', $modspec); + $modspec = $modname; + my $mod = $os_mods{$modname} + or die "ERROR: '$modname' is not a valid module.\n"; + my $default_opts = $mod->{default_opts} || {}; + my %opts = (%$default_opts, map { split '=', $_ } @modopts); + $mod->{compute} = $mod->{mkcompute}(\%opts) if exists $mod->{mkcompute}; } printf("{ \"version\": 1 }\n[\n"); -# Call each module's init subroutine -# Remove args from @mods -foreach my $modspec (@mods) { - my ($modname, @modargs) = split ',', $modspec; - my $mod = $os_mods{$modname}; - my %args = (map { split '=', $_ } @modargs); - $cache{$modname} = &{$mod->{init}}(\%args) if $mod->{init}; - $modspec = $modname; -} - +my @timedeltas; +my %cache; my $counter = 0; while (1) { my @sections; my (undef, $time0) = gettimeofday() if $profile; - foreach my $modname (@mods) { + foreach my $modname (@user_mods) { my $mod = $os_mods{$modname}; my (undef, $time) = gettimeofday() if $profile; my $result = $cache{$modname}; @@ -339,7 +352,10 @@ while (1) { push(@sections, render_section($result)); show_time_delta($modname, $time, $counter % $mod->{period} == 0) if $profile; } - show_time_delta('TOTAL', $time0, 0) if $profile; + if ($profile) { + push @timedeltas, show_time_delta('TOTAL', $time0, 0); + printf STDERR "AVG/ROUND: %.2f ms\n", (sum @timedeltas) / @timedeltas; + } printf("[%s],\n", join(",$separator,", @sections)); |