Cover V04, I01
Article
Figure 1
Listing 1
Listing 2
Listing 3

jan95.tar


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