OpenSolaris_b135/cmd/acct/acctcom.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <time.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include "acctdef.h"
#include <grp.h>
#include <sys/acct.h>
#include <pwd.h>
#include <sys/stat.h>
#include <locale.h>
#include <stdlib.h>
#include <libgen.h>

struct	acct ab;
char	command_name[16];
char	obuf[BUFSIZ];
static char	time_buf[50];

double	cpucut,
	syscut,
	hogcut,
	iocut,
	realtot,
	cputot,
	usertot,
	systot,
	kcoretot,
	iotot,
	rwtot;
extern long	timezone;
extern int	daylight;	/* daylight savings time if set */
long	daydiff,
	offset = -2,
	cmdcount;
ulong_t	elapsed,
	sys,
	user,
	cpu,
	io,
	rw,
	mem,
	etime;
time_t	tstrt_b,
	tstrt_a,
	tend_b,
	tend_a;
int	backward,
	flag_field,
	average,
	quiet,
	option,
	verbose = 1,
	uidflag,
	gidflag,
	unkid,	/*user doesn't have login on this machine*/
	errflg,
	su_user,
	fileout = 0,
	stdinflg,
	nfiles;
static int	eflg = 0,
	Eflg = 0,
	sflg = 0,
	Sflg = 0;
#ifdef uts
dev_t   linedev = 0xffff;  /* changed from -1, as dev_t is now ushort */
#else
dev_t	linedev = (dev_t)-1;
#endif
uid_t	uidval;
gid_t	gidval;
char	*cname = NULL; /* command name pattern to match*/

struct passwd *getpwnam(), *getpwuid(), *pw;
struct group *getgrnam(),*grp;
long	convtime();

#ifdef uts
float   expand();
#else
ulong_t	expand();
#endif

char	*ofile,
	*devtolin(),
	*uidtonam();
dev_t	lintodev();

void dofile(char *);
void doexit(int) __NORETURN;
void usage(void);
void fatal(char *, char *);
void println(void);
void printhd(void);
char *cmset(char *);

FILE	*ostrm;

int
main(int argc, char **argv)
{
	int	c;

	(void)setlocale(LC_ALL, "");
	setbuf(stdout,obuf);
	while((c = getopt(argc, argv,
		"C:E:H:I:O:S:abe:fg:hikl:mn:o:qrs:tu:v")) != EOF) {
		switch(c) {
		case 'C':
			sscanf(optarg,"%lf",&cpucut);
			continue;
		case 'O':
			sscanf(optarg,"%lf",&syscut);
			continue;
		case 'H':
			sscanf(optarg,"%lf",&hogcut);
			continue;
		case 'I':
			sscanf(optarg,"%lf",&iocut);
			continue;
		case 'a':
			average++;
			continue;
		case 'b':
			backward++;
			continue;
		case 'g':
			if(sscanf(optarg,"%ld",&gidval) == 1) {
				if (getgrgid(gidval) == NULL)
					fatal("Unknown group", optarg);
			} else if((grp=getgrnam(optarg)) == NULL)
				fatal("Unknown group", optarg);
			else
				gidval=grp->gr_gid;
			gidflag++;
			continue;
		case 'h':
			option |= HOGFACTOR;
			continue;
		case 'i':
			option |= IORW;
			continue;
		case 'k':
			option |= KCOREMIN;
			continue;
		case 'm':
			option |= MEANSIZE;
			continue;
		case 'n':
			cname=cmset(optarg);
			continue;
		case 't':
			option |= SEPTIME;
			continue;
		case 'r':
			option |= CPUFACTOR;
			continue;
		case 'v':
			verbose=0;
			continue;
		case 'l':
			linedev = lintodev(optarg);
			continue;
		case 'u':
			if(*optarg == '?') {
				unkid++;
				continue;
			}
			if(*optarg == '#') {
				su_user++;
				uidval = 0;
				uidflag++;
				continue;
			}
			if((pw = getpwnam(optarg)) == NULL) {
				uidval = (uid_t)atoi(optarg);
				/* atoi will return 0 in abnormal situation */
				if (uidval == 0 && strcmp(optarg, "0") != 0) {
					fprintf(stderr, "%s: Unknown user %s\n", argv[0], optarg);
					exit(1);
				}
 				if ((pw = getpwuid(uidval)) == NULL) {
					fprintf(stderr, "%s: Unknown user %s\n", argv[0], optarg);
					exit(1);
				}
				uidflag++;
			} else {
				uidval = pw->pw_uid;
				uidflag++;
			}
			continue;
		case 'q':
			quiet++;
			verbose=0;
			average++;
			continue;
		case 's':
			sflg = 1;
			tend_a = convtime(optarg);
			continue;
		case 'S':
			Sflg = 1;
			tstrt_a = convtime(optarg);
			continue;
		case 'f':
			flag_field++;
			continue;
		case 'e':
			eflg = 1;
			tstrt_b = convtime(optarg);
			continue;
		case 'E':
			Eflg = 1;
			tend_b = convtime(optarg);
			continue;
		case 'o':
			ofile = optarg;
			fileout++;
			if((ostrm = fopen(ofile, "w")) == NULL) {
				perror("open error on output file");
				errflg++;
			}
			continue;
		case '?':
			errflg++;
			continue;
		}
	}

	if(errflg) {
		usage();
		exit(1);
	}


	argv = &argv[optind];
	while(optind++ < argc) {
		dofile(*argv++);    /* change from *argv */
		nfiles++;
	}

	if(nfiles==0) {
		if(isatty(0) || isdevnull())
			dofile(PACCT);
		else {
			stdinflg = 1;
			backward = offset = 0;
			dofile(NULL);
		}
	}
	doexit(0);
	/* NOTREACHED */
}

