From: Tom Christiansen <tchrist@convex.COM>
Subject: Re: sa -e ????
Followup-To: comp.unix.admin,comp.lang.perl
Originator: tchrist@pixel.convex.com
Reply-To: tchrist@convex.COM (Tom Christiansen)
Organization: CONVEX Realtime Development, Colorado Springs, CO
Date: Tue, 28 Jul 1992 12:29:13 GMT
X-Disclaimer: This message was written by a user at CONVEX Computer
              Corp. The opinions expressed are those of the user and
              not necessarily those of CONVEX.

[I'm redirecting this thread into comp.unix.admin, where would 
 seem to be a better home for it; change the newsgroups/followup-to
 line if you disagree. 

 It's also a good example of using c2ph-generate require files.
 Followups probably ought to go to comp.lang.perl about that.]

From the keyboard of jay@silence.princeton.nj.us (Jay Plett):
:In article <1992Jul27.155418.9036@news.eng.convex.com>, tchrist@convex.COM (Tom Christiansen) writes:
:> Given sys/acct.h, you could very easily write either a Perl or a C
:> program to emulate what my "sa -e" does.
:
:Well, it's pretty easy once you've figured out how to interpret
:all of the fields in the accounting file (sys/acct.h isn't always
:awfully helpful with this).
:
:A C program which formats the accounting file according to
:printf-like arguments is available by anonymous ftp in
:pub/facct.tar.Z on ee-ftp.princeton.edu.  It works on SunOS
:and Convex-OS and should be fairly easy to port to other
:systems.

There are several problems with that approach. The most
important of these is that you have to hack the C file
for each possible change in the acct structure.  There may 
be new fields, or fields with a different type.  Here are 
four example acct files:

    ::::::::::::::
    acct.convex
    ::::::::::::::
    char    ac_comm[10];            
    struct timeval  ac_nutime;      
    struct timeval  ac_nstime;      
    struct timeval  ac_netime;      
    time_t  ac_btime;               
    u_short ac_uid;                 
    u_short ac_gid;                 
    long    ac_aid;                 
    s64_t   ac_kbsec;               
    long    ac_io;                  
    dev_t   ac_tty;                 
    char    ac_flag;                
    char    ac_unused[5];           
    float   ac_avconcur;            

    ::::::::::::::
    acct.mips
    ::::::::::::::
    char    ac_flag;                
    char    ac_stat;                
    ushort  ac_uid;                 
    ushort  ac_gid;                 
    dev_t   ac_tty;                 
    time_t  ac_btime;               
    comp_t  ac_utime;               
    comp_t  ac_stime;               
    comp_t  ac_etime;               
    comp_t  ac_mem;                 
    comp_t  ac_io;                  
    comp_t  ac_rw;                  
    char    ac_comm[8];             

    ::::::::::::::
    acct.sun
    ::::::::::::::
    char    ac_flag;                
    char    ac_stat;                
    uid_t   ac_uid;                 
    gid_t   ac_gid;                 
    dev_t   ac_tty;                 
    time_t  ac_btime;               
    comp_t  ac_utime;               
    comp_t  ac_stime;               
    comp_t  ac_etime;               
    comp_t  ac_mem;                 
    comp_t  ac_io;                  
    comp_t  ac_rw;                  
    char    ac_comm[8];             

    ::::::::::::::
    acct.vax
    ::::::::::::::
    char    ac_comm[10];            
    #ifdef vax
	float   ac_utime;               
	float   ac_stime;               
	float   ac_etime;               
    #else mips
	comp_t  ac_utime;
	comp_t  ac_stime;
	comp_t  ac_etime;
    #endif mips
    time_t  ac_btime;               
    short   ac_uid;                 
    short   ac_gid;                 
    #ifdef vax
	float   ac_mem;                 
	float   ac_io;                  
    #else mips
	short   ac_mem;
	comp_t  ac_io;
    #endif mips
    dev_t   ac_tty;                 
    char    ac_flag;                


Another problem with the facct program is that you have to learn a whole
new set of %blah escapes rather than using regular printf ones.

Rather than hack the C program each time, another approach is 
to write your code in Perl instead.  I have a prototype, called 
pacctf, which behaves this way:

    pacctf "cmd = %-10s %5.2fs %5.2fu uid = %5d concur=%3.1f\n" \
		    comm nstime nutime uid concur


As you see, I am using Convex-style naming.  If I were to use
the rw field, which Convex doesn't have, I'd've gotten this:

    pacctf: No such field in struct acct: rw
    Valid field names are: aid, avconcur, btime, comm, flag, gid, io,
			   kbsec, netime, nstime, nutime, tty, uid, unused

There's a lot of room for improvement here.  It should probably be made
into a perl library package.  There's no contigency for xlating things
like uid, dev, gid into strings, although I do fix up the timevals.  It
should be able to read from an alternate accounting file.  It mihgt be
nice for some further logic in the args (like converting ac_btime into
ctime() fmt) , although that's where a library would help.

But hey, it's a prototype -- whadya want?  No, seriously, what might
one like in this kind of program?

--tom


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	pacctf
# This archive created: Tue Jul 28 07:23:13 1992
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'pacctf'" '(1335 characters)'
if test -f 'pacctf'
then
	echo shar: "will not over-write existing file 'pacctf'"
else
sed 's/^	X//' << \SHAR_EOF > 'pacctf'
	X$ACCT = '/usr/adm/acct';
	X
	Xif (@ARGV == 0) {
	X    die <<EOF
	Xusage: $0 fmt args ...
	X
	Xeg:    $0 "cmd = %-8s uid = %d\\n" comm uid
	XEOF
	X} 
	X
	Xopen ACCT || die "can't open $ACCT: $!";
	X
	Xrequire 'sys/acct.ph';
	X
	X$size = &acct'sizeof();
	X($binfmt = &acct'typedef()) =~ s/\bc(\d+)/A$1/g; 
	X# fix c10 to A10, as v1.2 c2ph did this wrong
	X
	X$mask = shift;
	Xeval qq(\$mask = "$mask");
	Xdie $@ if $@;
	X
	Xfor (@ARGV) {
	X    $i = eval "&acct'ac_$_";
	X    if ($@ eq "Undefined subroutine \"acct'ac_" . $_ . 
	X		"\" called at (eval) line 1.\n") {
	X	select(STDERR);
	X	print "$0: No such field in struct acct: $_\n";
	X	$fieldnames = join(", ", sort grep(s/ac_//, @acct'fieldnames));
	X	write;
	Xformat STDERR =
	XValid field names are: ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
	X$fieldnames
	X		    ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
	X$fieldnames
	X.
	X	exit 1;
	X    }
	X    die $@ if $@;
	X    push(@indices, $i);
	X    $wantidx[$i]++;
	X}
	X
	X$| = 1;
	X
	X@tvalidx = grep($wantidx[$_] && $acct'typeof[$_] eq 'timeval', @acct'indices);
	X@sidx = grep($wantidx[$_] && $acct'typedef[$_] =~ /^[ca]\d/i, @acct'indices);
	X
	Xwhile (read(ACCT, $ac, $size) == $size) {
	X    @ac = unpack($binfmt, $ac);
	X    # fix timevals
	X    for $i (@tvalidx) { $ac[$i] += $ac[$i+1] / 1_000_000; } 
	X    # fix nulls in strings
	X    grep (s/\0[\000-\377]*//, @ac[@sidx]);  
	X    printf $mask, @ac[@indices];
	X
	X} 
SHAR_EOF
if test 1335 -ne "`wc -c < 'pacctf'`"
then
	echo shar: "error transmitting 'pacctf'" '(should have been 1335 characters)'
fi
chmod 775 'pacctf'
fi
exit 0
#	End of shell archive
-- 
    Tom Christiansen      tchrist@convex.com      convex!tchrist

    "* Unix is a footnote of AT&T Bell Laboratories."
                             --Barry Shein