[comp.unix.sysv386] Perl unpack/tacct file

todd@pinhead.pegasus.com (Todd Ogasawara) (05/05/91)

I just started using Perl a few weeks ago and have run into a problem when
trying to use unpack to look at 'tacct' type accounting files on an 386 box
running Interactive UNIX 2.2 (UNIX System V/386 R3). I'm pretty sure I
understand the structure of this file type since C programs I've written to
read it work fine. The C structure of this file looks like this:

#p = prime time, np = non-prime time
#struct	tacct {
#	unsigned short	ta_uid;		/* userid */
#	char		ta_name[8];	/* login name */
#	float		ta_cpu[2];	/* cum cpu time, p/np (mins) */
#	float		ta_kcore[2];	/* cum kcore-minutes, p/np */
#	float		ta_con[2];	/* cum connect time p/np, mins */
#	float		ta_du;		/* cum disk usage */
#	long		ta_pc;		/* count of processes */
#	unsigned short	ta_sc;		/* count of login sessions */
#	unsigned short	ta_dc;		/* count of disk samples */
#	unsigned short	ta_fee;		/* fee for special services */
#};

Here's the perl program I've tried to use to read a tacct file with. (BTW,
I'm using Perl 4.0 patch level 3).

open(TACCT,"/usr/adm/acct/sum/tacct");

while(read(TACCT,$tacct,52)) {
	($uid,$name,$cpuP,$cpuNP,$kcoreP,$kcoreNP,$conP,$conNP,
	 $du,$pc,$sc,$dc,$fee) = 
		unpack("S A8 f f f f f f f l S S S",$tacct);
	local($TCONNECT) = $conP + $conNP;
	printf "%4d %8s %7.2f %d %d\n",
		$uid, $name, $TCONNECT, $sc, $dc;
}

The $uid and $name variables contain what I expect it to. However,
everything else that follows seems to be one off. I.e., The $dc variable
should contain the number of disk samples but seems to contain the number
of login sessions instead. The floating point values are all over the
place. I know that I must be reading in the floating point variables
incorrectly, but I don't know what it is that I'm doing wrong. BTW, I have
the Wall & Schwartz "Programming Perl" book and have gone through the
sections describing pack and unpack several times to try to understand what
is going on here.

Thanks for any help/hints anyone can offer.

-- 
Todd Ogasawara ::: Hawaii Medical Service Association
Internet       ::: todd@pinhead.pegasus.com
Telephone      ::: (808) 536-9162 ext. 7

jdeitch@jadpc.cts.com (Jim Deitch) (05/05/91)

In article <1991May04.185409.24228@pinhead.pegasus.com> todd@pinhead.pegasus.com (Todd Ogasawara) writes:
>
>Here's the perl program I've tried to use to read a tacct file with. (BTW,
>I'm using Perl 4.0 patch level 3).
>
           ^^^^^^^^^^^^^^^^^^^^^^
I saw this was available, but the message went by to fast.  Where can
I get a copy ?

Jim
-- 
ARPANET:    jadpc!jdeitch@nosc.mil
INTERNET:   jdeitch@jadpc.cts.com
UUCP:	    nosc!jadpc!jdeitch

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

In article <1991May04.185409.24228@pinhead.pegasus.com> todd@pinhead.pegasus.com (Todd Ogasawara) writes:
: 
: I just started using Perl a few weeks ago and have run into a problem when
: trying to use unpack to look at 'tacct' type accounting files on an 386 box
: running Interactive UNIX 2.2 (UNIX System V/386 R3). I'm pretty sure I
: understand the structure of this file type since C programs I've written to
: read it work fine. The C structure of this file looks like this:
: 
: #p = prime time, np = non-prime time
: #struct	tacct {
: #	unsigned short	ta_uid;		/* userid */
: #	char		ta_name[8];	/* login name */
: #	float		ta_cpu[2];	/* cum cpu time, p/np (mins) */
: #	float		ta_kcore[2];	/* cum kcore-minutes, p/np */
: #	float		ta_con[2];	/* cum connect time p/np, mins */
: #	float		ta_du;		/* cum disk usage */
: #	long		ta_pc;		/* count of processes */
: #	unsigned short	ta_sc;		/* count of login sessions */
: #	unsigned short	ta_dc;		/* count of disk samples */
: #	unsigned short	ta_fee;		/* fee for special services */
: #};
: 
: Here's the perl program I've tried to use to read a tacct file with. (BTW,
: I'm using Perl 4.0 patch level 3).
: 
: open(TACCT,"/usr/adm/acct/sum/tacct");
: 
: while(read(TACCT,$tacct,52)) {
: 	($uid,$name,$cpuP,$cpuNP,$kcoreP,$kcoreNP,$conP,$conNP,
: 	 $du,$pc,$sc,$dc,$fee) = 
: 		unpack("S A8 f f f f f f f l S S S",$tacct);
: 	local($TCONNECT) = $conP + $conNP;
: 	printf "%4d %8s %7.2f %d %d\n",
: 		$uid, $name, $TCONNECT, $sc, $dc;
: }
: 
: The $uid and $name variables contain what I expect it to. However,
: everything else that follows seems to be one off. I.e., The $dc variable
: should contain the number of disk samples but seems to contain the number
: of login sessions instead. The floating point values are all over the
: place. I know that I must be reading in the floating point variables
: incorrectly, but I don't know what it is that I'm doing wrong. BTW, I have
: the Wall & Schwartz "Programming Perl" book and have gone through the
: sections describing pack and unpack several times to try to understand what
: is going on here.

Your C compiler is probably throwing you a curve by aligning the first
float to a 4-byte boundary.  Try saying

		unpack("S A8 x2 f f f f f f f l S S S",$tacct);

Larry

todd@pinhead.pegasus.com (Todd Ogasawara) (05/07/91)

I posted a query last week. No responses so far, but I found a
workaround. My question now is, "Why does my workaround work?"

Here is my original posting.

In article <1991May04.185409.24228@pinhead.pegasus.com> todd@pinhead.pegasus.com (Todd Ogasawara) writes:
]I just started using Perl a few weeks ago and have run into a problem when
]trying to use unpack to look at 'tacct' type accounting files on an 386 box
]running Interactive UNIX 2.2 (UNIX System V/386 R3). I'm pretty sure I
]understand the structure of this file type since C programs I've written to
]read it work fine. The C structure of this file looks like this:

]#p = prime time, np = non-prime time
]#struct	tacct {
]#	unsigned short	ta_uid;		/* userid */
]#	char		ta_name[8];	/* login name */
]#	float		ta_cpu[2];	/* cum cpu time, p/np (mins) */
]#	float		ta_kcore[2];	/* cum kcore-minutes, p/np */
]#	float		ta_con[2];	/* cum connect time p/np, mins */
]#	float		ta_du;		/* cum disk usage */
]#	long		ta_pc;		/* count of processes */
]#	unsigned short	ta_sc;		/* count of login sessions */
]#	unsigned short	ta_dc;		/* count of disk samples */
]#	unsigned short	ta_fee;		/* fee for special services */
]#};
]
]Here's the perl program I've tried to use to read a tacct file with. (BTW,
]I'm using Perl 4.0 patch level 3).
]
]open(TACCT,"/usr/adm/acct/sum/tacct");
]
]while(read(TACCT,$tacct,52)) {
]	($uid,$name,$cpuP,$cpuNP,$kcoreP,$kcoreNP,$conP,$conNP,
]	 $du,$pc,$sc,$dc,$fee) = 
]		unpack("S A8 f f f f f f f l S S S",$tacct);
]	local($TCONNECT) = $conP + $conNP;
]	printf "%4d %8s %7.2f %d %d\n",
]		$uid, $name, $TCONNECT, $sc, $dc;
]}
]
]The $uid and $name variables contain what I expect it to. However,
]everything else that follows seems to be one off. I.e., The $dc variable
]should contain the number of disk samples but seems to contain the number
]of login sessions instead. The floating point values are all over the
]place. I know that I must be reading in the floating point variables
]incorrectly, but I don't know what it is that I'm doing wrong. BTW, I have
]the Wall & Schwartz "Programming Perl" book and have gone through the
]sections describing pack and unpack several times to try to understand what
]is going on here.

