[comp.unix.questions] Searching the output of last

guru@buhub.bradley.edu (Jerry Whelan) (04/28/91)

	Does anyone have/know of a program to parse the output of the
'last' command and tell me who was logged in at some arbitrary time?
I'd like to be able to type: 

			whenwho 9:56

and get a list of all the people who were logged in during that particular
minute.  My site resets the wtmp file at 4 AM, so we are not dealing with
infinite output from 'last.'  Awk, perl, C or anything that works reasonably
quickly is fine with me.

-------------------------------------------------------------------------------
	"I'm not sure what I mean, so I'm going to listen to what I say."
 guru@ (buhub.bradley.edu || bucc1.bradley.edu) || whelan@wiliki.eng.hawaii.edu

navarra@casbah.acns.nwu.edu (John 'tms' Navarra) (04/29/91)

In article <1991Apr28.070748.5279@bradley.bradley.edu> guru@buhub.bradley.edu (Jerry Whelan) writes:
>
>	Does anyone have/know of a program to parse the output of the
>'last' command and tell me who was logged in at some arbitrary time?
>I'd like to be able to type: 
>
> 			whenwho 9:56

         do last | grep time  
                  
>
>and get a list of all the people who were logged in during that particular
>minute.  My site resets the wtmp file at 4 AM, so we are not dealing with
>infinite output from 'last.'  Awk, perl, C or anything that works reasonably
>quickly is fine with me.
>
>-------------------------------------------------------------------------------
>	"I'm not sure what I mean, so I'm going to listen to what I say."
> guru@ (buhub.bradley.edu || bucc1.bradley.edu) || whelan@wiliki.eng.hawaii.edu


-- 
From the Lab of the MaD ScIenTiST:
      
navarra@casbah.acns.nwu.edu

jik@athena.mit.edu (Jonathan I. Kamens) (04/29/91)

In article <1991Apr29.021236.12132@casbah.acns.nwu.edu>, navarra@casbah.acns.nwu.edu (John 'tms' Navarra) writes:
|> In article <1991Apr28.070748.5279@bradley.bradley.edu> guru@buhub.bradley.edu (Jerry Whelan) writes:
|> >	Does anyone have/know of a program to parse the output of the
|> >'last' command and tell me who was logged in at some arbitrary time?
|> >I'd like to be able to type: 
|> >
|> > 			whenwho 9:56
|> 
|>          do last | grep time  

  I want to be polite, but I have an overwhelming urge to respond to this by
saying, "Get a clue."

  Mr. Whelan wants to know how to find out who was logged in at any particular
time, not who logged in or logged out at any particular time.

  The output of "last" indicates only the time a user logged in, and the time
he/she logged out.  Therefore, grepping for a particular time will catch only
the people who logged in or logged out during that minute.

  In order to do what Mr. Whelan wants, you need a utility that reads the
output of last (or reads /usr/adm/wtmp directly) and checks each pair of login
and logout times to see if the specified time is between them.

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

navarra@casbah.acns.nwu.edu (John 'tms' Navarra) (04/29/91)

In article <1991Apr29.040325.1822@athena.mit.edu> jik@athena.mit.edu (Jonathan I. Kamens) writes:
>In article <1991Apr29.021236.12132@casbah.acns.nwu.edu>, navarra@casbah.acns.nwu.edu (John 'tms' Navarra) writes:
>|> In article <1991Apr28.070748.5279@bradley.bradley.edu> guru@buhub.bradley.edu (Jerry Whelan) writes:
>|> >	Does anyone have/know of a program to parse the output of the
>|> >'last' command and tell me who was logged in at some arbitrary time?
>|> >I'd like to be able to type: 
>|> >
>|> > 			whenwho 9:56
>|> 
>|>          do last | grep time  
>
>  I want to be polite, but I have an overwhelming urge to respond to this by
>saying, "Get a clue."
>
>  Mr. Whelan wants to know how to find out who was logged in at any particular
>time, not who logged in or logged out at any particular time.

 My bad! I misread the question. how bout setting up a program that has two
 arguments: a interval and a begining time. Then it looks thru utmp by
 doing a last | grep time ( might be slow) where time is the starting time
 and then increments it by one for interval loops. This will catch all 
 ocurrences of someone being logged in at a particular interval but the 
 trouble would be that you need some way to filter the output because it 
 will report a users name everytime thru the loop if he was logged on for
 more than a minute in the specified interval.                          
		Just a suggestion -- needs some work though.

>-- 
>Jonathan Kamens			              USnail:
>MIT Project Athena				11 Ashford Terrace
>jik@Athena.MIT.EDU				Allston, MA  02134
>Office: 617-253-8085			      Home: 617-782-0710


-- 
From the Lab of the MaD ScIenTiST:
      
navarra@casbah.acns.nwu.edu

raymond@math.berkeley.edu (Raymond Chen) (04/29/91)

In article <1991Apr29.043041.20454@casbah.acns.nwu.edu>, navarra@casbah (John 'tms' Navarra) writes: 

>how bout setting up a program that... looks thru utmp by doing a last
>| grep time (might be slow) ... then increments it by one for interval loops.
              ^^^^^^^^^^^^^
              my vote for understatement of the day.  It is not unheard
of for people to be logged in for days at a time.  Incrementing by one means
you have to perform thousands of greps.

[For comp.lang.perlers, the problem is to write a progral called `whenwho'
which prints out everybody who was logged on at the time indicated on the
command line.]

Try this.  Hacked up in 15 minutes, minimally tested.  Part of the
problem is that you never know if you've searched backwards far
enough, because there might be someone who has been logged on
continuously for the past five months.  In the original problem,
however, we are told that the wtmp goes back only as far as around 5am
the day of the run, so this isn't an issue.

#!/bin/perl
# fetch desired time
die "usage:  whenwho hh:mm\n" 
  if (($hr, $min) = $ARGV[0] =~ /^(\d+):(\d+)$/) != 2;

# compute the human-readable time into a UNIX time thingie
$now = time; @now = localtime($now);
$midnight = $now - 3600*$now[2] - 60*$now[1] - $now[0]; #midnight today
$desired = $midnight + 3600*$hr + 60*min;               #time we want

# prepare to read the wtmp
$wtmp = "A8 A8 A16 l"; $size = length(pack($wtmp, "", "", "", 0));
$pos = -$size;
open(U, "/usr/adm/wtmp") || die "Yow!  /usr/adm/wtmp: $!\n";

# go for it!  Seek backwards through the wtmp.
while (seek(U, $pos, 2)) { read(U, $_, $size); $pos -= $size;
  ($line, $name, $host, $time) = unpack($wtmp, $_); # get wtmp
  if ($name) { # somebody logged in
  if ($time < $desired && $desired < $line{$line}) { # got one!
    printf "%8s %8s\n", $line, $name;
  } } else { $line{$line} = $time; } #logged out; remember logout time
}
__END__
Of course, Randal probably could do this in one line :-)

print sort "another p", "Just ", "hacker,", "erl ";

lwall@jpl-devvax.jpl.nasa.gov (Larry Wall) (04/29/91)

In article <1991Apr29.053721.8733@agate.berkeley.edu> raymond@math.berkeley.edu (Raymond Chen) writes:
: In article <1991Apr29.043041.20454@casbah.acns.nwu.edu>, navarra@casbah (John 'tms' Navarra) writes: 
: 
: >how bout setting up a program that... looks thru utmp by doing a last
: >| grep time (might be slow) ... then increments it by one for interval loops.
:               ^^^^^^^^^^^^^
:               my vote for understatement of the day.  It is not unheard
: of for people to be logged in for days at a time.  Incrementing by one means
: you have to perform thousands of greps.
: 
: [For comp.lang.perlers, the problem is to write a progral called `whenwho'
: which prints out everybody who was logged on at the time indicated on the
: command line.]
: 
: Try this.  Hacked up in 15 minutes, minimally tested.  Part of the
: problem is that you never know if you've searched backwards far
: enough, because there might be someone who has been logged on
: continuously for the past five months.  In the original problem,
: however, we are told that the wtmp goes back only as far as around 5am
: the day of the run, so this isn't an issue.
: 
[Script that reads wtmp directly omitted.]

