Skip to content

Instantly share code, notes, and snippets.

@pmakholm
Created May 18, 2009 13:53
Show Gist options
  • Save pmakholm/113475 to your computer and use it in GitHub Desktop.
Save pmakholm/113475 to your computer and use it in GitHub Desktop.
Script to summarize working time based om utmp entries
#!/usr/bin/perl
=license
"THE BEER-WARE LICENSE" (Revision 42):
<peter@makholm.net> wrote this file. As long as you retain this notice
you can do whatever you want with this stuff. If we meet some day, and
you think this stuff is worth it, you can buy me a beer in return
Peter Makholm
=cut
=changelog
2008-12-12: Initial version
2008-12-15: Add license and cahngelog
Bug fixes (Thanks to Jesper Nyerup)
- Handle missing wtmp.1
- handle crashes
2009-05-18: Enhancements from Jesper Nyerup:
- Default arguments
- Support for file with aditional time
2009-09-23: Correct handling of slacking af fraction of an hour
=cut
use strict;
use warnings;
use Carp;
use POSIX qw(strftime);
use Sys::Utmp;
use Time::ParseDate;
if ( @ARGV == 0 ) {
push( @ARGV, "00:00 a week ago tomorrow" );
push( @ARGV, "now" );
}
die "Usage: $0 <from time> <to time>\n" unless @ARGV == 2;
# Haven't found a pure perl time parser as flexible as gnu date(1):
my ( $from, $to ) = map qx(/bin/date -d "$_" +%s), @ARGV;
chomp( $from, $to );
# My wtmp is rotated each month. can the last two:
my @wtmps = grep { -e $_ } qw( /var/log/wtmp.1 /var/log/wtmp );
die "No wtmp found!" unless @wtmps;
my $wtmp = Sys::Utmp->new(
Filename => shift( @wtmps ),
);
my @additions;
if ( open my $addfile, "<", $ENV{HOME} . "/.worktime" ) {
while (<$addfile>) {
s/#.*//;
chomp;
next unless length;
my @list = split /\s+/, $_, 3;
my $time = parsedate( $list[0] );
my $delta = $list[1] * 3600;
next if $from > $time;
next if $to < $time;
push @additions, [ $time, $time + $delta, $list[2] ];
}
}
sub next_wtmp {
my $ent = $wtmp->getutent;
while ( !defined($ent) && @wtmps ) {
$wtmp->endutent;
$wtmp->utmpname( shift(@wtmps) );
$ent = $wtmp->getutent;
}
return $ent;
}
# Assume that machine was on at $from:
my @uptimes = [ $from, undef ];
while ( my $ut = next_wtmp() ) {
my ($time, $user) = ( $ut->ut_time, $ut->ut_user );
next if $time < $from;
last if $time > $to;
push @uptimes, [ $time, undef ] if $user eq 'reboot';
$uptimes[-1][1] = $time if $user eq 'shutdown';
}
# Forget the assumption if we didn't shutdown before reboot;
shift @uptimes if !defined( $uptimes[0][1] );
# If we were booted at $to, use this as endtime
$uptimes[-1][1] //= $to;
# Merge aditions in
@uptimes = sort { $a->[0] <=> $b->[0] } @uptimes, @additions;
my $total;
while (my $work = shift @uptimes) {
my $boot = $work->[0];
my $shutdown = $work->[1];
my $addition = exists( $work->[2] ) ? "*" : "";
# If the computer crashes it never writes an shutdown event.
# Assume that the computer is booted right away and just scan for the
# next shutdown.
while ( !defined($shutdown) ) {
$work = shift @uptimes;
$shutdown = $work->[1];
}
my $time = $shutdown - $boot;
printf "%15s - %s%02d:%02d %1s\n",
strftime("%a %b %e %H:%M", localtime $boot),
( $time > 0 ? "worked " : "slacked -" ),
int( abs($time) / 3600 ),
int( (abs($time) % 3600) / 60),
$addition;
$total += $time;
}
print " -----\n";
printf " total worked %02d:%02d\n",
int( $total / 3600 ),
int( ($total % 3600) / 60);
__END__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment