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
flee@cs.psu.edu (Felix Lee) (04/29/91)
> # go for it! Seek backwards through the wtmp.
It's easier to scan the wtmp file forward.
perl -e '$w=`getdate 8:35`;$x{$v[0]}=$v[1]while read(stdin,$_,36)&&(@v=unpack(
"A8A8A16l",$_),$v[3]<=$w);for(keys%x){print"$_ $x{$_}\n"if$x{$_};}'</usr/adm/wtmp
Two lines. Can't really get it shorter, I'm afraid.
--
Felix Lee flee@cs.psu.edu
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