#!/usr/bin/perl # # $Header: /home/cvs/my-acpi/battery.pl,v 1.7 2005/07/25 14:11:37 garry Exp $ # # Copyright (c) 2005 Garry T. Williams # Modified by: Spencer Shimko # set the CPU frequency governors here my $CPUGOV_BAT = 'ondemand'; my $CPUGOV_AC = 'performance'; use warnings; use strict; use Getopt::Std qw/getopts/; use Pod::Usage qw/pod2usage/; our $VERSION = qw$Revision: 1.7 $[1]; #--------------------------------------------------------------------- # process command line #--------------------------------------------------------------------- my %options; my ($pgm) = $0 =~ m!([^/]+)\z!; getopts('hdfi:s:t:a:b:', \%options) || pod2usage(); pod2usage(verbose => 1) if $options{h}; warn "$pgm: entered\n" if $options{d}; if (defined $options{t}) { pod2usage(verbose => 1) unless $options{t} =~ /^\d+\z/; warn "$pgm: threshold specified: $options{t}\n" if $options{d}; } else { $options{t} = 1; } if (defined $options{i}) { die "$pgm: invalid info file: $options{i}: $!\n" unless -r $options{i}; warn "$pgm: using info file $options{i}\n" if $options{d}; } if (defined $options{s}) { die "$pgm: invalid state file: $options{s}: $!\n" unless -r $options{s}; warn "$pgm: using state file $options{s}\n" if $options{d}; } $options{a} = '/var/run/ac_adapter_state' unless $options{a}; if (defined $options{b}) { unless ($options{b} =~ /^\d+\z/ && $options{b} >= 1 && $options{b} <= 8) { warn "invalid -b (brightness) option\n"; pod2usage(verbose => 1); } } else { $options{b} = 1; } #--------------------------------------------------------------------- # globals #--------------------------------------------------------------------- my $NO_SHUT = '/no_shutdown'; my $BRIGHT = '/proc/acpi/sony/brightness'; my $CPUFREQ = '/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor'; my $INFO = $options{i} || '/proc/acpi/battery/BAT0/info'; my $STATE = $options{s} || '/proc/acpi/battery/BAT0/state'; my $bright = "8"; my $capacity; my $remaining; #--------------------------------------------------------------------- # Determine current battery situation. Process AC adapter state # transitions. Exit unless battery is charging. #--------------------------------------------------------------------- { open my $fh, $STATE or die "can't open $STATE: $!\n"; my %state; while (<$fh>) { chomp; my ($key, $val) = split /:\s+/; $state{$key} = $val; } if ($options{d}) { warn "$pgm: battery state:\n"; while (my ($key, $val) = each %state) { warn "\t`$key': `$val'\n"; } } die "$pgm: no `remaining capacity' found in $STATE\n" unless exists $state{'remaining capacity'}; die "$pgm: no `charging state' found in $STATE\n" unless exists $state{'charging state'}; $remaining = (split ' ', $state{'remaining capacity'})[0]; my $ac_state; if (-e $options{a}) { $ac_state = get_last_ac_adapter_state($options{a}); } if ($state{'charging state'} eq 'discharging') { if (((!defined $ac_state)|| $ac_state) || defined $options{f}) { warn "$pgm: updating AC adapter state $options{a} to unplugged\n" if $options{d}; set_last_ac_adapter_state($options{a}, 0); warn "$pgm: reducing brightness\n" if $options{d}; decrease_brightness(); warn "$pgm: switching to $CPUGOV_BAT cpu frequency governor\n" if $options{d}; cpu_gov($CPUGOV_BAT); } warn "$pgm: battery is discharging\n" if $options{d}; } else { unless (((defined $ac_state) && $ac_state) && !(defined $options{f})) { warn "$pgm: updating AC adapter state $options{a} to plugged in\n" if $options{d}; set_last_ac_adapter_state($options{a}, 1); warn "$pgm: increasing brightness\n" if $options{d}; increase_brightness(); warn "$pgm: switching to $CPUGOV_AC cpu frequency governor\n" if $options{d}; cpu_gov($CPUGOV_AC); } exit 0; } } { open my $fh, $INFO or die "can't open $INFO: $!\n"; my %info; while (<$fh>) { chomp; my ($key, $val) = split /:\s+/; $info{$key} = $val; } if ($options{d}) { warn "$pgm: battery info:\n"; while (my ($key, $val) = each %info) { warn "\t`$key': `$val'\n"; } } die "$pgm: no `last full capacity' found in $INFO\n" unless exists $info{'last full capacity'}; $capacity = (split ' ', $info{'last full capacity'})[0]; } #--------------------------------------------------------------------- # Determine percentage of capacity remaining # # Exit, if remaining capacity > 1% #--------------------------------------------------------------------- my $percent = ($remaining / $capacity) * 100; warn sprintf "$pgm: battery remaining: %.1f%%\n", $percent if $options{d}; exit 0 unless $percent < $options{t}; #--------------------------------------------------------------------- # shut down now #--------------------------------------------------------------------- if (-f $NO_SHUT) { warn "$pgm: no shutdown; remove $NO_SHUT\n"; exit 0; } warn "$pgm: shutting down -- battery critical!\n"; system '/sbin/shutdown', '-h', 'now' and die "$pgm: shutdown command failed with status $?\n"; #--------------------------------------------------------------------- # get_last_ac_adapter_state() # # Obtain the last known AC adapter state from file. # # Input: file name containing state # # Return: 1, if last known was charging # 0, if last known was discharging or if error #--------------------------------------------------------------------- sub get_last_ac_adapter_state { my ($file) = @_; my $fh; unless (open $fh, $file) { warn "$pgm: can't open $file: $!\n"; return 0; } my $state = <$fh>; chomp $state if defined $state; return(defined $state ? $state : 0); } #--------------------------------------------------------------------- # set_last_ac_adapter_state() # # Set the new state of the AC adapter. # # Input: file name to contain new state # new state # # Output: updated AC adapter state in file # # Return: undef, if error # true, if success #--------------------------------------------------------------------- sub set_last_ac_adapter_state { my ($file, $new_state) = @_; my $fh; unless (open $fh, "> $file") { warn "$pgm: can't open $file: $!\n"; return; } print $fh "$new_state\n"; unless (close $fh) { warn "$pgm: close($file): $!\n"; return; } 1; } #--------------------------------------------------------------------- # increase_brightness() # # This function depends on the sony_acpi module to create and handle # the /proc/acpi/sony/brightness file. # # Input: none # # Output: true on success # false on error #--------------------------------------------------------------------- sub increase_brightness { my $fh; unless (open $fh, "> $BRIGHT") { warn "$pgm: can't open $BRIGHT: $!\n"; return; } print $fh $bright; unless (close($fh)) { warn "$pgm: close($BRIGHT): $!\n"; return; } 1; } #--------------------------------------------------------------------- # decrease_brightness() # # This function depends on the sony_acpi module to create and handle # the /proc/acpi/sony/brightness file. # # Input: none # # Output: true on success # false on error #--------------------------------------------------------------------- sub decrease_brightness { my $fh; unless (open $fh, "> $BRIGHT") { warn "$pgm: can't open $BRIGHT: $!\n"; return; } print $fh $options{b}; unless (close($fh)) { warn "$pgm: close($BRIGHT): $!\n"; return; } 1; } #--------------------------------------------------------------------- # cpu_gov(governor) # # # Output: true on success # false on error #--------------------------------------------------------------------- sub cpu_gov { my $fh; my ($gov) = @_; unless (open $fh, "> $CPUFREQ") { warn "$pgm: can't open $CPUFREQ: $!\n"; return; } print $fh $gov; unless (close($fh)) { warn "$pgm: close($CPUFREQ): $!\n"; return; } 1; } __END__ =pod =head1 NAME battery - shutdown system on critically low battery =head1 SYNOPSIS B [ B<-h> ] [ B<-f> ] [ B<-d> ] S<[ B<-t> I ]> S<[ B<-i> I ]> S<[ B<-s> I ]> S<[ B<-a> I ]> S<[ B<-b> I ]> =head1 DESCRIPTION This program will call shutdown(1), if the battery capacity drops critically low. This program is designed to be called by acpid(1). That is accomplished by specifying it in the F directory. It assumes that it is called in response to a "battery" event. Here's an example of a F file: event=battery.* action=/etc/acpi/actions/battery -dt5 If the file F exists, then this program simply exits, even when the threshold has been reached. Note: The Sony Vaio FS640/W with Fedora Core 4 does not generate AC adapter events. It does generate a battery event when the AC adapter is plugged in or unplugged. So we will use this event handler to keep track of the transitions and adjust the screen brightness upon a transition. This functionality depends on the sony_acpi module. That module creates F to allow changing the brightness in eight steps from 1 to 8. =head1 OPTIONS =over =item B<-h> Print help information. =item B<-f> Force update to system state. Useful for boot scripts where the state file might not accurately represent state transitions. =item B<-d> Print debugging information. The acpid(1) daemon will copy this to F. =item B<-t> I This option specifies the threshold of battery capacity at which the shutdown(1) program will be called. The I is specified as a whole percentage. If the B<-t> option is omitted, it defaults to 1 (percent). =item B<-i> This option is mainly for testing. It specifies the info file path. If B<-i> is omitted, it defaults to F. =item B<-s> This option is mainly for testing. It specifies the state file path. If B<-s> is omitted, it defaults to F. =item B<-a> This option specifies the AC adapter state file. This file will allow this handler to detect transitions with the AC adapter and adjust brightness in response to transitions. If the B<-a> option is omitted, then it will default to F. =item B<-b> This option specifies the brightness setting to use when the AC adapter is unplugged. The value must be an integer from 1 to 8, 8 being the brightest. If this option is omitted, it will default to 5. When the adapter is plugged back in, the brightness will be set to 8. =back =head1 FILES =over =item F This file contains the last full capacity of the battery. That value is used along with the remaining capacity specified in the state file to compute a percentage of remaining capacity. This file name can be changed with the B<-i> option. =item F This file contains the charging state and the remaining capacity. This program exits immediately, unless the charging state is "discharging". The remaining capacity value is used along with the last full capacity to compute the percentage of battery capacity remaining. This file name can be changed with the B<-s> option. =item F If this file exists, then the program will exit instead of calling shutdown(1) even when the threshold is reached. =item F This file is used to keep track of AC adapter transitions. We need to do this because the Sony FS640/W with FC4 doesn't issue ac_adapter events when the adapter is plugged in or unplugged. Those actions do trigger battery events, so we keep track of that here. This file name can be changed with the B<-a> option. =back =head1 SEE ALSO acpid(1), shutdown(1), the sony_acpi driver source is at http://popies.net/sonypi/sony_acpi.tar.gz =head1 AUTHOR Garry T. Williams, Egtwilliams@gmail.comE =head1 LICENSE $Header: /home/cvs/my-acpi/battery.pl,v 1.7 2005/07/25 14:11:37 garry Exp $ Copyright (c) 2005 Garry T. Williams This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut