http://paperlined.org/dev/perl/parse/parse_wtmp.pl
#!/usr/bin/perl
use strict;
use warnings;
#use Data::Dumper(); use Perl::Tidy;
#sub Dumper {perltidy source=>\(Data::Dumper::Dumper@_),destination=>\(my$t);$t}
use Data::Dumper;
$Data::Dumper::Quotekeys = 0;
$Data::Dumper::Sortkeys = 1;
print Dumper [ parse_wtmp() ];
sub parse_wtmp {
# 'c2ph' was used to generate this,
# with help from dump_size_offset() to confirm/fix alignment:
# http://paperlined.org/dev/perl/modules/related_modules/more_complex_than_pack.html
my ($fnames, $ftypes) = unzip(qw_comments(<<'EOF'));
type l # short int type of login (0 through 9); see wtmp(5)
pid i # pid_t PID of login process
line Z32 # char[UT_LINESIZE] device name of TTY
id Z4 # char[4] terminal name suffix, or inittab ID
user Z32 # char[UT_NAMESIZE] username
host A256 # char[UT_HOSTSIZE] hostname (if remote login)
exit l # struct exit_status exit status of a process when ut_type==DEAD_PROCESS
session l # long int session ID, used for windowing
tv_sec i # struct timeval.tv_sec time this entry was made
tv_usec i # struct timeval.tv_usec
addr_v6 a16 # int32_t[4] internet address of remote host; IPv4 address uses just ut_addr_v6[0]
unused Z20 # char[20] reserved for future use
EOF
$ftypes = join(" ", @$ftypes);
open my $fin, '<', '/var/log/wtmp' or die $!;
local $/ = \(length pack $ftypes); # read fixed-length records
my @entries;
while (<$fin>) {
my %ent;
@ent{@$fnames} = unpack $ftypes, $_;
$ent{tv_human} = ~~localtime $ent{tv_sec};
if (!grep {$_} unpack 'x4L3', $ent{addr_v6}) { # if the last 12 bytes are all-zero, then it's an IPv4 address
$ent{addr_human} = join '.', unpack 'C4', $ent{addr_v6};
} else {
$ent{addr_human} = join ':', unpack 'H4H4H4H4H4H4H4H4', $ent{addr_v6};
}
push @entries, \%ent;
}
return @entries;
}
# split [1,'a',2,'b',3,'c'] into [[1,2,3],['a','b','c']]
sub unzip {my($i,$j)=(0,0); [grep{++$i%2}@_],[grep{$j++%2}@_]}
# Like qw[...], but it allows use of comments (hash symbol).
sub qw_comments {local$_=shift;s/\s+#.*//gm;split}
Generated by GNU enscript 1.6.4.