#!/usr/bin/perl # copyright 2006 Petr Rockai # distributable under the terms of BSD license # DISCLAIMER: never run unless you know what you are doing... tee hee # (there is of course NO WARRANTY of any kind and some of the things # this does to your system may screw up, at minimum, the running kernel) use strict; # put your device id's here, as seen in lspci output # if you don't have some of them, put "not available" or something # that will make them noops and their status lines will be empty my $sndid = "00:1e.2"; my $ethid = "01:00.0"; my $wlanid = "02:03.0"; my $modemid = "00:1e.3"; my $smctlid = "02:01.5"; my $pcmciaid = "02:01.0"; my $isaid = "00:1f.0"; # how to lock your session # switching to a vt and issuing lockvc could be an option # assuming you don't have sysrq enabled (i have) # umounting crypto devices could be another, but you'd have to handle # post-resume remount sub lockscreen { system "dcop kdesktop KScreensaverIface lock"; } sub strstate { my $i = shift; return "on" if ( $i == 0 ); return "off" if ( $i == 2 ); return "unknown"; } sub setstate { my $x = shift; my $i = 0; # we loop because we don't order properly and some changes may fail while ($i < 5) { print "D: committing power state changes, try $i\n"; for (keys %$x) { system "sudo sh -c 'echo -n $$x{$_} > $_/state' 2> /dev/null"; } my $ok = 1; my %current = %{&getstate}; for (keys %$x) { $ok = 0 if ( $current{$_} != $$x{$_} ); } return if $ok; ++$i; } print "W: some state changes failed\n"; } sub getstate { my %state; for (split /\0/, `find /sys/devices -name power -print0`) { $state{$_} = `cat "$_/state"`; chomp $state{$_}; } return \%state; } sub dpm { my $wha = shift; my $grep = shift; my %state = %{&getstate}; for (grep {/$grep/} (keys %state)) { $state{$_} = $wha eq "off" ? 2 : 0; } setstate \%state; } sub wlan { dpm shift, $wlanid; } sub eth { dpm shift, $ethid; } sub usb { dpm shift, "usb"; } sub snd { dpm shift, $sndid; } sub pcmcia { dpm shift, $pcmciaid; } sub smctl { dpm shift, $smctlid; } sub isa { dpm shift, $isaid; } # to make this work, you need to run: # echo 1 > /proc/sys/vm/block_dump sub parsedump { my $recent = shift; my %procs; for (split /\n/, $recent) { /([a-zA-Z_]+)\(([0-9]+)\)/; my $name = $1; my $pid = $2; $procs{$name} .= ", $pid" unless $procs{$name} =~ /$pid/; } for (keys %procs) { my $pids = $procs{$_}; $pids =~ s/^, //; print "$_: $pids\n"; } } sub recentreads { &parsedump( `dmesg | grep "READ block"` ); } sub recentwrites { &parsedump( `dmesg | grep "WRITE block"` ); } sub modem { my $wha = shift; my $grep = shift; my %state = %{&getstate}; for (grep { !/unknown codec/ } (grep {/$modemid/} (keys %state))) { $state{$_} = $wha eq "off" ? 2 : 0; } setstate \%state; } # we currently only handle one battery (i only have one) sub bat { my $st = `cat /proc/acpi/battery/BAT0/state`; my $acpi = `acpi | head -n1`; $acpi =~ s,^.*?Battery [0-9]: ,,; chomp $acpi; $st =~ /^present rate:.*?([0-9]+)/sm; my $rate = $1; $st =~ /.*?^present voltage:.*?([0-9]+)/sm; my $volt = $1; $st =~ /.*?^remaining capacity:.*?([0-9]+)/sm; my $capa = $1; printf "battery 0: $acpi, rate %.2f Watt, capacity %d mAh\n", (($rate * $volt) / (1000*1000)), $capa; } sub suspend { my $wha = shift; my %state = %{&getstate}; my %allon; for ( keys %state ) { $allon{$_} = 0; } # wake all devices up, kernel doesn't seem # to like them half asleep when suspending setstate \%allon; print "locking session...\n"; &lockscreen; print "suspending...\n"; system "sudo sh -c 'sync; chvt 1; echo -n $wha > /sys/power/state'"; setstate \%state; # restore power states, kernel tends to screw those up? # chvt voodoo to wake i810 up... no, i have no idea -why- ... but it works system "sudo sh -c 'chvt 1; chvt 7; chvt 1; chvt 7'"; # laptop-mode-tools suck at restoring this one (or someone else, who knows) # (this sets spindown timeout to 5 seconds (!)) system "sudo hdparm -S 1 /dev/hda"; } sub show { my %state = %{&getstate}; &hdstate; print "eth:"; print " " . strstate( $state{$_} ) for (grep { /$ethid/ } (keys %state)); print "\nwlan:"; print " " . strstate( $state{$_} ) for (grep { /$wlanid/ } (keys %state)); print "\nusb:"; print " " . strstate( $state{$_} ) for (grep { /usb/ } (keys %state)); print "\nsnd:"; print " " . strstate( $state{$_} ) for (grep { /$sndid/ } (keys %state)); print "\nmodem:"; print " " . strstate( $state{$_} ) for (grep { !/unknown codec/ } (grep {/$modemid/} (keys %state))); print "\nsmctl:"; print " " . strstate( $state{$_} ) for (grep { /$smctlid/ } (keys %state)); print "\npcmcia:"; print " " . strstate( $state{$_} ) for (grep { /$pcmciaid/ } (keys %state)); print "\nisa:"; print " " . strstate( $state{$_} ) for (grep { /$isaid/ } (keys %state)); print "\n"; } sub cpufreq { my $freqi = `cat /proc/cpuinfo | grep '^cpu MHz'`; $freqi =~ /([0-9]+)/; my $freq = $1; my $governor = `cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor`; chomp $governor; print "cpu: $freq MHz, $governor\n"; } sub temp { my $tempi = `cat /proc/acpi/thermal_zone/THM/temperature`; $tempi =~ /([0-9]+ C)/; my $temp = $1; print "temperature: $temp\n"; } sub hdstate { my $statei = `sudo hdparm -C /dev/hda`; $statei =~ /drive state is: *([^ \n\t]+)/; my $state = $1; if ( $state eq "active/idle" ) { # avoid spinning up the drive my $smarti = `sudo smartctl -A /dev/hda`; $smarti =~ /Temperature_Celsius.*?Always *- *([0-9]+)/; my $temp = $1; $smarti =~ /Start_Stop_Count.*?Always *- *([0-9]+)/; my $startstop = $1 / 1000; printf "hda: $state, $temp C, %dk spinups\n", $startstop; } else { printf "hda: $state\n"; } } sub summary { print "# system\n"; &cpufreq; &temp; &bat; print "\n# devices\n"; &show; print "\n# recent reads\n"; &recentreads; print "\n# recent writes\n"; &recentwrites; } my $wha = shift @ARGV; if ($wha eq "mem" || $wha eq "disk") { suspend($wha); } elsif ($wha eq "usb") { usb(shift @ARGV); } elsif ($wha eq "eth") { eth(shift @ARGV); &show; } elsif ($wha eq "snd") { snd(shift @ARGV); &show; } elsif ($wha eq "modem") { modem(shift @ARGV); &show; } elsif ($wha eq "wlan") { wlan(shift @ARGV); &show; } elsif ($wha eq "pcmcia") { pcmcia(shift @ARGV); &show; } elsif ($wha eq "smctl") { smctl(shift @ARGV); &show; } elsif ($wha eq "isa") { isa(shift @ARGV); &show; } elsif ($wha eq "dump") { my %state = %{&getstate}; for (sort (keys %state)) { print "$_: $state{$_}\n"; } } elsif ($wha eq "show") { &show; } elsif ($wha eq "bat") { &bat; } elsif ($wha eq "summary") { &summary; }