void
dofile(char *fname)
{
	struct acct *a = &ab;
	struct tm *t;
	time_t curtime;
	time_t	ts_a = 0,
		ts_b = 0,
		te_a = 0,
		te_b = 0;
	long	daystart;
	long	nsize;
	int	ver;	/* version of acct structure */
	int	dst_secs;	/* number of seconds to adjust
				   for daylight savings time */

	if(fname != NULL)
		if(freopen(fname, "r", stdin) == NULL) {
			fprintf(stderr, "acctcom: cannot open %s\n", fname);
			return;
		}

	if (fread((char *)&ab, sizeof(struct acct), 1, stdin) != 1)
		return;
	else if (ab.ac_flag & AEXPND)
		ver = 2;	/* 4.0 acct structure */
	else 
		ver = 1;	/* 3.x acct structure */

	rewind(stdin);
		

	if(backward) {
		if (ver == 2)
			nsize = sizeof(struct acct);	/* make sure offset is signed */
		else
			nsize = sizeof(struct o_acct);	/* make sure offset is signed */
		fseek(stdin, (long)(-nsize), 2);
	}
	tzset();
	daydiff = a->ac_btime - (a->ac_btime % SECSINDAY);
	time(&curtime);
	t = localtime(&curtime);
	if (daydiff < (curtime - (curtime % SECSINDAY))) {
		time_t t;
		/*
		 * it is older than today
		 */
		t = (time_t)a->ac_btime;
		cftime(time_buf, DATE_FMT, &t);
		fprintf(stdout, "\nACCOUNTING RECORDS FROM:  %s", time_buf);
	}

	/* adjust time by one hour for daylight savings time */
	if (daylight && t->tm_isdst != 0)
		dst_secs = 3600;
	else
		dst_secs = 0;
	daystart = (a->ac_btime - timezone + dst_secs) - 
	    ((a->ac_btime - timezone + dst_secs) % SECSINDAY);
	if (Sflg) {
		ts_a = tstrt_a + daystart - dst_secs;
		cftime(time_buf, DATE_FMT, &ts_a);
		fprintf(stdout, "START AFT: %s", time_buf);
	}
	if (eflg) {
		ts_b = tstrt_b + daystart - dst_secs;
		cftime(time_buf, DATE_FMT, &ts_b);
		fprintf(stdout, "START BEF: %s", time_buf);
	}
	if (sflg) {
		te_a = tend_a + daystart - dst_secs;
		cftime(time_buf, DATE_FMT, &te_a);
		fprintf(stdout, "END AFTER: %s", time_buf);
	}
	if (Eflg) {
		te_b = tend_b + daystart - dst_secs;
		cftime(time_buf, DATE_FMT, &te_b);
		fprintf(stdout, "END BEFOR: %s", time_buf);
	}
	if(ts_a) {
		if (te_b && ts_a > te_b) te_b += SECSINDAY;
	}

	while(aread(ver) != 0) {
		elapsed = expand(a->ac_etime);
		etime = (ulong_t)a->ac_btime + (ulong_t)SECS(elapsed);
		if(ts_a || ts_b || te_a || te_b) {

			if(te_a && (etime < te_a)) {
				if(backward) return;
				else continue;
			}
			if(te_b && (etime > te_b)) {
				if(backward) continue;
				else return;
			}
			if(ts_a && (a->ac_btime < ts_a))
				continue;
			if(ts_b && (a->ac_btime > ts_b))
				continue;
		}
		if(!MYKIND(a->ac_flag))
			continue;
		if(su_user && !SU(a->ac_flag))
			continue;
		sys = expand(a->ac_stime);
		user = expand(a->ac_utime);
		cpu = sys + user;
		if(cpu == 0)
			cpu = 1;
		mem = expand(a->ac_mem);
		(void) strncpy(command_name, a->ac_comm, 8);
		io=expand(a->ac_io);
		rw=expand(a->ac_rw);
		if(cpucut && cpucut >= SECS(cpu))
			continue;
		if(syscut && syscut >= SECS(sys))
			continue;
#ifdef uts
		if(linedev != 0xffff && a->ac_tty != linedev)
			continue;
#else
		if(linedev != (dev_t)-1 && a->ac_tty != linedev)
			continue;
#endif
		if(uidflag && a->ac_uid != uidval)
			continue;
		if(gidflag && a->ac_gid != gidval)
			continue;
		if(cname && !cmatch(a->ac_comm,cname))
			continue;
		if(iocut && iocut > io)
			continue;
		if(unkid && uidtonam(a->ac_uid)[0] != '?')
			continue;
		if(verbose && (fileout == 0)) {
			printhd();
			verbose = 0;
		}
		if(elapsed == 0)
			elapsed++;
		if(hogcut && hogcut >= (double)cpu/(double)elapsed)
			continue;
		if(fileout)
			fwrite(&ab, sizeof(ab), 1, ostrm);
		else
			println();
		if(average) {
			cmdcount++;
			realtot += (double)elapsed;
			usertot += (double)user;
			systot += (double)sys;
			kcoretot += (double)mem;
			iotot += (double)io;
			rwtot += (double)rw;
		};
	}
}