The following Perl program reads tacct type files correctly. My only
problem is I don't understand why it works. I had to place two skips of x2
in the unpack template (see $stbuf_def) to get the program to work. I'm
pretty sure my problem is not understanding exactly how C structures are
built. I found that a C sizeof(tacctstruct) told me the structure is 52
bytes large. However, when I added the size of each individual structure
member it added up to 48 bytes. I had to do a hex dump to find that there
were four bytes in two places (two bytes in each place) that never seemed
to be used. Is there some easy rule I can follow when using Perl to unpack
files created using C structures?

#! /usr/local/bin/perl

open(TACCT,"/usr/adm/acct/sum/tacct");

$stbuf_def = "SA8x2f2f2f2flS3x2";
$streclen = length(pack($stbuf_def, 0));

print " UID     NAME      cpuP     cpuNP    conP   conNP  disksamp  login\n";

while(read(TACCT,$tacct,$streclen)) {
	($uid,$name,$cpuP,$cpuNP,$kcoreP,$kcoreNP,$conP,$conNP,
	 $du,$pc,$sc,$dc,$fee) = 
		unpack($stbuf_def,$tacct);
	printf "%4d %8s %9.2f %9.2f %7.2f %7.2f %9.2f %4d\n",
		$uid, $name, $cpuP, $cpuNP, $conP, $conNP, $du, $sc;
}
-- 
Todd Ogasawara ::: Hawaii Medical Service Association
Internet       ::: todd@pinhead.pegasus.com
Telephone      ::: (808) 536-9162 ext. 7

