aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillermo Ramos2025-09-18 00:12:30 +0200
committerGuillermo Ramos2025-09-26 00:48:59 +0200
commita7da12cb307e6dbe33cb75cc9e6784be31ff4988 (patch)
treecf99cd5bf9627e107774fba639731f94996be588
parente776589416f62cba277f177ecb77e91b1b50551a (diff)
downloadcli-a7da12cb307e6dbe33cb75cc9e6784be31ff4988.tar.gz
capibarra: options parsed in compile time
-rwxr-xr-xcapibarra256
1 files changed, 136 insertions, 120 deletions
diff --git a/capibarra b/capibarra
index 5a3c969..8040883 100755
--- a/capibarra
+++ b/capibarra
@@ -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));