int
aread(int ver)
{
	static int ok = 1;
	struct o_acct oab;
	int ret;

	if (ver != 2) {
		if ((ret = fread((char *)&oab, sizeof(struct o_acct), 1, stdin)) == 1){
			/* copy SVR3 acct struct to SVR4 acct struct */
			ab.ac_flag = oab.ac_flag | AEXPND;
			ab.ac_stat = oab.ac_stat;
			ab.ac_uid = (uid_t) oab.ac_uid;
			ab.ac_gid = (gid_t) oab.ac_gid;
			ab.ac_tty = (dev_t) oab.ac_tty;
			ab.ac_btime = oab.ac_btime;
			ab.ac_utime = oab.ac_utime;
			ab.ac_stime = oab.ac_stime;
			ab.ac_mem = oab.ac_mem;
			ab.ac_io = oab.ac_io;
			ab.ac_rw = oab.ac_rw;
			strcpy(ab.ac_comm, oab.ac_comm);
		}
	} else
		ret = fread((char *)&ab, sizeof(struct acct), 1, stdin);
	

	if(backward) {
		if(ok) {
			if(fseek(stdin,
				(long)(offset*(ver == 2 ? sizeof(struct acct) :
					sizeof(struct o_acct))), 1) != 0) {

					rewind(stdin);	/* get 1st record */
					ok = 0;
			}
		} else
			ret = 0;
	}
	return(ret != 1 ? 0 : 1);
}