art@pilikia.pegasus.com (Art Neilson) (05/08/91)

In article <1991May06.235754.29292@pinhead.pegasus.com> todd@pinhead.pegasus.com (Todd Ogasawara) writes:
>I posted a query last week. No responses so far, but I found a
>workaround. My question now is, "Why does my workaround work?"
>
>Here is my original posting.
>
>In article <1991May04.185409.24228@pinhead.pegasus.com> todd@pinhead.pegasus.com (Todd Ogasawara) writes:
>The following Perl program reads tacct type files correctly. My only
>problem is I don't understand why it works. I had to place two skips of x2
>in the unpack template (see $stbuf_def) to get the program to work. I'm
>pretty sure my problem is not understanding exactly how C structures are
>built. I found that a C sizeof(tacctstruct) told me the structure is 52
>bytes large. However, when I added the size of each individual structure
>member it added up to 48 bytes. I had to do a hex dump to find that there
>were four bytes in two places (two bytes in each place) that never seemed
>to be used. Is there some easy rule I can follow when using Perl to unpack
>files created using C structures?

Your problem most likely is caused by structure padding.

The following quote from "C A Reference Manual" Second Edition by
Harbison and Steele is quite clear on the subject.

"The size of an object of a structure type is the amount of storage
necessary to represent all components of that type, including any
unused padding space between or after the components.  The rule is
that the structure will be padded out to the size the type would
occupy as an element of an array of such types.  (For any type T,
including structures, the size of an n-element array of T is the
same as the size of T times n.)  Another way of saying this is that
the structure must terminate on the same alignment boundary on which it
started; that is, if the structure must begin on an even byte boundary,
it must also end on an even byte boundary." 

The text in the book goes on for several paragraphs more, providing
examples and such.  See page 107, section 5.6.7 titled
"Sizes of Structures" for the complete story.
-- 
Arthur W. Neilson III		| INET: art@pilikia.pegasus.com
Bank of Hawaii Tech Support	| UUCP: uunet!ucsd!nosc!pilikia!art