Persib has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

I am trying to understand lastlogin.pl (a sample code from book Unix Programming - Programming Perl).
This sample code is working fine in my linux SuSe (after i changed $lastlog_t = "L a8 a256" to $lastlog_t="L a32 a256", and i know this after i did the super search at perlmonks and found this View last login times of everyone on your system).

So i have a conclusion that each record (fixed length records database) in /usr/var/lastlog has a "fixed" length, and the length of each record is 292 bytes

$lastlog_t = "L a32 a256"; #(your machine maybe differ); $LEN = length(pack($lastlog_t,0, '', '')); print "length = $LEN \n";

Then, in my shell i did 'ls -l /var/log/lastlog', and i see that the size of lastlog is 146292 bytes.

So the sum of all records = 146292 / 292 = 501 records .

i am not sure about this, because when i ran lastlogin.pl there are only 16 UIDs.

i am curious, i used a sample script like this :

setpwent(); while(@list = getpwent()) { ($login,$home) = @list[0,7]; print "home directory for $login is $home \n"; } endpwent();
and still only display 16 records.

And now im confused, my /var/log/lastlog's size should be 4672 bytes not 146292 bytes !
or did i miss something ? (btw i am really new to this linux world)
Do you have any clues ?

You can see lastlogin.pl in Persib's scratchpad

Update : here's the lastlogin.pl

#!/usr/bin/perl $lastlog_t = "L a32 a256"; #(your machine maybe differ); $LEN = length(pack($lastlog_t,0, '', '')); open(LASTLOG,'/var/log/lastlog') || die "can't open lastlog file: $! \ +n"; open(PASSWD,'/etc/passwd') || die "can't open passwd file: $! \n"; @month=(Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec); while($varrec = <PASSWD>) { ($login,$pass,$uid,$gid,$gcos) = split(/:/,$varrec); $fullname = $gcos; # $fullname = ~ s/^[^,]*--(.*)\(.*/$1/ || $fullname =~ s/,.*//; #try to uncomment code line above, cuz i got weird result if ($fullname =~ /&/) { #u dont want to know $name = $login; substr($name,0,1) = ~ tr/a-z/A-Z/; $fullname = ~ s/&/$name/; } seek(LASTLOG,$uid * $LEN, 0); read(LASTLOG,$lastlog,$LEN); ($time,$line,$host) = unpack($lastlog_t,$lastlog); push(@records, "$time:$login:$uid:$fullname"); } @records = sort numerically @records; foreach $record(@records) { ($time,$login,$uid,$fullname) = split(/:/,$record); if ($time) { ($sec,$min,$hour,$mday,$mon,$year) = localtime($time); $year +=1900; $date = "$mday $month[$mon] $year"; } else { $date = "never"; } write; } sub numerically {$a <=> $b; } format top = Uid Login Full Name Last Login . format STDOUT = @>>>>> @<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @|||||||||||| $uid $login $fullname $date

thanks, and sorry for my english

Replies are listed 'Best First'.
Re: Fixed length record and lastlog's size
by Fletch (Bishop) on Dec 01, 2005 at 19:10 UTC

    UNIX filesystems support files with "holes" in them. They're contiguous blocks of NULL \0 characters which don't take up any space on disk.

    Usually on most Linux distros the first user account gets a UID of 501. Since the lastlog records are indexed by UID you're seeing a file that looks like it contains that many entries, but what's really happening under the hood is that the first part of the file contains the entries for the system accounts, then holes (again these take up no real space on disk but show up as part of the file's size), then the info for UID 501. Really your file is using 4672 bytes on disk, it's just the official size is the 146292.

Re: Fixed length record and lastlog's size
by GrandFather (Saint) on Dec 01, 2005 at 19:43 UTC

    Note that the contents of scratch pads tend to be transient. Rather than referring to code in your scratch you would be better to included it in your node in readmore tags (<readmore title="lastlogin.pl"> ... </readmore>).

    Update: s/readme/readmore/g :)


    DWIM is Perl's answer to Gödel