|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 11972 (0x2ec4)
Types: TextFile
Notes: UNIX file
Names: »sa.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/sa.c«
/*
* System accounting
* of command execution (in
* conjuction with acct system
* call.)
*/
#include <stdio.h>
#include <ctype.h>
#include <pwd.h>
#include <acct.h>
#include <times.h>
#include <dir.h>
#include <machine.h>
#define MIN 60 /* Seconds in a minute */
#define MINHZ (MIN*HZ) /* HZ in a minute */
#define CPUTIME 0 /* Sort by CPU time */
#define PERCALL 1 /* Sort by CPU time per call */
#define CALLS 2 /* Sort by numbers of calls */
#define NUSER 100 /* Maximum number of users */
#define NCOMM 100 /* Maximum number of commands */
#define NSORT NUSER
#define NCNAME (sizeof(ac.ac_comm)) /* Size of a command name) */
struct acct ac;
/*
* structure of /usr/adm/usracct
* for per-user type of `sa' information.
*/
struct svu {
unsigned svu_count; /* Number of processes measured */
short svu_uid; /* User number */
time_t svu_stime; /* Total system time (HZ) */
time_t svu_utime; /* Total user time */
time_t svu_etime; /* Total elapsed time (sec.)*/
} svu[NUSER];
/*
* For per command information as
* is stored in /usr/adm/savacct.
*/
struct svc {
unsigned svc_count; /* Number of calls */
char svc_comm[NCNAME]; /* Command name */
time_t svc_stime;
time_t svc_utime;
time_t svc_etime;
} svc[NCOMM];
struct svc *junkp, *otherp;
/*
* For the final sort. This is
* the combined information of
* the two structures above.
* I.e. either one or the other.
*/
struct sort {
unsigned s_count; /* Number of calls */
char s_comm[NCNAME]; /* Command name */
short s_uid; /* User number */
time_t s_key; /* Sort key */
time_t s_stime; /* System time */
time_t s_utime; /* User time */
time_t s_etime; /* Elapsed time */
} sort[NSORT];
char *acctf = "/usr/adm/acct"; /* Raw accounting file */
char *sacctf = "/usr/adm/savacct"; /* Summary file */
char *uacctf = "/usr/adm/usracct"; /* Per-user summary */
char junk[NCNAME] = "**junk**";
char other[NCNAME] = "***other";
int aflag; /* Move unlikely commands to "**other" */
int cflag; /* Give percentage of total time */
int jflag; /* Give seconds instead of minutes/call */
int lflag; /* Separate system & user times */
int mflag; /* Number of processes and CPU min. per user */
int rflag; /* Reverse sort order */
int sflag; /* Merge accounting file when done */
int tflag; /* Print ratio of real to CPU time */
int uflag; /* Print user-id and command name */
int vflag; /* Prompt if used fewer than `n' times */
int sortflag = CPUTIME; /* Sort mode */
long ctol();
time_t units();
struct svc *make();
struct svc *move();
char *uname();
int compar();
main(argc, argv)
char *argv[];
{
register char *ap;
while (argc>1 && *argv[1]=='-') {
for (ap = &argv[1][1]; *ap != '\0'; ap++)
switch (*ap) {
case 'a':
aflag = 1;
break;
case 'b':
sortflag = PERCALL;
break;
case 'c':
cflag = 1;
break;
case 'j':
jflag = 1;
break;
case 'l':
lflag = 1;
break;
case 'm':
mflag = 1;
break;
case 'n':
sortflag = CALLS;
break;
case 'r':
rflag = 1;
break;
case 's':
sflag = 1;
break;
case 't':
tflag = 1;
break;
case 'u':
uflag = 1;
break;
case 'v':
if (ap[1]>='0' && ap[1]<='9')
vflag = *++ap - '0';
break;
default:
usage();
}
argc--;
argv++;
}
if (argc == 2)
acctf = argv[1];
else if (argc > 2)
usage();
if (!uflag)
rsummary();
rraw(acctf);
if (sflag && !uflag)
samerge();
if (!uflag)
saprint();
exit(0);
}
/*
* Read the summary file of old accounting information
* from both user and command saved accounting files.
*/
rsummary()
{
register FILE *fp;
if ((fp = fopen(sacctf, "r")) != NULL) {
register struct svc *svcp;
for (svcp = svc; svcp < &svc[NCOMM]; svcp++)
if (fread(svcp, sizeof *svcp, 1, fp) != 1)
break;
if (svcp >= &svc[NCOMM])
fprintf(stderr, "%s is too large\n", sacctf);
fclose(fp);
}
if ((fp = fopen(uacctf, "r")) != NULL) {
register struct svu *svup;
for (svup = svu; svup < &svu[NUSER]; svup++)
if (fread(svup, sizeof *svup, 1, fp) != 1)
break;
if (svup >= &svu[NUSER])
fprintf(stderr, "%s is too large\n", uacctf);
fclose(fp);
}
}
/*
* Read the raw accounting.
*/
rraw(af)
char *af;
{
FILE *afp;
register struct svc *svcp;
if ((afp = fopen(af, "r")) == NULL) {
fprintf(stderr, "Cannot open raw accounting file `%s'\n", af);
exit(1);
}
while (fread(&ac, sizeof ac, 1, afp) == 1) {
if (uflag)
printf("%-*s %.*s\n", DIRSIZ, uname(ac.ac_uid),
NCNAME, ac.ac_comm);
else {
senter(&ac);
uenter(&ac);
}
}
fclose(afp);
/*
* Create (if not there) junk and other classes.
*/
if (aflag) {
otherp = make(other);
for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++) {
if (svcp == otherp)
continue;
if (svcp->svc_count==1 || unprintable(svcp->svc_comm))
otherp = move(svcp--, otherp);
}
}
if (vflag) {
junkp = make(junk);
for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++) {
if (svcp == junkp)
continue;
if (svcp->svc_count<=vflag && yes(svcp->svc_comm))
junkp = move(svcp--, junkp);
}
}
}
/*
* Merge system accounting info back
* into the two merged files and
* truncate the raw accounting file.
* Accounting probably should be
* turned off when `sa' is called
* if this is to be done.
*/
samerge()
{
register FILE *fp;
if ((fp = fopen(sacctf, "w")) != NULL) {
register struct svc *svcp;
for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++)
if (fwrite(svcp, sizeof *svcp, 1, fp) != 1)
saerr("%s: write error", sacctf);
fclose(fp);
} else
saerr("cannot rewrite %s", sacctf);
if ((fp = fopen(uacctf, "w")) != NULL) {
register struct svu *svup;
for (svup = svu; svup<&svu[NUSER] && svup->svu_count; svup++)
if (fwrite(svup, sizeof *svup, 1, fp) != 1)
saerr("%s: write error", uacctf);
fclose(fp);
} else
saerr("cannot rewrite %s", uacctf);
if ((fp = fopen(acctf, "w")) == NULL)
saerr("cannot truncate %s", acctf);
fclose(fp);
}
/*
* Output the accounting
* information according to
* sorting and printing options.
*/
saprint()
{
register struct sort *sp;
time_t tottime;
if (mflag)
userenter(); else
commenter();
for (sp = sort; sp<&sort[NSORT] && sp->s_count; sp++)
if (sortflag == CPUTIME)
sp->s_key = sp->s_stime+sp->s_utime;
else if (sortflag == PERCALL)
sp->s_key = (sp->s_stime+sp->s_utime)/sp->s_count;
else /* # calls */
sp->s_key = sp->s_count;
qsort(sort, sp-sort, sizeof *sp, compar);
if (mflag)
printf("%-*s #PROC", DIRSIZ, ""); else
printf("%-*s #CALL", NCNAME, "");
if (lflag)
printf(" USER SYS"); else
printf(" CPU");
printf(" REAL");
if (cflag)
printf(" CPU %% ");
if (tflag)
printf(" CPU/REAL %%");
putchar('\n');
if (cflag) {
tottime = 0;
for (sp = sort; sp<&sort[NSORT] && sp->s_count; sp++) {
tottime += sp->s_stime;
tottime += sp->s_utime;
}
}
for (sp = sort; sp<&sort[NSORT] && sp->s_count; sp++) {
if (mflag)
printf("%-*s", DIRSIZ, uname(sp->s_uid)); else
printf("%-*s", NCNAME, sp->s_comm);
printf(" %5d", sp->s_count);
if (lflag)
printf(" %5D %5D", units(sp->s_utime, sp),
units(sp->s_stime, sp));
else
printf(" %5D", units(sp->s_utime+sp->s_stime, sp));
printf(" %5D", units(sp->s_etime*HZ, sp));
if (cflag)
percent(sp->s_utime+sp->s_stime, tottime);
if (tflag) {
printf(" ");
percent(sp->s_utime+sp->s_stime, sp->s_etime*HZ);
}
putchar('\n');
}
}
/*
* Enter user information for the sort.
*/
userenter()
{
register struct sort *sp;
register struct svu *svup;
sp = sort;
svup = svu;
while (svup < &svu[NCOMM] && svup->svu_count) {
sp->s_count = svup->svu_count;
sp->s_uid = svup->svu_uid;
sp->s_stime = svup->svu_stime;
sp->s_utime = svup->svu_utime;
sp->s_etime = svup->svu_etime;
sp++;
svup++;
}
/*
free(svc);
*/
}
/*
* Enter the commands into the list for
* sorting.
*/
commenter()
{
register struct sort *sp;
register struct svc *svcp;
sp = sort;
svcp = svc;
while (svcp < &svc[NCOMM] && svcp->svc_count) {
sp->s_count = svcp->svc_count;
strncpy(sp->s_comm, svcp->svc_comm, NCNAME);
sp->s_stime = svcp->svc_stime;
sp->s_utime = svcp->svc_utime;
sp->s_etime = svcp->svc_etime;
sp++;
svcp++;
}
/*
free(svu);
*/
}
/*
* Enter this accounting entry into
* the command table for savacct.
*/
senter(ap)
register struct acct *ap;
{
register struct svc *svcp;
for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++)
if (strncmp(svcp->svc_comm, ap->ac_comm, NCNAME) == 0)
break;
if (svcp >= &svc[NCOMM])
saerr("Command table overflow");
if (svcp->svc_count++ == 0)
strncpy(svcp->svc_comm, ap->ac_comm, NCNAME);
svcp->svc_stime += ctol(ap->ac_stime);
svcp->svc_utime += ctol(ap->ac_utime);
svcp->svc_etime += ctol(ap->ac_etime);
}
/*
* Enter this accounting entry into
* the user table for usracct.
*/
uenter(ap)
register struct acct *ap;
{
register struct svu *svup;
for (svup = svu; svup<&svu[NUSER] && svup->svu_count; svup++)
if (svup->svu_uid == ap->ac_uid)
break;
if (svup >= &svu[NUSER])
saerr("User table overflow");
svup->svu_count++;
svup->svu_uid = ap->ac_uid;
svup->svu_stime += ctol(ap->ac_stime);
svup->svu_utime += ctol(ap->ac_utime);
svup->svu_etime += ctol(ap->ac_etime);
}
/*
* Make a new zero entry with
* the indicated name.
*/
struct svc *
make(name)
register char *name;
{
register struct svc *svcp;
for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++)
if (strncmp(name, svcp->svc_comm, NCNAME) == 0)
return (svcp);
if (svcp >= &svc[NCOMM])
saerr("out of room for %s", name);
strncpy(svcp->svc_comm, name, NCNAME);
return (svcp);
}
/*
* Move an entry to one of the deprecated
* places.
*/
struct svc *
move(svcp, depp)
register struct svc *svcp;
struct svc *depp;
{
depp->svc_count += svcp->svc_count;
depp->svc_stime += svcp->svc_stime;
depp->svc_utime += svcp->svc_utime;
depp->svc_etime += svcp->svc_etime;
while (svcp->svc_count) {
if (svcp == depp)
depp--;
bcopy(svcp+1, svcp, sizeof *svcp);
svcp++;
}
(svcp-1)->svc_count = 0;
return (depp);
}
/*
* Return the user-name for
* a user-ID.
*/
char *
uname(uid)
short uid;
{
register struct passwd *pwp;
static char ubuf[15];
if ((pwp = getpwuid(uid)) != NULL)
return (pwp->pw_name);
return (sprintf(ubuf, "%d", uid));
}
/*
* Block copy of `n' bytes.
*/
bcopy(f, t, n)
register char *f, *t;
register unsigned n;
{
if (n)
do {
*t++ = *f++;
} while (--n);
}
/*
* Return non-zero if an NCNAME-length string
* is unprintable.
*/
unprintable(s)
register char *s;
{
register int n = NCNAME;
register int c;
do {
if ((c = *s++) == '\0')
break;
if (!isascii(c) || !isprint(c))
return (1);
} while (--n);
return (0);
}
/*
* Ask whether or not to delete and
* entry. Return 1 if yes.
*/
yes(s)
register char *s;
{
register int c, ans = 0;
printf("%.*s ? ", NCNAME, s);
if ((c = getchar()) == 'y')
ans = 1;
while (c!='\n' && c!=EOF)
c = getchar();
return (ans);
}
/*
* Qsort compare routine.
*/
compar(sp1, sp2)
register struct sort *sp1, *sp2;
{
register int rval;
if (sp1->s_key == sp2->s_key)
return (0);
rval = 1;
if (sp1->s_key > sp2->s_key)
rval = -1;
if (rflag)
return (-rval);
return (rval);
}
/*
* Return the number of CPU minutes from CPU HZ.
* or if `-j', return seconds/call.
*/
time_t
units(hz, sp)
time_t hz;
register struct sort *sp;
{
if (jflag)
return ((hz + HZ/2) / (HZ*sp->s_count)); else
return ((hz + MINHZ/2) / MINHZ);
}
/*
* Print the percentage of
* `t' out of `total'.
*/
percent(t, total)
time_t t, total;
{
t *= 100;
printf("%3D.", t/total);
t %= total;
if (t < 0)
t = -t;
printf("%1D ", t*10/total);
}
usage()
{
fprintf(stderr, "Usage: sa [-abcjlmnrstu] [-v[n]] [file]\n");
exit(1);
}
/* VARARGS */
saerr(x)
{
fprintf(stderr, "sa: %r", &x);
putc('\n', stderr);
exit(1);
}