void
printhd(void)
{
	fprintf(stdout, "COMMAND                           START    END          REAL");
	ps("CPU");
	if(option & SEPTIME)
		ps("(SECS)");
	if(option & IORW){
		ps("CHARS");
		ps("BLOCKS");
	}
	if(option & CPUFACTOR)
		ps("CPU");
	if(option & HOGFACTOR)
		ps("HOG");
	if(!option || (option & MEANSIZE))
		ps("MEAN");
	if(option & KCOREMIN)
		ps("KCORE");
	fprintf(stdout, "\n");
	fprintf(stdout, "NAME       USER     TTYNAME       TIME     TIME       (SECS)");
	if(option & SEPTIME) {
		ps("SYS");
		ps("USER");
	} else
		ps("(SECS)");
	if(option & IORW) {
		ps("TRNSFD");
		ps("READ");
	}
	if(option & CPUFACTOR)
		ps("FACTOR");
	if(option & HOGFACTOR)
		ps("FACTOR");
	if(!option || (option & MEANSIZE))
		ps("SIZE(K)");
	if(option & KCOREMIN)
		ps("MIN");
	if(flag_field)
		fprintf(stdout, "  F STAT");
	fprintf(stdout, "\n");
	fflush(stdout);
}

void
println(void)
{
	char name[32];
	struct acct *a = &ab;
	time_t t;

	if(quiet)
		return;
	if(!SU(a->ac_flag))
		strcpy(name,command_name);
	else {
		strcpy(name,"#");
		strcat(name,command_name);
	}
	fprintf(stdout, "%-*.*s", (OUTPUT_NSZ + 1),
	    (OUTPUT_NSZ + 1), name);
	strcpy(name,uidtonam(a->ac_uid));
	if(*name != '?')
		fprintf(stdout, "  %-*.*s", (OUTPUT_NSZ + 1),
		    (OUTPUT_NSZ + 1), name);
	else
		fprintf(stdout, "  %-9d",a->ac_uid);
#ifdef uts
	fprintf(stdout, " %-*.*s", OUTPUT_LSZ, OUTPUT_LSZ,
	    a->ac_tty != 0xffff? devtolin(a->ac_tty):"?");
#else
	fprintf(stdout, " %-*.*s", OUTPUT_LSZ, OUTPUT_LSZ,
	    a->ac_tty != (dev_t)-1? devtolin(a->ac_tty):"?");
#endif
	t = a->ac_btime;
	cftime(time_buf, DATE_FMT1, &t);
	fprintf(stdout, "%.9s", time_buf);
	cftime(time_buf, DATE_FMT1, (time_t *)&etime);
	fprintf(stdout, "%.9s ", time_buf);
	pf((double)SECS(elapsed));
	if(option & SEPTIME) {
		pf((double)sys / HZ);
		pf((double)user / HZ);
	} else
		pf((double)cpu / HZ);
	if(option & IORW)
		fprintf(stdout, io < 100000000 ? "%8ld%8ld" : "%12ld%8ld",io,rw);
	if(option & CPUFACTOR)
		pf((double)user / cpu);
	if(option & HOGFACTOR)
		pf((double)cpu / elapsed);
	if(!option || (option & MEANSIZE))
		pf(KCORE(mem / cpu));
	if(option & KCOREMIN)
		pf(MINT(KCORE(mem)));
	if(flag_field)
		fprintf(stdout, "  %1o %3o", (unsigned char) a->ac_flag,
						(unsigned char) a->ac_stat);
	fprintf(stdout, "\n");
}

/*
 * convtime converts time arg to internal value
 * arg has form hr:min:sec, min or sec are assumed to be 0 if omitted
 */
long
convtime(str)
char *str;
{
	long	hr, min, sec;

	min = sec = 0;

	if(sscanf(str, "%ld:%ld:%ld", &hr, &min, &sec) < 1) {
		fatal("acctcom: bad time:", str);
	}
	tzset();
	sec += (min*60);
	sec += (hr*3600);
	return(sec + timezone);
}

