
Listing 1: lastlogin

#
# Thomas Richter
# Print the last logins in human readable form
#
# lastlogin [-cnumber] [-l units|-r units|-u units] [-h hosts] \
#        [-t ttys] [-f file] [user...]"
#
# Following flags are supported:
# -c number: List all users which have an invalid login count
#        greater or equal than number.
# -f file: Read input from file, default is /etc/security/lastlog.
# -h hostlist: List all users who logged on from a host in hostlist.
#        Hostlist is a comma separated list of host names.
#        Default is any host.
# -l units: List all users who have logged on during the last n units.
#        If units is zero, list the last logged on  details of every entry.
#        Displayed fields are time_last_login, tty_last_login,
#        host_last_login and unsuccessful_login_count.
# -r units: List all users whose log on failed on during the last n units.
#        If units is zero, list refused logged data of every entry.
#        Displayed fields are time_last_unsuccessful_login,
#        tty_last_unsuccessful_login, host_last_unsuccessful_login
#        and unsuccessful_login_count.
# -t terminallist: List all users who logged on from a terminal
#        in terminallist. Terminallist is a comma separated list
#        of terminal names. Default is any terminal.
# -u units: List all users who have not logged on for more than n units.
#        Displayed fields are time_last_login, tty_last_login,
#        host_last_login and unsuccessful_login_count.
#
# Parameter can be user names.
# All options are and conditions.
# Terminallist and hostlist are mapped against unsuccessful terminal or
# host names if flag -r is specified.



PATH=/usr/bin

typeset -i counter=-1 refused=-1 last=-1 current unused=-1
infile=/etc/security/lastlog
# Sort for last/unused login time
sortfield='+7'
current=`/var/adm/local/cvttime`
ttylist=''
hostlist=''

# Check if the first parameter is acceptable flags l, r and u are mutually
# exclusive. Also convert a unit parameter into seconds. Acceptable units
# are M (minutes), h (hours), d (days) and m (month). Others units are mapped
# to days.
function checkarg
{
    if [[ $2 > -1 || $3 > -1 ]]
    then
         print -u2 "usage ${0##*/}: parameter combination invalid"
         exit 2
    fi
    unit=`expr $1 ':' '.*\([Mhdwm]\)$'`
    value=`expr $1 ':' '\([0-9]*\)'`
    case $unit
    in M)seconds=60
    ;; h)seconds=3600
    ;; d)seconds=86400
    ;; w)seconds=604800
    ;; m)seconds=2592000
    ;; *)seconds=86400
    esac
    typeset -i result
    (( result = $value * $seconds ))
    [[ $result -eq 0 ]] && { print $current; return; }
    print $result
}

while getopts :f:r:c:l:u:h:t: opt
do
 case $opt
 in r)   refused=$(checkarg $OPTARG $unused $last)
  sortfield='+4'
 ;; u)   unused=$(checkarg $OPTARG $refused $last)
 ;; l)   last=$(checkarg $OPTARG $unused $refused)
 ;; c)   counter=$OPTARG
 ;; f)   infile=$OPTARG
 ;; h)   hostlist=$OPTARG
 ;; t)   ttylist=$OPTARG
 ;; \?) print -u2 "usage ${0##*/}: [-cnumber] [-l units|-r units|-u units] [-h hosts] [-t ttys] [-f file] [user...]"
  exit 1
 ;; :)   # Options specified without a value
        print -u2 "${0##*/}: $OPTARG requires a value"
        exit 1
 esac
done
shift OPTIND-1

# Get the list of users if any and build a comma separated list of users
for i in "$@"
do
    if [ -n "$userlist" ]
    then
        userlist="$userlist,$i"
    else
        userlist="$i"
    fi
done

awk -v userlist=$userlist -v hostlist=$hostlist -v ttylist=$ttylist \
    -v current=$current -v refused=$refused -v unused=$unused -v last=$last \
    -v counter=$counter '
# Check if name is in the list of names separated by comma. Empty list means
# everybody/everything.
function inlist(name, list )
{
 if( list == "" )
     return 1
 split(list, array, ",")
 for( i in array )
     if( name == array[i] )
        return 1
 return 0
}

function hostname(name, list)
{
 if( list == "" )
     return 1
 split(list, array, ",")
 for( i in array ){
     len = length(array[i])
     if( len > length(name) )
        len = length(name)
     compare1 = substr(name, 1, len)
     compare2 = substr(array[i], 1, len)
     # print compare1, compare2
     if( compare1 == compare2 )
        return 1
 }
 return 0
}

# Check if time is in range, if the flag was not set (value -1) return true.
function qualify(value, limit, flag)
{
 if( flag == -1 || value >= limit )
     return 1
 return 0
}

function output( )
{
 if( refused > -1 ){
     search_tty = Utty
     search_host = Uhost
 }else{
     search_tty = tty
     search_host = host
 }
 # Check if entry matches output criteria
 if( inlist(user, userlist) == 0 \
 || inlist(search_tty, ttylist) == 0 \
 || hostname(search_host, hostlist) == 0 \
 || qualify(Uattempt, counter, counter) == 0 \
 || qualify(Utime, current - refused, refused) == 0 \
 || qualify(-time, -(current - unused), unused) == 0 \
 || qualify(time, current - last, last) == 0 )
     return 0;

 print user, Uattempt, Uhost, Utty, Utime, host, tty, time;
}

BEGIN   { # Set all variables to unused values
 first=1;
}
/^[a-zA-Z0-9]*:$/{
 if ( first == 0 ){
  output( );
 }
 first = 0
 len = length
 user = substr($1, 1, len - 1)
}

/^      time_last_login/  { time = $3 }
/^      tty_last_login/   { tty = $3 }
/^      host_last_login/  { host = $3 }
/^      unsuccessful_login_count/{ Uattempt = $3 }
/^      time_last_unsuccessful_login/   { Utime = $3 }
/^      tty_last_unsuccessful_login/    { Utty = $3 }
/^      host_last_unsuccessful_login/   { Uhost = $3 }
END     {
 output( );
}' $infile | sort -t' ' $sortfield -n |
while read user failed fhost ftty ftime host tty time
do
    if [[ $refused -ne -1 ]]
    then
        printf "%-8.8s %3d %s %-10.10s %s\n" $user $failed \
        "`/var/adm/local/cvttime -f '%d-%b-%y %H:%M' $ftime`" $ftty $fhost
    else
        printf "%-8.8s %3d %s %-10.10s %s\n" $user $failed \
        "`/var/adm/local/cvttime -f '%d-%b-%y %H:%M' $time`" $tty $host
    fi
done