If we can assume nobody is logged in overnight (or that records aren't kept),
then I'd just process the output of "last", like this:

#!/usr/bin/perl

$when = sprintf("%02d:%02d", shift =~ /^(\d+):?(\d\d)$/);
$today = (localtime())[3];
open(LAST, "last |");
while (<LAST>) {
    next if length() < 60;
    ($date,$in,$out) = unpack(x44A2xA5x3A5,$_);
    last if $date != $today;
    print if $when ge $in && $when le $out;
}

Not quite a one-liner, but getting closer...  Hmm...  If we throw out the
claptrap to check for todayness, and force people to enter exactly \d\d:\d\d
for the time, we can say

$w=shift;@ARGV="last|";$w ge substr($_,47,5)&&$w lt substr($_,55)&&print while<>

Hmm...  No reason not to use a regular expression...

$w=shift;@ARGV="last|";/.{47}(.{5})...(.*)/&&$w ge$1&&$w lt$2&&print while<>

Hmm...  We can dump two of those spaces that separate alphanumeric tokens...

$$=shift;@ARGV="last|";/.{47}(.{5})...(.*)/&&$$ge$1&&$$lt$2&&print while<>

Hmm...  We can assume that the first field contains the first colon...

$$=shift;@ARGV="last|";/(..:..)...(.*)/&&$$ge$1&&$$lt$2&&print while<>

I don't see how to make that shorter, offhand.  At least not in Perl alone.
Combining with shell, we can say

last|perl -pe '$_ x=/(..:..)...(.*)/&&"'$1'"ge$1&&"'$1'"lt$2'

That's gonna be tough for Randal to beat...  :-)

Larry

cliff@demon.co.uk (Cliff Stanford) (04/30/91)

In article <1991Apr28.070748.5279@bradley.bradley.edu> guru@buhub.bradley.edu (Jerry Whelan) writes:
>
>	Does anyone have/know of a program to parse the output of the
>'last' command and tell me who was logged in at some arbitrary time?
>I'd like to be able to type: 
>
>			whenwho 9:56
>
>and get a list of all the people who were logged in during that particular
>minute.  My site resets the wtmp file at 4 AM, so we are not dealing with
>infinite output from 'last.'  Awk, perl, C or anything that works reasonably
>quickly is fine with me.

	I just knoked the following up.  Don't blame me if it's
full of bugs or non-prtable.  It works for me!
		Cliff.

#!/bin/sh
# whenwho
last | gawk -v wanted=$1 '
BEGIN	{
		wmin = whr = wanted
		sub(/.*:/, "", wmin)
		sub(/:.*/, "", whr)
		}

FNR==1	{ next }

		{
		min = hr = $8
		sub(/.*:/, "", min)
		sub(/:.*/, "", hr)
		if (hr < whr)
			next
		if (hr == whr && min < wmin)
			next
		if (hr > whr && min > wmin)
			next
		emin = ehr = $9
		sub(/.*:/, "", emin)
		sub(/:.*/, "", ehr)
		hr += ehr
		min += emin
		if (min > 59)
			{
			hr++
			min -= 60
			}
		if (hr < whr)
			next
		if (hr == whr && min < wmin)
			next
		print $1
		}
' | sort | uniq
-- 
Cliff Stanford				Email:	cliff@demon.co.uk (Work)
Demon Systems Limited				cms@demon.co.uk   (Home)
42 Hendon Lane				Phone:	081-349 0063	  (Office)
London	N3 1TT	England				0860 375870	  (Mobile)

rockwell@socrates.umd.edu (Raul Rockwell) (04/30/91)

Larry Wall writes:
   If we can assume nobody is logged in overnight (or that records
   aren't kept), ...

When I need to determine when people logged in, worst case is last
time the machine was rebooted.  But then, I only do this once per day,
and I keep track of the month of the oldest active login (ok, so it's
just as easy to keep track of the day of the oldest active login...).

No perl, little wisdom, just thoughts...

Raul Rockwell

rbj@uunet.UU.NET (Root Boy Jim) (04/30/91)

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:

>If we can assume nobody is logged in overnight (or that records aren't kept),
>then I'd just process the output of "last", like this:

We have the opposite situation. lots of people logged on for a short time.

Here are a few interesting statistics:

11:37pm /USR/sys/conf [root@uunet 288] ll /usr/adm/wtmp
1224 -rw-r--r--  1 root     bin       1238724 Apr 29 23:37 /usr/adm/wtmp
11:37pm /USR/sys/conf [root@uunet 289] ll /USR/adm/wtmp
23128 -rw-rw-r--  1 root     wheel    23652936 Apr 28 10:04 /USR/adm/wtmp
11:50pm /USR/sys/conf [root@uunet 291] date;last|wc;date
Mon Apr 29 23:51:00 EDT 1991
   17354  162894 1214646
Mon Apr 29 23:53:56 EDT 1991
11:53pm /USR/sys/conf [root@uunet 292] 

The first file contains login records for today only.
The second is cumulative for the entire month.
We upgraded the OS yesterday, so you can extrapolate
that it would take about an hour alone to run last
on a whole month's statistics.

>last|perl -pe '$_ x=/(..:..)...(.*)/&&"'$1'"ge$1&&"'$1'"lt$2'
>
>That's gonna be tough for Randal to beat...  :-)

I just ran it with args 12:34 and 23:45. Here's the timing:
137.510u 88.580s 4:58.87 75% 0+0k 212+7io 0pf+0w

Five minutes for one day. Two and 1/2 hours for the entire month.

OK, so we're not a typical site :-)

>Larry
-- 
		[rbj@uunet 1] stty sane
		unknown mode: sane

lwall@jpl-devvax.jpl.nasa.gov (Larry Wall) (05/01/91)

In article <130922@uunet.UU.NET> rbj@uunet.UU.NET (Root Boy Jim) writes:
: The first file contains login records for today only.
: The second is cumulative for the entire month.
: We upgraded the OS yesterday, so you can extrapolate
: that it would take about an hour alone to run last
: on a whole month's statistics.
: 
: >last|perl -pe '$_ x=/(..:..)...(.*)/&&"'$1'"ge$1&&"'$1'"lt$2'
: >
: >That's gonna be tough for Randal to beat...  :-)
: 
: I just ran it with args 12:34 and 23:45. Here's the timing:
: 137.510u 88.580s 4:58.87 75% 0+0k 212+7io 0pf+0w
: 
: Five minutes for one day. Two and 1/2 hours for the entire month.
: 
: OK, so we're not a typical site :-)

Oh, good grief.  I wasn't optimizing for speed, but brevity (obscurity?).
By all means, read the file directly if you want speed.  There's More
Than One Way To Do It.

Larry