int
cmatch(char *comm, char *cstr)
{

	char	xcomm[9];
	int i;

	for(i=0;i<8;i++){
		if(comm[i]==' '||comm[i]=='\0')
			break;
		xcomm[i] = comm[i];
	}
	xcomm[i] = '\0';
	
	return (regex(cstr,xcomm) ? 1 : 0);
}

char *
cmset(char *pattern)
{

	if((pattern=(char *)regcmp(pattern,(char *)0))==NULL){
		fatal("pattern syntax", NULL);
	}

	return (pattern);
}

void
doexit(int status)
{
	if(!average)
		exit(status);
	if(cmdcount) {
		fprintf(stdout, "cmds=%ld ",cmdcount);
		fprintf(stdout, "Real=%-6.2f ",SECS(realtot)/cmdcount);
		cputot = systot + usertot;
		fprintf(stdout, "CPU=%-6.2f ",SECS(cputot)/cmdcount);
		fprintf(stdout, "USER=%-6.2f ",SECS(usertot)/cmdcount);
		fprintf(stdout, "SYS=%-6.2f ",SECS(systot)/cmdcount);
		fprintf(stdout, "CHAR=%-8.2f ",iotot/cmdcount);
		fprintf(stdout, "BLK=%-8.2f ",rwtot/cmdcount);
		fprintf(stdout, "USR/TOT=%-4.2f ",usertot/cputot);
		fprintf(stdout, "HOG=%-4.2f ",cputot/realtot);
		fprintf(stdout, "\n");
	}
	else
		fprintf(stdout, "\nNo commands matched\n");
	exit(status);
}

int
isdevnull(void)
{
	struct stat	filearg;
	struct stat	devnull;

	if(fstat(0,&filearg) == -1) {
		fprintf(stderr,"acctcom: cannot stat stdin\n");
		return (0);
	}
	if(stat("/dev/null",&devnull) == -1) {
		fprintf(stderr,"acctcom: cannot stat /dev/null\n");
		return (0);
	}

	if (filearg.st_rdev == devnull.st_rdev)
		return (1);
	else
		return (0);
}

void
fatal(char *s1, char *s2)
{
	fprintf(stderr,"acctcom: %s %s\n", s1, (s2 ? s2 : ""));
	exit(1);
}

void
usage(void)
{
	fprintf(stderr, "Usage: acctcom [options] [files]\n");
	fprintf(stderr, "\nWhere options can be:\n");
	diag("-b	read backwards through file");
	diag("-f	print the fork/exec flag and exit status");
	diag("-h	print hog factor (total-CPU-time/elapsed-time)");
	diag("-i	print I/O counts");
	diag("-k	show total Kcore minutes instead of memory size");
	diag("-m	show mean memory size");
	diag("-r	show CPU factor (user-time/(sys-time + user-time))");
	diag("-t	show separate system and user CPU times");
	diag("-v	don't print column headings");
	diag("-a	print average statistics of selected commands");
	diag("-q	print average statistics only");
	diag("-l line	\tshow processes belonging to terminal /dev/line");
	diag("-u user	\tshow processes belonging to user name or user ID");
	diag("-u #	\tshow processes executed by super-user");
	diag("-u ?	\tshow processes executed by unknown UID's");
	diag("-g group	show processes belonging to group name of group ID");
	diag("-s time	\tshow processes ending after time (hh[:mm[:ss]])");
	diag("-e time	\tshow processes starting before time");
	diag("-S time	\tshow processes starting after time");
	diag("-E time	\tshow processes ending before time");
	diag("-n regex	select commands matching the ed(1) regular expression");
	diag("-o file	\tdo not print, put selected pacct records into file");
	diag("-H factor	show processes that exceed hog factor");
	diag("-O sec	\tshow processes that exceed CPU system time sec");
	diag("-C sec	\tshow processes that exceed total CPU time sec");
	diag("-I chars	show processes that transfer more than char chars");
}