# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (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 2015 Gary Mills # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= acctcms acctcom acctcon acctcon1 acctcon2 \ acctdisk acctdusg acctmerg accton acctprc acctprc1 \ acctprc2 acctwtmp closewtmp fwtmp \ wtmpfix utmp2wtmp # Hammerhead: removed 'acct' (SysV-init wrapper; SMF/SysV gone — zyginit doesn't # invoke /etc/init.d/acct). The acct subsystem itself stays (accton, ckpacct, # runacct, etc. under /usr/lib/acct). SHFILE1= chargefee ckpacct dodisk lastlogin\ monacct nulladm prctmp prdaily prtacct \ remove runacct shutacct startup turnacct SHFILE2= ptecms.awk ptelus.awk SUBDIRS= lib ALL= $(PROG) $(SHFILE1) $(SHFILE2) holidays SRCS= $(PROG:%=%.c) SHFILE1SRCS= $(SHFILE1:%=%.sh) BINPROG= acctcom LIBPROG= acctcms acctcon acctcon1 acctcon2 acctdisk \ acctdusg acctmerg accton acctprc acctprc1 acctprc2 \ acctwtmp closewtmp fwtmp utmp2wtmp \ wtmpfix chargefee ckpacct dodisk monacct \ lastlogin nulladm prctmp prdaily prtacct \ remove runacct shutacct startup turnacct \ ptecms.awk ptelus.awk ETCPROG= holidays include ../Makefile.cmd all: TARGET= all install: TARGET= install clean: TARGET= clean clobber: TARGET= clobber lint: TARGET= lint acctcom : LDLIBS += lib/a.a acctcms acctcon acctcon1 acctmerg acctprc1 acctprc : LDLIBS += lib/a.a acctdusg: CPPFLAGS += -D_FILE_OFFSET_BITS=64 acctdusg: LDLIBS += -lcmdutils LIBACCTD= $(ROOTLIB)/acct ETCACCTD= $(ROOTETC)/acct VARADMD= $(ROOT)/var/adm ACCTDIR= $(VARADMD)/acct ACCTSUBDIRS= $(ACCTDIR)/nite $(ACCTDIR)/fiscal $(ACCTDIR)/sum WKDIRS= $(ACCTDIR) $(ACCTSUBDIRS) DIRS= $(LIBACCTD) $(ETCACCTD) $(WKDIRS) USRBINPROG= $(BINPROG:%=$(ROOTBIN)/%) LIBACCTPROG= $(LIBPROG:%=$(LIBACCTD)/%) ETCACCTPROG= $(ETCPROG:%=$(ETCACCTD)/%) $(LIBACCTD) : DIRMODE= 755 $(ETCACCTD) : DIRMODE= 755 $(WKDIRS) : DIRMODE= 775 $(LIBACCTD)/accton : FILEMODE= 04755 $(ETCACCTPROG) : FILEMODE= 0644 CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-parentheses CERRWARN += -Wno-unused-variable CERRWARN += -Wno-address CERRWARN += -Wno-type-limits # Hammerhead: Suppress pointer/int cast warnings in legacy acct code CERRWARN += -Wno-pointer-to-int-cast CERRWARN += -Wno-int-to-pointer-cast # not linted SMATCH=off .KEEP_STATE: .PARALLEL: $(ALL) all: $(SUBDIRS) .WAIT $(ALL) install: all .WAIT $(DIRS) .WAIT $(USRBINPROG) $(LIBACCTPROG) $(ETCACCTPROG) THIS_YEAR = $(shell date +%Y) holidays: FRC @if grep $(THIS_YEAR) holidays > /dev/null 2>&1;\ then \ :;\ else \ $(ECHO) "building holidays";\ ( \ $(ECHO) "* @(#)holidays\tJanuary 1, `date +%Y`";\ $(ECHO) "*";\ $(ECHO) "* Prime/Nonprime Table for UNIX Accounting System";\ $(ECHO) "*";\ $(ECHO) "* Curr\tPrime\tNon-Prime";\ $(ECHO) "* Year\tStart\tStart";\ $(ECHO) "*";\ $(ECHO) " `date +%Y`\t0800\t1800";\ $(ECHO) "*";\ $(ECHO) "* only the first column (month/day) is significiant.";\ $(ECHO) "*";\ $(ECHO) "* month/day\tCompany";\ $(ECHO) "* \t\tHoliday";\ $(ECHO) "*";\ $(ECHO) "1/1\t\tNew Years Day";\ $(ECHO) "7/4\t\tIndep. Day";\ $(ECHO) "12/25\t\tChristmas" ) > holidays;\ fi $(DIRS): $(INS.dir) $(LIBACCTD)/% : % $(INS.file) $(ETCACCTD)/% : % $(INS.file) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: clean: $(SUBDIRS) clobber: $(SUBDIRS) $(RM) $(PROG) $(SHFILE1) holidays lint: $(SUBDIRS) $(LINT.c) $(SRCS) /* * 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 */ /* * acctcms [-a] [-c] [-j] [-n] [-s] [-p] [-o] [-t] [file...] * summarize per-process accounting * -a output in ascii, rather than [pt]cms.h format * -c sort by total cpu, rather than total kcore-minutes * -j anything used only once -> ***other * -n sort by number of processes * -s any following files already in pcms.h format * -p output prime time command summary (only with -a) * -o output non-prime time (offshift) command summary (only * with -a option) * -t process records in total (old) style (tcms.h) format * file file in [pt]cms.h (if -s seen already) or acct.h (if not) * expected use: * acctcms /var/adm/pacct? > today; acctcms -s old today >new * cp new old; rm new * acctcms -a today; acctcms -a old */ #include #include #include #include "acctdef.h" #include #include #include #include int csize = CSIZE; /* * Total cms records format */ struct tcms { char tcm_comm[8]; /* command name */ long tcm_pc; /* number of processes */ float tcm_cpu; /* cpu time(min) */ float tcm_real; /* real time(min) */ float tcm_kcore; /* kcore-minutes */ ulong_t tcm_io; /* chars transferred */ ulong_t tcm_rw; /* blocks read */ } ; struct tcms *tcm; /* * prime/nonprime CMS record format */ struct pcms { char pcm_comm[8]; /* command name */ long pcm_pc[2]; /* number of processes */ float pcm_cpu[2]; /* cpu time(min) */ float pcm_real[2]; /* real time(min) */ float pcm_kcore[2]; /* kcore-minutes */ float pcm_io[2]; /* chars transferred */ float pcm_rw[2]; /* blocks read */ } ; struct pcms *pcm; struct tcms tcmtmp = {{'*','*','*','o','t','h','e','r'}}; struct pcms pcmtmp = {{'*','*','*','o','t','h','e','r'}}; int aflg; int cflg; int jflg; int nflg; int sflg; int pflg; int oflg; int tflg; int errflg; #ifdef uts float expand(); #else ulong_t expand(); #endif void outputc(void); void totprnt(struct pcms *); void pprint(struct pcms *); void prnt(struct pcms *, int); void print(struct pcms *); void outputa(void); void toutptc(void); void tprint(struct tcms *); void toutpta(void); int ncmp(struct pcms *, struct pcms *); int tncmp(struct tcms *, struct tcms *); int tccmp(struct tcms *, struct tcms *); int tkcmp(struct tcms *, struct tcms *); int ccmp(struct pcms *, struct pcms *); int kcmp(struct pcms *, struct pcms *); void tdofile(char *); void dofile(char *); void tfixjunk(void); void fixjunk(void); void tcmadd(struct tcms *, struct tcms *); void pcmadd(struct pcms *, struct pcms *); void tsqueeze(void); void squeeze(void); /* Format specification for ASCII printing */ char *fmtcmd = "%-8.8s", *fmtcnt = "%8ld", *fmtkcore = " %11.2f", *fmtcpu = " %9.2f", *fmtreal = " %12.2f", *fmtmsz = " %7.2f", *fmtmcpu = " %6.2f", *fmthog = " %5.2f", *fmtcharx = " %12.0f", *fmtblkx = " %10.0f" ; int main(int argc, char **argv) { int c; while((c = getopt(argc, argv, "acjnspot")) != EOF) switch(c) { case 'a': aflg++; continue; case 'c': cflg++; continue; case 'j': jflg++; continue; case 'n': nflg++; continue; case 's': sflg++; continue; case 'p': pflg++; continue; case 'o': oflg++; continue; case 't': tflg++; continue; default: errflg++; continue; } if(errflg){ fprintf(stderr, "Usage: %s [-acjnspot] [file ...]\n", argv[0]); exit(1); } if(tflg) { if( (tcm = (struct tcms *)calloc(CSIZE, sizeof(struct tcms))) == NULL) { fprintf(stderr, "%s: Cannot allocate memory\n", argv[0]); exit(5); } for(; optind < argc; optind++) tdofile(argv[optind]); if (jflg) tfixjunk(); tsqueeze(); qsort(tcm, csize, sizeof(tcm[0]), (int (*)(const void *, const void *)) ( nflg ? tncmp: (cflg? tccmp: tkcmp))); if (aflg) toutpta(); else toutptc(); } else { if( (pcm = (struct pcms *)calloc(CSIZE, sizeof(struct pcms))) == NULL) { fprintf(stderr, "%s: Cannot allocate memory\n", argv[0]); exit(6); } for(; optind < argc; optind++) dofile(argv[optind]); if (jflg) fixjunk(); squeeze(); qsort(pcm, csize, sizeof(pcm[0]), (int (*)(const void *, const void *)) (nflg? ncmp: (cflg? ccmp: kcmp))); if (aflg) outputa(); else outputc(); } exit(0); } void tdofile(char *fname) { struct tcms cmt; union { struct acct ab; /* SVR4 acct structure */ struct o_acct oab; /* SVR3 acct structure */ } acct; int ver = 0; ulong_t mem; ulong_t cpu; ulong_t real; if (freopen(fname, "r", stdin) == NULL) { fprintf(stderr, "acctcms: cannot open %s\n", fname); return; } if (sflg) while (fread(&cmt, sizeof(cmt), 1, stdin) == 1) tenter(&cmt); else { if (fread(&acct.ab, sizeof(acct.ab), 1, stdin) == 1) /* check for expanded account structure flag */ if (acct.ab.ac_flag & AEXPND) ver = 2; /* 4.0 acct file */ else ver = 1; /* SVR3.x acct file */ rewind(stdin); /* reset file pointer */ switch(ver) { default: /* this can't happen */ fprintf(stderr, "acctcms: encountered bad version number\n"); return; case 1 : while (fread(&acct.oab, sizeof(acct.oab), 1, stdin) == 1) { CPYN(cmt.tcm_comm, acct.oab.ac_comm); cmt.tcm_pc = 1; cpu = expand(acct.oab.ac_stime)+ expand(acct.oab.ac_utime); cmt.tcm_cpu = MINT(cpu); real = expand(acct.oab.ac_etime); cmt.tcm_real = MINT(real); mem = expand(acct.oab.ac_mem); cmt.tcm_kcore = MINT(KCORE(mem)); cmt.tcm_io = expand(acct.oab.ac_io); cmt.tcm_rw = expand(acct.oab.ac_rw); tenter(&cmt); } break; case 2 : while (fread(&acct.ab, sizeof(acct.ab), 1, stdin) == 1) { CPYN(cmt.tcm_comm, acct.ab.ac_comm); cmt.tcm_pc = 1; cpu = expand(acct.oab.ac_stime)+ expand(acct.oab.ac_utime); cmt.tcm_cpu = MINT(cpu); real = expand(acct.ab.ac_etime); cmt.tcm_real = MINT(real); mem = expand(acct.ab.ac_mem); cmt.tcm_kcore = MINT(KCORE(mem)); cmt.tcm_io = expand(acct.ab.ac_io); cmt.tcm_rw = expand(acct.ab.ac_rw); tenter(&cmt); } break; } } } void dofile(char *fname) { union { struct acct ab; struct o_acct oab; } acct; struct pcms pcmt; double ratio; long elaps[2]; ulong_t etime; double dtmp; unsigned long ltmp; ulong_t mem; ulong_t cpu; ulong_t real; if (freopen(fname, "r", stdin) == NULL) { fprintf(stderr, "acctcms: cannot open %s\n", fname); return; } if (sflg) while (fread(&pcmt, sizeof(pcmt), 1, stdin) == 1) enter(&pcmt); else { int ver = 0; if (fread(&acct.ab, sizeof(acct.ab), 1, stdin) == 1) /* check for expanded account structure flag */ if (acct.ab.ac_flag & AEXPND) ver = 2; /* 4.0 acct file */ else ver = 1; /* SVR3.x acct file */ rewind(stdin); /* reset file pointer */ switch(ver) { default : /* this can't happen */ fprintf(stderr, "acctcms: encountered bad version number\n"); return; case 1 : while (fread(&acct.oab, sizeof(acct.oab), 1, stdin) == 1) { CPYN(pcmt.pcm_comm, acct.oab.ac_comm); /* ** Approximate P/NP split as same as elapsed time */ if((etime = SECS(expand(acct.oab.ac_etime))) == 0) etime = 1; if (pnpsplit(acct.oab.ac_btime, etime, elaps) == 0) { (void) fprintf(stderr, "acctcms: could " "not calculate prime/non-prime " "hours\n"); exit(1); } ratio = (double)elaps[PRIME]/(double)etime; if(elaps[PRIME] > elaps[NONPRIME]) { pcmt.pcm_pc[PRIME] = 1; pcmt.pcm_pc[NONPRIME] = 0; } else { pcmt.pcm_pc[PRIME] = 0; pcmt.pcm_pc[NONPRIME] = 1; } cpu = expand(acct.oab.ac_stime)+ expand(acct.oab.ac_utime); dtmp = MINT(cpu); pcmt.pcm_cpu[PRIME] = dtmp * ratio; pcmt.pcm_cpu[NONPRIME] = (ratio == 1.0) ? 0.0 : (dtmp - pcmt.pcm_cpu[PRIME]); real = expand(acct.oab.ac_etime); dtmp = MINT(real); pcmt.pcm_real[PRIME] = dtmp * ratio; pcmt.pcm_real[NONPRIME] = (ratio == 1.0) ? 0.0 : (dtmp - pcmt.pcm_real[PRIME]); mem = expand(acct.oab.ac_mem); dtmp = MINT(KCORE(mem)); pcmt.pcm_kcore[PRIME] = dtmp * ratio; pcmt.pcm_kcore[NONPRIME] = (ratio == 1.0) ? 0.0 : (dtmp - pcmt.pcm_kcore[PRIME]); ltmp = expand(acct.oab.ac_io); pcmt.pcm_io[PRIME] = (double)ltmp * ratio; pcmt.pcm_io[NONPRIME] = (ratio == 1.0) ? 0.0 : ((double)ltmp - pcmt.pcm_io[PRIME]); ltmp = expand(acct.oab.ac_rw); pcmt.pcm_rw[PRIME] = (double)ltmp * ratio; pcmt.pcm_rw[NONPRIME] = (ratio == 1.0) ? 0.0 : ((double)ltmp - pcmt.pcm_rw[PRIME]); enter(&pcmt); } break; case 2 : while (fread(&acct.ab, sizeof(acct.ab), 1, stdin) == 1) { CPYN(pcmt.pcm_comm, acct.ab.ac_comm); /* ** Approximate P/NP split as same as elapsed time */ if((etime = SECS(expand(acct.ab.ac_etime))) == 0) etime = 1; if(pnpsplit(acct.ab.ac_btime, etime, elaps) == 0) { fprintf(stderr, "acctcms: could not calculate prime/non-prime hours\n"); exit(1); } ratio = (double)elaps[PRIME]/(double)etime; if(elaps[PRIME] > elaps[NONPRIME]) { pcmt.pcm_pc[PRIME] = 1; pcmt.pcm_pc[NONPRIME] = 0; } else { pcmt.pcm_pc[PRIME] = 0; pcmt.pcm_pc[NONPRIME] = 1; } cpu = expand(acct.ab.ac_stime)+ expand(acct.ab.ac_utime); dtmp = MINT(cpu); pcmt.pcm_cpu[PRIME] = dtmp * ratio; pcmt.pcm_cpu[NONPRIME] = (ratio == 1.0) ? 0.0 : (dtmp - pcmt.pcm_cpu[PRIME]); real = expand(acct.ab.ac_etime); dtmp = MINT(real); pcmt.pcm_real[PRIME] = dtmp * ratio; pcmt.pcm_real[NONPRIME] = (ratio == 1.0) ? 0.0 : (dtmp - pcmt.pcm_real[PRIME]); mem = expand(acct.ab.ac_mem); dtmp = MINT(KCORE(mem)); pcmt.pcm_kcore[PRIME] = dtmp * ratio; pcmt.pcm_kcore[NONPRIME] = (ratio == 1.0) ? 0.0 : (dtmp - pcmt.pcm_kcore[PRIME]); ltmp = expand(acct.ab.ac_io); pcmt.pcm_io[PRIME] = (double)ltmp * ratio; pcmt.pcm_io[NONPRIME] = (ratio == 1.0) ? 0.0 : ((double)ltmp - pcmt.pcm_io[PRIME]); ltmp = expand(acct.ab.ac_rw); pcmt.pcm_rw[PRIME] = (double)ltmp * ratio; pcmt.pcm_rw[NONPRIME] = (ratio == 1.0) ? 0.0 : ((double)ltmp - pcmt.pcm_rw[PRIME]); enter(&pcmt); } break; } } } int tenter(struct tcms *p) { int i; int j; struct tcms *ntcm; for (i = j = 0; j < sizeof(p->tcm_comm); j++) { if (p->tcm_comm[j] && p->tcm_comm[j] <= 037) p->tcm_comm[j] = '?'; i = i*7 + p->tcm_comm[j]; /* hash function */ } if (i < 0) i = -i; for (i %= csize, j = 0; tcm[i].tcm_comm[0] && j != csize; i = (i+1)%csize, j++) if (EQN(p->tcm_comm, tcm[i].tcm_comm)) break; if(j == csize) { if ((ntcm = (struct tcms *) realloc(tcm, (csize + CSIZE - 1) * sizeof (struct tcms))) == NULL) { fprintf(stderr, "acctcms: Cannot reallocate memory (tcm)\n"); return(-1); } else { memset(&ntcm[csize], 0, CSIZE - 1); tcm = ntcm; if (!EQN(p->tcm_comm, tcm[i].tcm_comm)) i = csize; csize = csize + CSIZE - 1; } } if (tcm[i].tcm_comm[0] == 0) CPYN(tcm[i].tcm_comm, p->tcm_comm); tcmadd(&tcm[i], p); return(i); } int enter(struct pcms *p) { int i; int j; struct pcms *npcm; for (i = j = 0; j < sizeof(p->pcm_comm); j++) { if (p->pcm_comm[j] && p->pcm_comm[j] <= 037) p->pcm_comm[j] = '?'; i = i*7 + p->pcm_comm[j]; /* hash function */ } if (i < 0) i = -i; for (i %= csize, j = 0; pcm[i].pcm_comm[0] && j != csize; i = (i+1)%csize, j++) if (EQN(p->pcm_comm, pcm[i].pcm_comm)) break; if(j == csize) { if ((npcm = (struct pcms *) realloc(pcm, (csize + CSIZE - 1) * sizeof (struct pcms))) == NULL) { fprintf(stderr, "acctcms: Cannot reallocate memory (pcm)\n"); return(-1); } else { memset(&npcm[csize], 0, CSIZE - 1); pcm = npcm; if (!EQN(p->pcm_comm, pcm[i].pcm_comm)) i = csize; csize = csize + CSIZE - 1; } } if (pcm[i].pcm_comm[0] == 0) CPYN(pcm[i].pcm_comm, p->pcm_comm); pcmadd(&pcm[i], p); return(i); } void tfixjunk(void) /* combine commands used only once */ { int i, j; j = tenter(&tcmtmp); for (i = 0; i < csize; i++) if (i != j && tcm[i].tcm_comm[0] && tcm[i].tcm_pc <= 1) { tcmadd(&tcm[j], &tcm[i]); tcm[i].tcm_comm[0] = 0; } } void fixjunk(void) /* combine commands used only once */ { int i, j; j = enter(&pcmtmp); for (i = 0; i < csize; i++) if (i != j && pcm[i].pcm_comm[0] && (pcm[i].pcm_pc[PRIME] + pcm[i].pcm_pc[NONPRIME]) <= 1) { pcmadd(&pcm[j], &pcm[i]); pcm[i].pcm_comm[0] = 0; } } void tcmadd(struct tcms *p1, struct tcms *p2) { p1->tcm_pc += p2->tcm_pc; p1->tcm_cpu = p1->tcm_cpu + p2->tcm_cpu; p1->tcm_real = p1->tcm_real + p2->tcm_real; p1->tcm_kcore = p1->tcm_kcore + p2->tcm_kcore; p1->tcm_io += p2->tcm_io; p1->tcm_rw += p2->tcm_rw; } void pcmadd(struct pcms *p1, struct pcms *p2) { p1->pcm_pc[PRIME] += p2->pcm_pc[PRIME]; p1->pcm_pc[NONPRIME] += p2->pcm_pc[NONPRIME]; p1->pcm_cpu[PRIME] += p2->pcm_cpu[PRIME]; p1->pcm_cpu[NONPRIME] += p2->pcm_cpu[NONPRIME]; p1->pcm_real[PRIME] += p2->pcm_real[PRIME]; p1->pcm_real[NONPRIME] += p2->pcm_real[NONPRIME]; p1->pcm_kcore[PRIME] += p2->pcm_kcore[PRIME]; p1->pcm_kcore[NONPRIME] += p2->pcm_kcore[NONPRIME]; p1->pcm_io[PRIME] += p2->pcm_io[PRIME]; p1->pcm_io[NONPRIME] += p2->pcm_io[NONPRIME]; p1->pcm_rw[PRIME] += p2->pcm_rw[PRIME]; p1->pcm_rw[NONPRIME] += p2->pcm_rw[NONPRIME]; } void tsqueeze(void) /* get rid of holes in hash table */ { int i, k; for (i = k = 0; i < csize; i++) if (tcm[i].tcm_comm[0]) { CPYN(tcm[k].tcm_comm, tcm[i].tcm_comm); tcm[k].tcm_pc = tcm[i].tcm_pc; tcm[k].tcm_cpu = tcm[i].tcm_cpu; tcm[k].tcm_real = tcm[i].tcm_real; tcm[k].tcm_kcore = tcm[i].tcm_kcore; tcm[k].tcm_io = tcm[i].tcm_io; tcm[k].tcm_rw = tcm[i].tcm_rw; k++; } csize = k; } void squeeze(void) /* get rid of holes in hash table */ { int i, k; for (i = k = 0; i < csize; i++) if (pcm[i].pcm_comm[0]) { CPYN(pcm[k].pcm_comm, pcm[i].pcm_comm); pcm[k].pcm_pc[PRIME] = pcm[i].pcm_pc[PRIME]; pcm[k].pcm_pc[NONPRIME] = pcm[i].pcm_pc[NONPRIME]; pcm[k].pcm_cpu[PRIME] = pcm[i].pcm_cpu[PRIME]; pcm[k].pcm_cpu[NONPRIME] = pcm[i].pcm_cpu[NONPRIME]; pcm[k].pcm_real[PRIME] = pcm[i].pcm_real[PRIME]; pcm[k].pcm_real[NONPRIME] = pcm[i].pcm_real[NONPRIME]; pcm[k].pcm_kcore[PRIME] = pcm[i].pcm_kcore[PRIME]; pcm[k].pcm_kcore[NONPRIME] = pcm[i].pcm_kcore[NONPRIME]; pcm[k].pcm_io[PRIME] = pcm[i].pcm_io[PRIME]; pcm[k].pcm_io[NONPRIME] = pcm[i].pcm_io[NONPRIME]; pcm[k].pcm_rw[PRIME] = pcm[i].pcm_rw[PRIME]; pcm[k].pcm_rw[NONPRIME] = pcm[i].pcm_rw[NONPRIME]; k++; } csize = k; } int tccmp(struct tcms *p1, struct tcms *p2) { if (p1->tcm_cpu == p2->tcm_cpu) return(0); return ((p2->tcm_cpu > p1->tcm_cpu)? 1 : -1); } int ccmp(struct pcms *p1, struct pcms *p2) { int index; if( (pflg && oflg) || (!pflg && !oflg) ) { if (p1->pcm_cpu[PRIME] + p1->pcm_cpu[NONPRIME] == p2->pcm_cpu[PRIME] + p2->pcm_cpu[NONPRIME]) return(0); return ((p2->pcm_cpu[PRIME] + p2->pcm_cpu[NONPRIME] > p1->pcm_cpu[PRIME] + p1->pcm_cpu[NONPRIME])? 1 : -1); } index = pflg ? PRIME : NONPRIME; if (p1->pcm_cpu[index] == p2->pcm_cpu[index]) return(0); return ((p2->pcm_cpu[index] > p1->pcm_cpu[index])? 1 : -1); } int tkcmp(struct tcms *p1, struct tcms *p2) { if (p1->tcm_kcore == p2->tcm_kcore) return(0); return ((p2->tcm_kcore > p1->tcm_kcore)? 1 : -1); } int kcmp(struct pcms *p1, struct pcms *p2) { int index; if( (pflg && oflg) || (!pflg && !pflg) ){ if (p1->pcm_kcore[PRIME] + p1->pcm_kcore[NONPRIME] == p2->pcm_kcore[PRIME] + p2->pcm_kcore[NONPRIME]) return(0); return ((p2->pcm_kcore[PRIME] + p2->pcm_kcore[NONPRIME] > p1->pcm_kcore[PRIME] + p1->pcm_kcore[NONPRIME])? 1 : -1); } index = pflg ? PRIME : NONPRIME; if (p1->pcm_kcore[index] == p2->pcm_kcore[index]) return(0); return ((p2->pcm_kcore[index] > p1->pcm_kcore[index])? 1 : -1); } int tncmp(struct tcms *p1, struct tcms *p2) { if (p1->tcm_pc == p2->tcm_pc) return(0); return ((p2->tcm_pc > p1->tcm_pc)? 1 : -1); } int ncmp(struct pcms *p1, struct pcms *p2) { int index; if( (pflg && oflg) || (!pflg && !oflg) ) { if (p1->pcm_pc[PRIME] + p1->pcm_pc[NONPRIME] == p2->pcm_pc[PRIME] + p2->pcm_pc[NONPRIME]) return(0); return ((p2->pcm_pc[PRIME] + p2->pcm_pc[NONPRIME] > p1->pcm_pc[PRIME] + p1->pcm_pc[NONPRIME])? 1 : -1); } index = pflg ? PRIME : NONPRIME; if (p1->pcm_pc[index] == p2->pcm_pc[index]) return(0); return ((p2->pcm_pc[index] > p1->pcm_pc[index])? 1 : -1); } char thd1[] = "COMMAND NUMBER TOTAL TOTAL TOTAL MEAN MEAN HOG CHARS BLOCKS\n"; char thd2[] = "NAME CMDS KCOREMIN CPU-MIN REAL-MIN SIZE-K CPU-MIN FACTOR TRNSFD READ\n"; void toutpta(void) { int i; printf(thd1); printf(thd2); printf("\n"); for (i = 0; i < csize; i++) tcmadd(&tcmtmp, &tcm[i]); CPYN(tcmtmp.tcm_comm, "TOTALS"); tprint(&tcmtmp); printf("\n"); for (i = 0; i < csize; i++) tprint(&tcm[i]); } void tprint(struct tcms *p) { printf("%-8.8s", p->tcm_comm); printf(" %7ld", p->tcm_pc); printf(" %11.2f", p->tcm_kcore); printf(" %10.2f", p->tcm_cpu); printf(" %12.2f", p->tcm_real); if(p->tcm_cpu == 0) p->tcm_cpu = 1; printf(" %6.2f", p->tcm_kcore/p->tcm_cpu); if(p->tcm_pc == 0) p->tcm_pc = 1; printf(" %7.2f", p->tcm_cpu/p->tcm_pc); if (p->tcm_real == 0) p->tcm_real = 1; printf(" %8.2f", p->tcm_cpu/p->tcm_real); printf(" %11lu", p->tcm_io); printf(" %11lu\n", p->tcm_rw); } void toutptc(void) { int i; for (i = 0; i < csize; i++) fwrite(&tcm[i], sizeof(tcm[i]), 1, stdout); } char hd1[] = "COMMAND NUMBER TOTAL TOTAL TOTAL MEAN MEAN HOG CHARS BLOCKS\n"; char hd2[] = "NAME CMDS KCOREMIN CPU-MIN REAL-MIN SIZE-K CPU-MIN FACTOR TRNSFD READ\n"; char hd3[] = "COMMAND NUMBER TOTAL CPU-MIN REAL-MIN MEAN MEAN HOG CHARS BLOCKS\n"; char hd4[] = "NAME (P) (NP) KCOREMIN (P) (NP) (P) (NP) SIZE-K CPU-MIN FACTOR TRNSFD READ\n"; char hdprime[] = " PRIME TIME COMMAND SUMMARY\n"; char hdnonprime[] = " NON-PRIME TIME COMMAND SUMMARY\n"; char hdtot[] = " TOTAL COMMAND SUMMARY\n"; char hdp[] = " PRIME/NON-PRIME TIME COMMAND SUMMARY\n"; void outputa(void) { int i; if( pflg && oflg ) printf(hdp); else if(pflg) printf(hdprime); else if(oflg) printf(hdnonprime); else printf(hdtot); if( (!pflg && !oflg) || (pflg ^ oflg)) { printf(hd1); printf(hd2); } else { printf(hd3); printf(hd4); } printf("\n"); for (i = 0; i < csize; i++) pcmadd(&pcmtmp, &pcm[i]); CPYN(pcmtmp.pcm_comm, "TOTALS"); print(&pcmtmp); printf("\n"); for (i = 0; i < csize; i++) print(&pcm[i]); } void print(struct pcms *p) { if(pflg && oflg) pprint(p); else if(pflg || oflg) prnt(p, pflg ? PRIME : NONPRIME); else totprnt(p); } void prnt(struct pcms *p, int hr) { if(p->pcm_pc[hr] == 0) return; printf(fmtcmd, p->pcm_comm); printf(fmtcnt, p->pcm_pc[hr]); printf(fmtkcore, p->pcm_kcore[hr]); printf(fmtcpu, p->pcm_cpu[hr]); printf(fmtreal, p->pcm_real[hr]); if(p->pcm_cpu[hr] == 0) p->pcm_cpu[hr] = 1; printf(fmtmsz, p->pcm_kcore[hr]/p->pcm_cpu[hr]); if(p->pcm_pc[hr] == 0) p->pcm_pc[hr] = 1; printf(fmtmcpu, p->pcm_cpu[hr]/p->pcm_pc[hr]); if (p->pcm_real[hr] == 0) p->pcm_real[hr] = 1; printf(fmthog, p->pcm_cpu[hr]/p->pcm_real[hr]); printf(fmtcharx,p->pcm_io[hr]); printf(fmtblkx,p->pcm_rw[hr]); printf("\n"); } void pprint(struct pcms *p) { printf(fmtcmd, p->pcm_comm); printf(fmtcnt, p->pcm_pc[PRIME]); printf(fmtcnt, p->pcm_pc[NONPRIME]); printf(fmtkcore, TOTAL(p->pcm_kcore)); printf(fmtcpu, p->pcm_cpu[PRIME]); printf(fmtcpu, p->pcm_cpu[NONPRIME]); printf(fmtreal, p->pcm_real[PRIME]); printf(fmtreal, p->pcm_real[NONPRIME]); if(TOTAL(p->pcm_cpu) == 0) p->pcm_cpu[PRIME] = 1; printf(fmtmsz, TOTAL(p->pcm_kcore)/TOTAL(p->pcm_cpu)); if(TOTAL(p->pcm_pc) == 0) p->pcm_pc[PRIME] = 1; printf(fmtmcpu, TOTAL(p->pcm_cpu)/TOTAL(p->pcm_pc)); if ( TOTAL(p->pcm_real) == 0) p->pcm_real[PRIME] = 1; printf(fmthog, TOTAL(p->pcm_cpu)/TOTAL(p->pcm_real)); printf(fmtcharx,TOTAL(p->pcm_io)); printf(fmtblkx, TOTAL(p->pcm_rw)); printf("\n"); } void totprnt(struct pcms *p) { printf(fmtcmd, p->pcm_comm); printf(fmtcnt, TOTAL(p->pcm_pc)); printf(fmtkcore, TOTAL(p->pcm_kcore)); printf(fmtcpu, TOTAL(p->pcm_cpu)); printf(fmtreal, TOTAL(p->pcm_real)); if(TOTAL(p->pcm_cpu) == 0) p->pcm_cpu[PRIME] = 1; printf(fmtmsz, TOTAL(p->pcm_kcore)/TOTAL(p->pcm_cpu)); if(TOTAL(p->pcm_pc) == 0) p->pcm_pc[PRIME] = 1; printf(fmtmcpu, TOTAL(p->pcm_cpu)/TOTAL(p->pcm_pc)); if (TOTAL(p->pcm_real) == 0) p->pcm_real[PRIME] = 1; printf(fmthog, TOTAL(p->pcm_cpu)/TOTAL(p->pcm_real)); printf(fmtcharx,TOTAL(p->pcm_io)); printf(fmtblkx,TOTAL(p->pcm_rw)); printf("\n"); } void outputc(void) { int i; for (i = 0; i < csize; i++) fwrite(&pcm[i], sizeof(pcm[i]), 1, stdout); } /* * 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 */ #include #include #include #include #include #include "acctdef.h" #include #include #include #include #include #include #include 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"); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctcon [-l file] [-o file] #include #include #include "acctdef.h" #include #include #include #include #include #include #include int a_tsize = A_TSIZE; int tsize = -1; /* highest index of used slot in tbuf table */ static int csize; struct utmpx wb; /* record structure read into */ struct ctmp cb; /* record structure written out of */ struct tacct tb; double timet, timei; struct tbuf { char tline[LSZ]; /* /dev/... */ char tname[NSZ]; /* user name */ time_t ttime; /* start time */ dev_t tdev; /* device */ int tlsess; /* # complete sessions */ int tlon; /* # times on (ut_type of 7) */ int tloff; /* # times off (ut_type != 7) */ long ttotal; /* total time used on this line */ } *tbuf; struct ctab { uid_t ct_uid; char ct_name[NSZ]; long ct_con[2]; ushort_t ct_sess; } *pctab; int nsys; struct sys { char sname[LSZ]; /* reasons for ACCOUNTING records */ char snum; /* number of times encountered */ } sy[NSYS]; static char time_buf[50]; time_t datetime; /* old time if date changed, otherwise 0 */ time_t firstime; time_t lastime; int ndates; /* number of times date changed */ int exitcode; char *report = NULL; char *replin = NULL; uid_t namtouid(); dev_t lintodev(); static int valid(void); static void fixup(FILE *); static void loop(void); static void bootshut(void); static int iline(void); static void upall(void); static void update(struct tbuf *); static void printrep(void); static void printlin(void); static int tcmp(struct tbuf *, struct tbuf *); static int node_compare(const void *, const void *); static void enter(struct ctmp *); static void print_node(const void *, VISIT, int); static void output(void); extern char *optarg; extern int optind; void **root = NULL; int main(int argc, char **argv) { int c; (void) setlocale(LC_ALL, ""); while ((c = getopt(argc, argv, "l:o:")) != EOF) switch (c) { case 'l': replin = optarg; break; case 'o': report = optarg; break; case '?': fprintf(stderr, "usage: %s [-l lineuse] " "[-o reboot]\n", argv[0]); exit(1); } if ((tbuf = (struct tbuf *)calloc(a_tsize, sizeof (struct tbuf))) == NULL) { fprintf(stderr, "acctcon: Cannot allocate memory\n"); exit(3); } /* * XXX - fixme - need a good way of getting the fd that getutxent would * use to access wtmpx, so we can convert this read of stdin to use * the APIs and remove the dependence on the existence of the file. */ while (fread(&wb, sizeof (wb), 1, stdin) == 1) { if (firstime == 0) firstime = wb.ut_xtime; if (valid()) loop(); else fixup(stderr); } wb.ut_name[0] = '\0'; strcpy(wb.ut_line, "acctcon"); wb.ut_type = ACCOUNTING; wb.ut_xtime = lastime; loop(); output(); if (report != NULL) printrep(); if (replin != NULL) printlin(); exit(exitcode); } /* * valid: check input wtmpx record, return 1 if looks OK */ static int valid() { int i, c; /* XPG say that user names should not start with a "-" */ if ((c = wb.ut_name[0]) == '-') return (0); for (i = 0; i < NSZ; i++) { c = wb.ut_name[i]; if (isalnum(c) || c == '$' || c == ' ' || c == '.' || c == '_' || c == '-') continue; else if (c == '\0') break; else return (0); } if ((wb.ut_type >= EMPTY) && (wb.ut_type <= UTMAXTYPE)) return (1); return (0); } static void fixup(FILE *stream) { fprintf(stream, "bad wtmpx: offset %lu.\n", ftell(stdin)-sizeof (wb)); fprintf(stream, "bad record is: %.*s\t%.*s\t%lu", sizeof (wb.ut_line), wb.ut_line, sizeof (wb.ut_name), wb.ut_name, wb.ut_xtime); cftime(time_buf, DATE_FMT, &wb.ut_xtime); fprintf(stream, "\t%s", time_buf); exitcode = 1; } static void loop() { int timediff; struct tbuf *tp; if (wb.ut_line[0] == '\0') /* It's an init admin process */ return; /* no connect accounting data here */ switch (wb.ut_type) { case OLD_TIME: datetime = wb.ut_xtime; return; case NEW_TIME: if (datetime == 0) return; timediff = wb.ut_xtime - datetime; for (tp = tbuf; tp <= &tbuf[tsize]; tp++) tp->ttime += timediff; datetime = 0; ndates++; return; case DOWN_TIME: return; case BOOT_TIME: upall(); /* FALLTHROUGH */ case ACCOUNTING: case RUN_LVL: lastime = wb.ut_xtime; bootshut(); return; case USER_PROCESS: case LOGIN_PROCESS: case INIT_PROCESS: case DEAD_PROCESS: /* WHCC mod 3/86 */ update(&tbuf[iline()]); return; case EMPTY: return; default: cftime(time_buf, DATE_FMT, &wb.ut_xtime); fprintf(stderr, "acctcon: invalid type %d for %s %s %s", wb.ut_type, wb.ut_name, wb.ut_line, time_buf); } } /* * bootshut: record reboot (or shutdown) * bump count, looking up wb.ut_line in sy table */ static void bootshut() { int i; for (i = 0; i < nsys && !EQN(wb.ut_line, sy[i].sname); i++) ; if (i >= nsys) { if (++nsys > NSYS) { fprintf(stderr, "acctcon: recompile with larger NSYS\n"); nsys = NSYS; return; } CPYN(sy[i].sname, wb.ut_line); } sy[i].snum++; } /* * iline: look up/enter current line name in tbuf, return index * (used to avoid system dependencies on naming) */ static int iline() { int i; for (i = 0; i <= tsize; i++) if (EQN(wb.ut_line, tbuf[i].tline)) return (i); if (++tsize >= a_tsize) { a_tsize = a_tsize + A_TSIZE; if ((tbuf = (struct tbuf *)realloc(tbuf, a_tsize * sizeof (struct tbuf))) == NULL) { fprintf(stderr, "acctcon: Cannot reallocate memory\n"); exit(2); } } CPYN(tbuf[tsize].tline, wb.ut_line); tbuf[tsize].tdev = lintodev(wb.ut_line); return (tsize); } static void upall() { struct tbuf *tp; wb.ut_type = DEAD_PROCESS; /* fudge a logoff for reboot record. */ for (tp = tbuf; tp <= &tbuf[tsize]; tp++) update(tp); } /* * update tbuf with new time, write ctmp record for end of session */ static void update(struct tbuf *tp) { time_t told, /* last time for tbuf record */ tnew; /* time of this record */ /* Difference is connect time */ told = tp->ttime; tnew = wb.ut_xtime; if (told > tnew) { cftime(time_buf, DATE_FMT, &told); fprintf(stderr, "acctcon: bad times: old: %s", time_buf); cftime(time_buf, DATE_FMT, &tnew); fprintf(stderr, "new: %s", time_buf); exitcode = 1; tp->ttime = tnew; return; } tp->ttime = tnew; switch (wb.ut_type) { case USER_PROCESS: tp->tlsess++; /* * Someone logged in without logging off. Put out record. */ if (tp->tname[0] != '\0') { cb.ct_tty = tp->tdev; CPYN(cb.ct_name, tp->tname); cb.ct_uid = namtouid(cb.ct_name); cb.ct_start = told; if (pnpsplit(cb.ct_start, (ulong_t)(tnew-told), cb.ct_con) == 0) { fprintf(stderr, "acctcon: could not calculate " "prime/non-prime hours\n"); exit(1); } enter(&cb); tp->ttotal += tnew-told; } else /* Someone just logged in */ tp->tlon++; CPYN(tp->tname, wb.ut_name); break; case DEAD_PROCESS: tp->tloff++; if (tp->tname[0] != '\0') { /* Someone logged off */ /* Set up and print ctmp record */ cb.ct_tty = tp->tdev; CPYN(cb.ct_name, tp->tname); cb.ct_uid = namtouid(cb.ct_name); cb.ct_start = told; if (pnpsplit(cb.ct_start, (ulong_t)(tnew-told), cb.ct_con) == 0) { fprintf(stderr, "acctcon: could not calculate " "prime/non-prime hours\n"); exit(1); } enter(&cb); tp->ttotal += tnew-told; tp->tname[0] = '\0'; } } } static void printrep() { int i; freopen(report, "w", stdout); cftime(time_buf, DATE_FMT, &firstime); printf("from %s", time_buf); cftime(time_buf, DATE_FMT, &lastime); printf("to %s", time_buf); if (ndates) printf("%d\tdate change%c\n", ndates, (ndates > 1 ? 's' : '\0')); for (i = 0; i < nsys; i++) printf("%d\t%.*s\n", sy[i].snum, sizeof (sy[i].sname), sy[i].sname); } /* * print summary of line usage * accuracy only guaranteed for wtmpx file started fresh */ static void printlin() { struct tbuf *tp; double ttime; int tsess, ton, toff; freopen(replin, "w", stdout); ttime = 0.0; tsess = ton = toff = 0; timet = MINS(lastime-firstime); printf("TOTAL DURATION IS %.0f MINUTES\n", timet); printf("LINE MINUTES PERCENT # SESS # ON # OFF\n"); qsort((char *)tbuf, tsize + 1, sizeof (tbuf[0]), (int (*)(const void *, const void *))tcmp); for (tp = tbuf; tp <= &tbuf[tsize]; tp++) { timei = MINS(tp->ttotal); ttime += timei; tsess += tp->tlsess; ton += tp->tlon; toff += tp->tloff; printf("%-*.*s %-7.0f %-7.0f %-6d %-4d %-5d\n", OUTPUT_LSZ, OUTPUT_LSZ, tp->tline, timei, (timet > 0.)? 100*timei/timet : 0., tp->tlsess, tp->tlon, tp->tloff); } printf("TOTALS %-7.0f -- %-6d %-4d %-5d\n", ttime, tsess, ton, toff); } static int tcmp(struct tbuf *t1, struct tbuf *t2) { return (strncmp(t1->tline, t2->tline, LSZ)); } static int node_compare(const void *node1, const void *node2) { if (((const struct ctab *)node1)->ct_uid > ((const struct ctab *)node2)->ct_uid) return (1); else if (((const struct ctab *)node1)->ct_uid < ((const struct ctab *)node2)->ct_uid) return (-1); else return (0); } static void enter(struct ctmp *c) { unsigned i; int j; struct ctab **pt; if ((pctab = (struct ctab *)malloc(sizeof (struct ctab))) == NULL) { fprintf(stderr, "acctcon: malloc fail!\n"); exit(2); } pctab->ct_uid = c->ct_uid; CPYN(pctab->ct_name, c->ct_name); pctab->ct_con[0] = c->ct_con[0]; pctab->ct_con[1] = c->ct_con[1]; pctab->ct_sess = 1; if (*(pt = (struct ctab **)tsearch((void *)pctab, (void **)&root, \ node_compare)) == NULL) { fprintf(stderr, "Not enough space available to build tree\n"); exit(1); } if (*pt != pctab) { (*pt)->ct_con[0] += c->ct_con[0]; (*pt)->ct_con[1] += c->ct_con[1]; (*pt)->ct_sess++; free(pctab); } } static void print_node(const void *node, VISIT order, int level) { if (order == postorder || order == leaf) { tb.ta_uid = (*(struct ctab **)node)->ct_uid; CPYN(tb.ta_name, (*(struct ctab **)node)->ct_name); tb.ta_con[0] = ((*(struct ctab **)node)->ct_con[0]) / 60.0; tb.ta_con[1] = ((*(struct ctab **)node)->ct_con[1]) / 60.0; tb.ta_sc = (*(struct ctab **)node)->ct_sess; fwrite(&tb, sizeof (tb), 1, stdout); } } static void output() { twalk((struct ctab *)root, print_node); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctcon1 [-p] [-t] [-l file] [-o file] ctmp-file * -p print input only, no processing * -t test mode: use latest time found in input, rather than * current time when computing times of lines still on * (only way to get repeatable data from old files) * -l file causes output of line usage summary * -o file causes first/last/reboots report to be written to file * reads input (normally /var/adm/wtmpx), produces * list of sessions, sorted by ending time in ctmp.h/ascii format * A_TSIZE is max # distinct ttys */ #include #include "acctdef.h" #include #include #include #include #include #include int a_tsize = A_TSIZE; int tsize = -1; /* used slots in tbuf table */ struct utmpx wb; /* record structure read into */ struct ctmp cb; /* record structure written out of */ struct tbuf { char tline[LSZ]; /* /dev/... */ char tname[NSZ]; /* user name */ time_t ttime; /* start time */ dev_t tdev; /* device */ int tlsess; /* # complete sessions */ int tlon; /* # times on (ut_type of 7) */ int tloff; /* # times off (ut_type != 7) */ long ttotal; /* total time used on this line */ } * tbuf; #define DATE_FMT "%a %b %e %H:%M:%S %Y\n" int nsys; struct sys { char sname[LSZ]; /* reasons for ACCOUNTING records */ char snum; /* number of times encountered */ } sy[NSYS]; time_t datetime; /* old time if date changed, otherwise 0 */ time_t firstime; time_t lastime; int ndates; /* number of times date changed */ int exitcode; char *report = NULL; char *replin = NULL; int printonly; int tflag; static char time_buf[50]; uid_t namtouid(); dev_t lintodev(); static size_t wread(void); static int valid(void); static void fixup(FILE *); static void loop(void); static void bootshut(void); static int iline(void); static void upall(void); static void update(struct tbuf *); static void printrep(void); static void printlin(void); static void prctmp(struct ctmp *); int main(int argc, char **argv) { char *prog = argv[0]; (void)setlocale(LC_ALL, ""); while (--argc > 0 && **++argv == '-') switch(*++*argv) { case 'l': if (--argc > 0) replin = *++argv; continue; case 'o': if (--argc > 0) report = *++argv; continue; case 'p': printonly++; continue; case 't': tflag++; continue; default: fprintf(stderr, "usage: %s [-p] [-t] [-l lineuse] [-o reboot]\n", prog); exit(1); } if ((tbuf = (struct tbuf *) calloc(a_tsize, sizeof (struct tbuf))) == NULL) { fprintf(stderr, "acctcon1: Cannot allocate memory\n"); exit(3); } if (printonly) { while (wread()) { if (valid()) { printf("%.*s\t%.*s\t%lu", sizeof (wb.ut_line), wb.ut_line, sizeof (wb.ut_name), wb.ut_name, wb.ut_xtime); cftime(time_buf, DATE_FMT, &wb.ut_xtime); printf("\t%s", time_buf); } else fixup(stdout); } exit(exitcode); } while (wread()) { if (firstime == 0) firstime = wb.ut_xtime; if (valid()) loop(); else fixup(stderr); } wb.ut_name[0] = '\0'; strcpy(wb.ut_line, "acctcon1"); wb.ut_type = ACCOUNTING; if (tflag) wb.ut_xtime = lastime; else time(&wb.ut_xtime); loop(); if (report != NULL) printrep(); if (replin != NULL) printlin(); exit(exitcode); } static size_t wread() { return (fread(&wb, sizeof(wb), 1, stdin) == 1); } /* * valid: check input wtmp record, return 1 if looks OK */ static int valid() { int i, c; /* XPG say that user names should not start with a "-". */ if ((c = wb.ut_name[0]) == '-') return(0); for (i = 0; i < NSZ; i++) { c = wb.ut_name[i]; if (isalnum(c) || c == '$' || c == ' ' || c == '_' || c == '-') continue; else if (c == '\0') break; else return(0); } if((wb.ut_type >= EMPTY) && (wb.ut_type <= UTMAXTYPE)) return(1); return(0); } /* * fixup assumes that V6 wtmp (16 bytes long) is mixed in with * V7 records (20 bytes each) * * Starting with Release 5.0 of UNIX, this routine will no * longer reset the read pointer. This has a snowball effect * On the following records until the offset corrects itself. * If a message is printed from here, it should be regarded as * a bad record and not as a V6 record. */ static void fixup(FILE *stream) { fprintf(stream, "bad wtmpx: offset %lu.\n", ftell(stdin)-sizeof(wb)); fprintf(stream, "bad record is: %.*s\t%.*s\t%lu", sizeof (wb.ut_line), wb.ut_line, sizeof (wb.ut_name), wb.ut_name, wb.ut_xtime); cftime(time_buf, DATE_FMT, &wb.ut_xtime); fprintf(stream, "\t%s", time_buf); #ifdef V6 fseek(stdin, (long)-4, 1); #endif exitcode = 1; } static void loop() { int timediff; struct tbuf *tp; if(wb.ut_line[0] == '\0' ) /* It's an init admin process */ return; /* no connect accounting data here */ switch(wb.ut_type) { case OLD_TIME: datetime = wb.ut_xtime; return; case NEW_TIME: if(datetime == 0) return; timediff = wb.ut_xtime - datetime; for (tp = tbuf; tp <= &tbuf[tsize]; tp++) tp->ttime += timediff; datetime = 0; ndates++; return; case BOOT_TIME: upall(); /* FALLTHROUGH */ case ACCOUNTING: case RUN_LVL: lastime = wb.ut_xtime; bootshut(); return; case USER_PROCESS: case LOGIN_PROCESS: case INIT_PROCESS: case DEAD_PROCESS: update(&tbuf[iline()]); return; case EMPTY: return; default: cftime(time_buf, DATE_FMT, &wb.ut_xtime); fprintf(stderr, "acctcon1: invalid type %d for %s %s %s", wb.ut_type, wb.ut_name, wb.ut_line, time_buf); } } /* * bootshut: record reboot (or shutdown) * bump count, looking up wb.ut_line in sy table */ static void bootshut() { int i; for (i = 0; i < nsys && !EQN(wb.ut_line, sy[i].sname); i++) ; if (i >= nsys) { if (++nsys > NSYS) { fprintf(stderr, "acctcon1: recompile with larger NSYS\n"); nsys = NSYS; return; } CPYN(sy[i].sname, wb.ut_line); } sy[i].snum++; } /* * iline: look up/enter current line name in tbuf, return index * (used to avoid system dependencies on naming) */ static int iline() { int i; for (i = 0; i <= tsize; i++) if (EQN(wb.ut_line, tbuf[i].tline)) return(i); if (++tsize >= a_tsize) { a_tsize = a_tsize + A_TSIZE; if ((tbuf = (struct tbuf *) realloc(tbuf, a_tsize * sizeof (struct tbuf))) == NULL) { fprintf(stderr, "acctcon1: Cannot reallocate memory\n"); exit(2); } } CPYN(tbuf[tsize].tline, wb.ut_line); tbuf[tsize].tdev = lintodev(wb.ut_line); return(tsize); } static void upall() { struct tbuf *tp; wb.ut_type = INIT_PROCESS; /* fudge a logoff for reboot record */ for (tp = tbuf; tp <= &tbuf[tsize]; tp++) update(tp); } /* * update tbuf with new time, write ctmp record for end of session */ static void update(struct tbuf *tp) { time_t told, /* last time for tbuf record */ tnew; /* time of this record */ /* Difference is connect time */ told = tp->ttime; tnew = wb.ut_xtime; cftime(time_buf, DATE_FMT, &told); fprintf(stderr, "The old time is: %s", time_buf); cftime(time_buf, DATE_FMT, &tnew); fprintf(stderr, "the new time is: %s", time_buf); if (told > tnew) { cftime(time_buf, DATE_FMT, &told); fprintf(stderr, "acctcon1: bad times: old: %s", time_buf); cftime(time_buf, DATE_FMT, &tnew); fprintf(stderr, "new: %s", time_buf); exitcode = 1; tp->ttime = tnew; return; } tp->ttime = tnew; switch(wb.ut_type) { case USER_PROCESS: tp->tlsess++; if(tp->tname[0] != '\0') { /* Someone logged in without */ /* logging off. Put out record. */ cb.ct_tty = tp->tdev; CPYN(cb.ct_name, tp->tname); cb.ct_uid = namtouid(cb.ct_name); cb.ct_start = told; if (pnpsplit(cb.ct_start, (ulong_t)(tnew-told), cb.ct_con) == 0) { fprintf(stderr, "acctcon1: could not calculate prime/non-prime hours\n"); exit(1); } prctmp(&cb); tp->ttotal += tnew-told; } else /* Someone just logged in */ tp->tlon++; CPYN(tp->tname, wb.ut_name); break; case INIT_PROCESS: case LOGIN_PROCESS: case DEAD_PROCESS: tp->tloff++; if(tp->tname[0] != '\0') { /* Someone logged off */ /* Set up and print ctmp record */ cb.ct_tty = tp->tdev; CPYN(cb.ct_name, tp->tname); cb.ct_uid = namtouid(cb.ct_name); cb.ct_start = told; if (pnpsplit(cb.ct_start, (ulong_t)(tnew-told), cb.ct_con) == 0) { fprintf(stderr, "acctcon1: could not calculate prime/non-prime hours\n"); exit(1); } prctmp(&cb); tp->ttotal += tnew-told; tp->tname[0] = '\0'; } } } static void printrep() { int i; freopen(report, "w", stdout); cftime(time_buf, DATE_FMT, &firstime); printf("from %s", time_buf); cftime(time_buf, DATE_FMT, &lastime); printf("to %s", time_buf); if (ndates) printf("%d\tdate change%c\n",ndates,(ndates>1 ? 's' : '\0')); for (i = 0; i < nsys; i++) printf("%d\t%.*s\n", sy[i].snum, sizeof (sy[i].sname), sy[i].sname); } /* * print summary of line usage * accuracy only guaranteed for wtmpx file started fresh */ static void printlin() { struct tbuf *tp; double timet, timei; double ttime; int tsess, ton, toff; freopen(replin, "w", stdout); ttime = 0.0; tsess = ton = toff = 0; timet = MINS(lastime-firstime); printf("TOTAL DURATION IS %.0f MINUTES\n", timet); printf("LINE MINUTES PERCENT # SESS # ON # OFF\n"); for (tp = tbuf; tp <= &tbuf[tsize]; tp++) { timei = MINS(tp->ttotal); ttime += timei; tsess += tp->tlsess; ton += tp->tlon; toff += tp->tloff; printf("%-*.*s %-7.0f %-7.0f %-6d %-4d %-5d\n", OUTPUT_LSZ, OUTPUT_LSZ, tp->tline, timei, (timet > 0.)? 100*timei/timet : 0., tp->tlsess, tp->tlon, tp->tloff); } printf("TOTALS %-7.0f -- %-6d %-4d %-5d\n", ttime, tsess, ton, toff); } static void prctmp(struct ctmp *t) { printf("%u\t%ld\t%.*s\t%lu\t%lu\t%lu", t->ct_tty, t->ct_uid, OUTPUT_NSZ, t->ct_name, t->ct_con[0], t->ct_con[1], t->ct_start); cftime(time_buf, DATE_FMT, &t->ct_start); printf("\t%s", time_buf); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctcon2 ctacct * reads std. input (ctmp.h/ascii format) * converts to tacct.h form, writes to std. output */ #include #include "acctdef.h" #include #include struct ctmp cb; struct tacct tb; int main(int argc, char **argv) { tb.ta_sc = 1; while (scanf("%lu\t%ld\t%s\t%lu\t%lu\t%lu\t%*[^\n]", &cb.ct_tty, &cb.ct_uid, cb.ct_name, &cb.ct_con[0], &cb.ct_con[1], &cb.ct_start) != EOF) { tb.ta_uid = cb.ct_uid; CPYN(tb.ta_name, cb.ct_name); tb.ta_con[0] = MINS(cb.ct_con[0]); tb.ta_con[1] = MINS(cb.ct_con[1]); fwrite(&tb, sizeof (tb), 1, stdout); } exit(0); } /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include static struct utmpx utdummy; /* dummy - used to get member sizes */ #define PACCT "/var/adm/pacct" #define HOLFILE "/etc/acct/holidays" #define NHOLIDAYS 200 /* max number of company holidays per year */ #define NSYS 200 /* number of run state changes */ #define NFILE 100 /* max number of files that acctmerg handles */ #ifdef uts #define CSIZE 10001 #define MAXUSERS 5003 #define A_SSIZE 60001 /* max num of sessions in 1 acct run */ #define A_TSIZE 10001 /* max num of line names in 1 acct run */ #define A_USIZE 20001 /* max num of distinct login names in 1 acct run */ #else #define CSIZE 5001 #define MAXUSERS 3001 #define A_SSIZE 6001 /* max num of sessions in 1 acct run */ #define A_TSIZE 1001 /* max num of line names in 1 acct run */ #define A_USIZE 2001 /* max num of distinct login names in 1 acct run */ #endif #define TSIZE1 100 /* # distinct names, for speed only */ #define USIZE1 100 #define MAXIGN 10 #define UNUSED -1 #define FAIL -1 #define SUCCEED 0 #define TRUE 1 #define FALSE 0 #define PRIME 0 #define NONPRIME 1 #define MEANSIZE 01 #define KCOREMIN 02 #define HOGFACTOR 04 #define SEPTIME 010 #define CPUFACTOR 020 #define IORW 040 #define ROOT 0 #define ERR (-1) #define OK 0 #define NOGOOD 1 #define VALID 0 #define INVALID 1 /* * The size of LSZ is based on MAX_SRCH_DEPTH because of the * implementation of devtolin() */ #define MAX_SRCH_DEPTH 4 #define LSZ (MAX_SRCH_DEPTH * sizeof (utdummy.ut_line)) #define MAX_DEV_PATH (LSZ + 5) /* max len of abs line name path */ #define NSZ (sizeof (utdummy.ut_name)) /* sizeof login name */ #define LINESZ (sizeof (utdummy.ut_line)) /* sizeof device name */ /* * These exist for backward compatibility. Until we can change the * output formats, we need to keep the field widths the same as * they always have been. */ #define OUTPUT_NSZ 8 #define OUTPUT_LSZ 12 #define MYKIND(flag) ((flag & ACCTF) == 0) #define SU(flag) ((flag & ASU) == ASU) #define TOTAL(a) (a[PRIME] + a[NONPRIME]) #define okay(time) ((time/100>=0) && (time/100<=24) \ && (time%100>=0) && (time%100<60)) #define pf(dble) fprintf(stdout, " %7.2lf", dble) #define ps(s) fprintf(stdout, "%8.8s", s) #define diag(string) fprintf(stderr, "\t%s\n", string) #define DATE_FMT "%a %b %e %H:%M:%S %Y\n" #define DATE_FMT1 " %H:%M:%S" #define CBEMPTY (ctab[i].ct_sess == 0) #define UBEMPTY (ub[i].ut_pc == 0 && ub[i].ut_cpu[0] == 0 && \ ub[i].ut_cpu[1] == 0 && ub[i].ut_kcore[0] ==0 && ub[i].ut_kcore[1] == 0) #define EQN(s1, s2) (strncmp(s1, s2, sizeof(s1)) == 0) #define CPYN(s1, s2) (void) strncpy(s1, s2 ? s2 : "", sizeof(s1)) #define SECSINDAY 86400L #define SECS(tics) ((double) tics)/HZ #define MINS(secs) ((double) secs)/60 #define MINT(tics) ((double) tics)/(60*HZ) #include #if UTS #define KCORE(clicks) ((double) BSIZE * (clicks/1024.0)) #else #define KCORE(clicks) ((double) (sysconf(_SC_PAGESIZE) >> 10) *clicks) #endif /* * total accounting (for acct period), also for day */ struct tacct { uid_t ta_uid; /* userid */ char ta_name[NSZ]; /* 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 */ }; /* * connect time record */ struct ctmp { dev_t ct_tty; /* major minor */ uid_t ct_uid; /* userid */ char ct_name[NSZ]; /* login name */ long ct_con[2]; /* connect time (p/np) secs */ time_t ct_start; /* session start time */ }; /* * per process temporary data */ struct ptmp { uid_t pt_uid; /* userid */ char pt_name[NSZ]; /* login name */ ulong_t pt_cpu[2]; /* CPU (sys+usr) P/NP time tics */ unsigned pt_mem; /* avg. memory size (64byte clicks) */ }; /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctdisk dtacct * reads std.input & converts to tacct.h format, writes to output * input: * uid name #blocks */ #include #include "acctdef.h" #include #include struct tacct tb; char ntmp[NSZ+1]; int main(int argc, char **argv) { int rc; tb.ta_dc = 1; while ((rc = scanf("%ld\t%s\t%f", &tb.ta_uid, ntmp, &tb.ta_du)) == 3) { CPYN(tb.ta_name, ntmp); fwrite(&tb, sizeof (tb), 1, stdout); } if (rc != EOF) { fprintf(stderr, "\nacctdisk: incorrect input format.\n"); exit(1); } else { exit(0); } } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.8 */ /* * acctdusg [-u file] [-p file] > dtmp-file * -u file for names of files not charged to anyone * -p get password info from file * reads std input (normally from find / -print) * and computes disk resource consumption by login */ #include #include #include #include #include #include #include #include #include "acctdef.h" struct disk { struct disk *next; /* next entry at same hash tbl index */ uid_t dsk_uid; /* user id of login name */ blkcnt_t dsk_du; /* disk usage */ char dsk_name[NSZ+1]; /* login name */ char validuser; /* set if the uid exists */ }; static char *pfile = NULL; static FILE *nchrg = NULL; static avl_tree_t *tree = NULL; static struct disk *usglist[MAXUSERS]; /* holds data on disk usg by uid */ #define HASHKEY(x) ((int)((unsigned int)(x) % MAXUSERS)) static struct disk *hash_insert(uid_t); static struct disk *hash_find(uid_t); static void openerr(char *); static void output(void); static void validate_entry(struct disk *, struct passwd *); static void charge(char *); #ifdef DEBUG static void pdisk(void); #endif int main(int argc, char **argv) { char fbuf[PATH_MAX+1], *fb; FILE *pwf; int c; struct passwd *pw; struct disk *entry; while ((c = getopt(argc, argv, "p:u:")) != EOF) { switch (c) { case 'u': if ((nchrg = fopen(optarg, "w")) == NULL) openerr(optarg); (void) chmod(optarg, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); break; case 'p': pfile = optarg; break; default: exit(1); } } if (pfile) { if ((pwf = fopen(pfile, "r")) == NULL) { openerr(pfile); } /* fill usglist with the user's in the passwd file */ while ((pw = fgetpwent(pwf)) != NULL) { if ((entry = hash_find(pw->pw_uid)) == NULL) entry = hash_insert(pw->pw_uid); validate_entry(entry, pw); } (void) fclose(pwf); } /* charge the files listed in names to users listed in the usglist */ while (fgets(fbuf, sizeof (fbuf), stdin) != NULL) { if ((fb = strchr(fbuf, '\n')) != NULL) { /* * replace the newline char at the end of the * filename with a null character */ *fb = '\0'; } charge(fbuf); } output(); if (nchrg) (void) fclose(nchrg); #ifdef DEBUG pdisk(); #endif return (0); } /* * create a new entry and insert. */ static struct disk * hash_insert(uid_t uid) { struct disk *curdisk; int key = HASHKEY(uid); if ((curdisk = malloc(sizeof (struct disk))) == NULL) { (void) fprintf(stderr, "acctdusg: cannot allocate memory " "for hash table entry\n"); exit(1); } curdisk->dsk_uid = uid; curdisk->dsk_du = 0; curdisk->validuser = 0; /* initially invalid */ curdisk->next = usglist[key]; usglist[key] = curdisk; return (curdisk); } /* * return the disk entry for given uid. return NULL if not found. */ static struct disk * hash_find(uid_t uid) { struct disk *curdisk; for (curdisk = usglist[HASHKEY(uid)]; curdisk != NULL; curdisk = curdisk->next) { if (curdisk->dsk_uid == uid) { return (curdisk); } } return (NULL); } static void openerr(char *file) { (void) fprintf(stderr, "Cannot open %s\n", file); exit(1); } static void output(void) { int index; struct disk *entry; for (index = 0; index < MAXUSERS; index++) { for (entry = usglist[index]; entry != NULL; entry = entry->next) { if (entry->dsk_du != 0) { (void) printf("%ld\t%s\t%lld\n", entry->dsk_uid, entry->dsk_name, entry->dsk_du); } } } } /* * Initialize the disk entry for a valid passwd entry. */ static void validate_entry(struct disk *entry, struct passwd *pw) { (void) strlcpy(entry->dsk_name, pw->pw_name, sizeof (entry->dsk_name)); entry->validuser = 1; } static void charge(char *n) { struct stat statb; struct disk *entry; struct passwd *pw; if (lstat(n, &statb) == -1) return; /* * do not count the duplicate entries. */ if (statb.st_nlink > 1) { switch (add_tnode(&tree, statb.st_dev, statb.st_ino)) { case 0: /* already exist */ return; case 1: /* added */ break; default: perror("acctdusg"); exit(1); } } /* * st_blocks is not defined for character/block special files. */ if (S_ISCHR(statb.st_mode) || S_ISBLK(statb.st_mode)) statb.st_blocks = 0; /* * If -p is given, we've all loaded the passwd entries. * Files with unknown uid should go into nchrg. Otherwise * (without -p), we try creating new entry for the uid. */ if ((entry = hash_find(statb.st_uid)) == NULL) { if (pfile == NULL) { pw = getpwuid(statb.st_uid); entry = hash_insert(statb.st_uid); if (pw != NULL) { validate_entry(entry, pw); } } } if (entry != NULL && entry->validuser) { entry->dsk_du += statb.st_blocks; } else if (nchrg) { (void) fprintf(nchrg, "%9ld\t%7llu\t%s\n", statb.st_uid, statb.st_blocks, n); } } #ifdef DEBUG static void pdisk() { int index; struct disk *entry; for (index = 0; index < MAXUSERS; index++) { for (entry = usglist[index]; entry != NULL; entry = entry->next) { (void) fprintf(stderr, "%.8s\t%9ld\t%7llu\n", entry->dsk_name, entry->dsk_uid, entry->dsk_du); } } } #endif /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctmerg [-a] [-i] [-p] [-t] [-u] [-v] [file...] * -a output in tacct.h/ascii (instead of tacct.h) * -i input is in tacct.h/ascii (instead of tacct.h) * -p print input files with no processing * -t output single record that totals all input * -u summarize by uid, rather than uid/name * -v output in verbose tacct.h/ascii * reads std input and 0-NFILE files, all in tacct.h format, * sorted by uid/name. * merge/adds all records with same uid/name (or same uid if -u, * or all records if -t], writes to std. output * (still in tacct.h format) * note that this can be used to summarize the std input */ #include #include #include #include "acctdef.h" #include int nfile; /* index of last used in fl */ FILE *fl[NFILE] = {stdin}; struct tacct tb[NFILE]; /* current record from each file */ struct tacct tt = { 0, "TOTAL" }; int asciiout; int asciiinp; int printonly; int totalonly; int uidsum; int verbose; int exitcode = 0; void prtacct(struct tacct *); struct tacct *getleast(void); void output(struct tacct *); void tacctadd(struct tacct *, struct tacct *); void sumcurr(struct tacct *); int main(int argc, char **argv) { int i; struct tacct *tp; while (--argc > 0) { if (**++argv == '-') switch (*++*argv) { case 'a': asciiout++; continue; case 'i': asciiinp++; continue; case 'p': printonly++; continue; case 't': totalonly++; continue; case 'u': uidsum++; continue; case 'v': verbose++; asciiout++; continue; } else { if (++nfile >= NFILE) { fprintf(stderr, "acctmerg: >%d files\n", NFILE); exit(1); } if ((fl[nfile] = fopen(*argv, "r")) == NULL) { fprintf(stderr, "acctmerg: can't open %s\n", *argv); exitcode = 1; /* exit(1); */ } } } if (printonly) { for (i = 0; i <= nfile; i++) while (getnext(i)) prtacct(&tb[i]); exit(exitcode); } for (i = 0; i <= nfile; i++) if(getnext(i) == 0) { fprintf(stderr,"acctmerg: read error file %d. File may be empty.\n", i); exitcode = 2; } while ((tp = getleast()) != NULL) /* get least uid of all files, */ sumcurr(tp); /* sum all entries for that uid, */ if (totalonly) /* and write the 'summed' record */ output(&tt); exit(exitcode); } /* * getleast returns ptr to least (lowest uid) element of current * avail, NULL if none left; always returns 1st of equals */ struct tacct * getleast(void) { struct tacct *tp, *least; least = NULL; for (tp = tb; tp <= &tb[nfile]; tp++) { if (tp->ta_name[0] == '\0') continue; if (least == NULL || tp->ta_uid < least->ta_uid || ((tp->ta_uid == least->ta_uid) && !uidsum && (strncmp(tp->ta_name, least->ta_name, NSZ) < 0))) least = tp; } return(least); } /* * sumcurr sums all entries with same uid/name (into tp->tacct record) * writes it out, gets new entry */ void sumcurr(struct tacct *tp) { struct tacct tc; char *memcpy(); memcpy(&tc, tp, sizeof(struct tacct)); tacctadd(&tt, tp); /* gets total of all uids */ getnext(tp-&tb[0]); /* get next one in same file */ while (tp <= &tb[nfile]) if (tp->ta_name[0] != '\0' && tp->ta_uid == tc.ta_uid && (uidsum || EQN(tp->ta_name, tc.ta_name))) { tacctadd(&tc, tp); tacctadd(&tt, tp); getnext(tp-&tb[0]); } else tp++; /* look at next file */ if (!totalonly) output(&tc); } void tacctadd(struct tacct *t1, struct tacct *t2) { t1->ta_cpu[0] = t1->ta_cpu[0] + t2->ta_cpu[0]; t1->ta_cpu[1] = t1->ta_cpu[1] + t2->ta_cpu[1]; t1->ta_kcore[0] = t1->ta_kcore[0] + t2->ta_kcore[0]; t1->ta_kcore[1] = t1->ta_kcore[1] + t2->ta_kcore[1]; t1->ta_con[0] = t1->ta_con[0] + t2->ta_con[0]; t1->ta_con[1] = t1->ta_con[1] + t2->ta_con[1]; t1->ta_du = t1->ta_du + t2->ta_du; t1->ta_pc += t2->ta_pc; t1->ta_sc += t2->ta_sc; t1->ta_dc += t2->ta_dc; t1->ta_fee += t2->ta_fee; } void output(struct tacct *tp) { if (asciiout) prtacct(tp); else fwrite(tp, sizeof(*tp), 1, stdout); } /* * getnext reads next record from stream i, returns 1 if one existed */ int getnext(int i) { struct tacct *tp; tp = &tb[i]; tp->ta_name[0] = '\0'; if (fl[i] == NULL) return(0); if (asciiinp) { if (fscanf(fl[i], "%ld\t%s\t%e %e %e %e %e %e %e %lu\t%hu\t%hu\t%hu", &tp->ta_uid, tp->ta_name, &tp->ta_cpu[0], &tp->ta_cpu[1], &tp->ta_kcore[0], &tp->ta_kcore[1], &tp->ta_con[0], &tp->ta_con[1], &tp->ta_du, &tp->ta_pc, &tp->ta_sc, &tp->ta_dc, &tp->ta_fee) != EOF) return(1); } else { if (fread(tp, sizeof(*tp), 1, fl[i]) == 1) return(1); } fclose(fl[i]); fl[i] = NULL; return(0); } char fmt[] = "%ld\t%.*s\t%.0f\t%.0f\t%.0f\t%.0f\t%.0f\t%.0f\t%.0f\t%lu\t%hu\t%hu\t%hu\n"; char fmtv[] = "%ld\t%.*s\t%e %e %e %e %e %e %e %lu %hu\t%hu\t%hu\n"; void prtacct(struct tacct *tp) { printf(verbose ? fmtv : fmt, tp->ta_uid, OUTPUT_NSZ, tp->ta_name, tp->ta_cpu[0], tp->ta_cpu[1], tp->ta_kcore[0], tp->ta_kcore[1], tp->ta_con[0], tp->ta_con[1], tp->ta_du, tp->ta_pc, tp->ta_sc, tp->ta_dc, tp->ta_fee); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * accton - calls syscall with super-user privileges */ #include #include #include #include "acctdef.h" #include #include #include #include #include uid_t admuid; struct passwd *pwd; void ckfile(char *); int main(int argc, char **argv) { uid_t uid; uid = getuid(); if ((pwd = getpwnam("adm")) == NULL) { perror("cannot determine adm's uid"), exit(1); } admuid = pwd->pw_uid; if (uid == ROOT || uid == admuid) { if (setuid(ROOT) == ERR) { perror("cannot setuid (check command mode and owner)"); exit(1); } if (argv[1]) ckfile(argv[1]); if (acct(argc > 1 ? argv[1] : 0) < 0) { perror(argv[1]), exit(1); } exit(0); } fprintf(stderr, "%s: permission denied\n", argv[0]); exit(1); } void ckfile(char *admfile) { struct stat stbuf; struct stat *s = &stbuf; int fd; if ((fd = open(admfile, O_RDONLY|O_CREAT, 0644)) == ERR) { perror("creat"), exit(1); } if (fstat(fd, s) == ERR) { perror("fstat"); exit(1); } if (s->st_uid != admuid || s->st_gid != (gid_t)admuid) if (fchown(fd, admuid, (gid_t)admuid) == ERR) { perror("cannot change owner"), exit(1); } /* was if(s->st_mode & 0777 != 0664) */ if ((s->st_mode & S_IAMB) != S_IRUSR|S_IWUSR|S_IRGRP|S_IWUSR|S_IROTH) if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWUSR|S_IROTH) == ERR) { perror("cannot chmod"), exit(1); } (void) close(fd); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctprc * reads std. input (acct.h format), * writes std. output (tacct format) * sorted by uid * adds login names */ #include #include #include #include "acctdef.h" #include #include #include #include struct acct ab; struct ptmp pb; struct tacct tb; struct utab { uid_t ut_uid; char ut_name[NSZ]; float ut_cpu[2]; /* cpu time (mins) */ float ut_kcore[2]; /* kcore-mins */ long ut_pc; /* # processes */ } * ub; static int usize; void **root = NULL; void output(void); void enter(struct ptmp *); int main(int argc, char **argv) { long elaps[2]; ulong_t etime, stime; unsigned long mem; #ifdef uts float expand(); #else ulong_t expand(); #endif while (fread(&ab, sizeof(ab), 1, stdin) == 1) { if (!MYKIND(ab.ac_flag)) continue; pb.pt_uid = ab.ac_uid; CPYN(pb.pt_name, NULL); /* * approximate cpu P/NP split as same as elapsed time */ if ((etime = SECS(expand(ab.ac_etime))) == 0) etime = 1; stime = expand(ab.ac_stime) + expand(ab.ac_utime); mem = expand(ab.ac_mem); if(pnpsplit(ab.ac_btime, etime, elaps) == 0) { fprintf(stderr, "acctprc: could not calculate prime/non-prime hours\n"); exit(1); } pb.pt_cpu[0] = (double)stime * (double)elaps[0] / etime; pb.pt_cpu[1] = (stime > pb.pt_cpu[0])? stime - pb.pt_cpu[0] : 0; pb.pt_cpu[1] = stime - pb.pt_cpu[0]; if (stime) pb.pt_mem = (mem + stime - 1) / stime; else pb.pt_mem = 0; /* unlikely */ enter(&pb); } output(); exit(0); } int node_compare(const void *node1, const void *node2) { if (((const struct utab *)node1)->ut_uid > \ ((const struct utab *)node2)->ut_uid) return(1); else if (((const struct utab *)node1)->ut_uid < \ ((const struct utab *)node2)->ut_uid) return(-1); else return(0); } void enter(struct ptmp *p) { double memk; struct utab **pt; if ((ub = (struct utab *)malloc(sizeof (struct utab))) == NULL) { fprintf(stderr, "acctprc: malloc fail!\n"); exit(2); } ub->ut_uid = p->pt_uid; CPYN(ub->ut_name, p->pt_name); ub->ut_cpu[0] = MINT(p->pt_cpu[0]); ub->ut_cpu[1] = MINT(p->pt_cpu[1]); memk = KCORE(pb.pt_mem); ub->ut_kcore[0] = memk * MINT(p->pt_cpu[0]); ub->ut_kcore[1] = memk * MINT(p->pt_cpu[1]); ub->ut_pc = 1; if (*(pt = (struct utab **)tsearch((void *)ub, (void **)&root, \ node_compare)) == NULL) { fprintf(stderr, "Not enough space available to build tree\n"); exit(1); } if (*pt != ub) { (*pt)->ut_cpu[0] += MINT(p->pt_cpu[0]); (*pt)->ut_cpu[1] += MINT(p->pt_cpu[1]); (*pt)->ut_kcore[0] += memk * MINT(p->pt_cpu[0]); (*pt)->ut_kcore[1] += memk * MINT(p->pt_cpu[1]); (*pt)->ut_pc++; free(ub); } } void print_node(const void *node, VISIT order, int level) { if (order == postorder || order == leaf) { tb.ta_uid = (*(struct utab **)node)->ut_uid; CPYN(tb.ta_name, (char *)uidtonam((*(struct utab **)node)->ut_uid)); tb.ta_cpu[0] = (*(struct utab **)node)->ut_cpu[0]; tb.ta_cpu[1] = (*(struct utab **)node)->ut_cpu[1]; tb.ta_kcore[0] = (*(struct utab **)node)->ut_kcore[0]; tb.ta_kcore[1] = (*(struct utab **)node)->ut_kcore[1]; tb.ta_pc = (*(struct utab **)node)->ut_pc; fwrite(&tb, sizeof(tb), 1, stdout); } } void output(void) { twalk((struct utab *)root, print_node); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctprc1 [ctmpfile] * reads std. input (acct.h format), adds login names * writes std. output (ptmp.h/ascii format) * if ctmpfile is given, it is expected have ctmp.h/ascii data, * sorted by uid/name; it is used to make better guesses at login names */ #include #include #include "acctdef.h" #include #include #include #include #define MYKIND(flag) ((flag & ACCTF) == 0) struct acct ab; struct ctmp cb; struct ptmp pb; int a_usize = A_USIZE; struct urec { /* 1 for each distinct uid/name */ uid_t ur_uid; /* sorted by uid/name */ char ur_name[NSZ]; struct srec *ur_srec; /* ptr to first session */ short ur_cnt; /* # sessions */ } * ur; struct urec *urlast; int a_ssize = A_SSIZE; int ssize; struct srec { /* 1 for each distinct session */ dev_t sr_tty; /* dev, used to connect with process*/ time_t sr_start; /* start time of session */ time_t sr_end; /* end time of session */ } * sr; char *getname(uid_t, dev_t, time_t); void readctmp(char *); char *getnamc(uid_t, dev_t, time_t); int aread(int); char *uidtonam(); int main(int argc, char **argv) { long elaps[2]; ulong_t etime, stime; unsigned long mem; ulong_t expand(); int ver; /* version of acct struct */ int aread(); if ((ur = (struct urec *) calloc(a_usize, sizeof (struct urec))) == NULL) { fprintf(stderr, "acctpr1: Cannot allocate memory\n"); exit(3); } urlast = ur; if ((sr = (struct srec *) calloc(a_ssize, sizeof (struct srec))) == NULL) { fprintf(stderr, "acctpr1: Cannot allocate memory\n"); exit(3); } while (--argc > 0) { if (**++argv == '-') switch(*++*argv) { } else { readctmp(*argv); } } if (fread((char *)&ab, sizeof(struct acct), 1, stdin) != 1) exit(1); else if (ab.ac_flag & AEXPND) ver = 2; /* 4.0 acct structure */ else ver = 1; /* 3.x acct structure */ rewind(stdin); while (aread(ver) == 1) { if (!MYKIND(ab.ac_flag)) continue; pb.pt_uid = ab.ac_uid; CPYN(pb.pt_name, getname(ab.ac_uid, ab.ac_tty, ab.ac_btime)); /* * approximate cpu P/NP split as same as elapsed time */ if ((etime = SECS(expand(ab.ac_etime))) == 0) etime = 1; stime = expand(ab.ac_stime) + expand(ab.ac_utime); mem = expand(ab.ac_mem); if(pnpsplit(ab.ac_btime, etime, elaps) == 0) { fprintf(stderr, "acctprc1: could not calculate prime/non-prime hours\n"); exit(1); } pb.pt_cpu[0] = (double)stime * (double)elaps[0] / etime; pb.pt_cpu[1] = (stime > pb.pt_cpu[0])? stime - pb.pt_cpu[0] : 0; pb.pt_cpu[1] = stime - pb.pt_cpu[0]; if (stime) pb.pt_mem = (mem + stime - 1) / stime; else pb.pt_mem = 0; /* unlikely */ printf("%ld\t%.*s\t%lu\t%lu\t%u\n", pb.pt_uid, OUTPUT_NSZ, pb.pt_name, pb.pt_cpu[0], pb.pt_cpu[1], pb.pt_mem); } exit(0); } /* * return ptr to name corresponding to uid * try ctmp first, then use uidtonam (internal list or passwd file) */ char * getname(uid_t uid, dev_t tty, time_t start) { char *p; if ((p = getnamc(uid, tty, start)) != NULL) return (p); return (uidtonam(uid)); } /* * read ctmp file, build up urec-srec data structures for * later use by getnamc */ void readctmp(char *fname) { FILE *fp; struct urec *up; struct srec *sp; int i = 0, j = 0, k=0; if ((fp = fopen(fname, "r")) == NULL) { fprintf(stderr, "acctprc1: can't open %s\n", fname); return; } up = NULL; sp = sr; while (fscanf(fp, "%hd\t%ld\t%s\t%lu\t%lu\t%lu\t%*[^\n]", &cb.ct_tty, &cb.ct_uid, cb.ct_name, &cb.ct_con[0], &cb.ct_con[1], &cb.ct_start) != EOF) { if (up == NULL || cb.ct_uid != up->ur_uid || !EQN(cb.ct_name, up->ur_name)) { if (up == NULL) up = ur; if (++up >= &ur[a_usize]) { a_usize = a_usize + A_USIZE; if ((ur = (struct urec *) realloc(ur, a_usize * sizeof (struct urec))) == NULL) { fprintf(stderr, "acctprc1: 1 Cannot reallocate memory\n"); exit(2); } up = &ur[a_usize - A_USIZE]; } up->ur_uid = cb.ct_uid; CPYN(up->ur_name, cb.ct_name); up->ur_srec = sp; up->ur_cnt = 0; } if (sp >= &sr[a_ssize-1]) { a_ssize = a_ssize + A_SSIZE; if ((sr = (struct srec *) realloc(sr, a_ssize * sizeof (struct srec))) == NULL) { fprintf(stderr, "acctprc1: 2 Cannot reallocate memory\n"); printf("errno=%d\n", errno); exit(2); } sp = &sr[a_ssize - A_SSIZE]; } sp->sr_tty = cb.ct_tty; sp->sr_start = cb.ct_start; sp->sr_end = cb.ct_start + cb.ct_con[0] + cb.ct_con[1]; sp++; up->ur_cnt++; } if (up != NULL) urlast = ++up; fclose(fp); } /* * using urec-srec data (if any), make best guess at login name * corresponding to uid, return ptr to the name. * must match on tty; use start time to help guess * for any urec having same uid as uid, search array of associated * srecs for those having same tty * if start time of process is within range of session, that's it * if none can be found within range, give it to person of same uid * who last logged off on that terminal */ char * getnamc(uid_t uid, dev_t tty, time_t start) { struct urec *up; struct srec *sp; struct srec *splast; long latest; char *guess; latest = 0; guess = NULL; for (up = ur; up < urlast && uid >= up->ur_uid; up++) if (uid == up->ur_uid) { sp = up->ur_srec; splast = sp+up->ur_cnt; for (; sp < splast; sp++) if (tty == sp->sr_tty) { if (start >= sp->sr_start && start <= sp->sr_end) return(up->ur_name); if (start >= sp->sr_start && sp->sr_end > latest) { latest = sp->sr_end; guess = up->ur_name; } } } return(guess); } int aread(int ver) { 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); return(ret != 1 ? 0 : 1); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctprc2 ptacct * reads std. input (in ptmp.h/ascii format) * hashes items with identical uid/name together, sums times * sorts in uid/name order, writes tacct.h records to output */ #include #include #include "acctdef.h" #include #include #include #include struct ptmp pb; struct tacct tb; struct utab { uid_t ut_uid; char ut_name[NSZ]; float ut_cpu[2]; /* cpu time (mins) */ float ut_kcore[2]; /* kcore-mins */ long ut_pc; /* # processes */ } * ub; static int usize; void **root = NULL; void output(void); void enter(struct ptmp *); int main(int argc, char **argv) { while (scanf("%ld\t%s\t%lu\t%lu\t%u", &pb.pt_uid, pb.pt_name, &pb.pt_cpu[0], &pb.pt_cpu[1], &pb.pt_mem) != EOF) enter(&pb); output(); exit(0); } int node_compare(const void *node1, const void *node2) { if (((const struct utab *)node1)->ut_uid > \ ((const struct utab *)node2)->ut_uid) return(1); else if (((const struct utab *)node1)->ut_uid < \ ((const struct utab *)node2)->ut_uid) return(-1); else return(strcmp(((const struct utab *) node1)->ut_name, ((const struct utab *) node2)->ut_name)); } void enter(struct ptmp *p) { unsigned int i; double memk; struct utab **pt; /* clear end of short users' names */ for(i = strlen(p->pt_name) + 1; i < NSZ; p->pt_name[i++] = '\0') ; if ((ub = (struct utab *)malloc(sizeof (struct utab))) == NULL) { fprintf(stderr, "acctprc2: malloc fail!\n"); exit(2); } ub->ut_uid = p->pt_uid; CPYN(ub->ut_name, p->pt_name); ub->ut_cpu[0] = MINT(p->pt_cpu[0]); ub->ut_cpu[1] = MINT(p->pt_cpu[1]); memk = KCORE(pb.pt_mem); ub->ut_kcore[0] = memk * MINT(p->pt_cpu[0]); ub->ut_kcore[1] = memk * MINT(p->pt_cpu[1]); ub->ut_pc = 1; if (*(pt = (struct utab **)tsearch((void *)ub, (void **)&root, \ node_compare)) == NULL) { fprintf(stderr, "Not enough space available to build tree\n"); exit(1); } if (*pt != ub) { (*pt)->ut_cpu[0] += MINT(p->pt_cpu[0]); (*pt)->ut_cpu[1] += MINT(p->pt_cpu[1]); (*pt)->ut_kcore[0] += memk * MINT(p->pt_cpu[0]); (*pt)->ut_kcore[1] += memk * MINT(p->pt_cpu[1]); (*pt)->ut_pc++; free(ub); } } void print_node(const void *node, VISIT order, int level) { if (order == postorder || order == leaf) { tb.ta_uid = (*(struct utab **)node)->ut_uid; CPYN(tb.ta_name, (*(struct utab **)node)->ut_name); tb.ta_cpu[0] = ((*(struct utab **)node)->ut_cpu[0]); tb.ta_cpu[1] = ((*(struct utab **)node)->ut_cpu[1]); tb.ta_kcore[0] = (*(struct utab **)node)->ut_kcore[0]; tb.ta_kcore[1] = (*(struct utab **)node)->ut_kcore[1]; tb.ta_pc = (*(struct utab **)node)->ut_pc; fwrite(&tb, sizeof(tb), 1, stdout); } } void output(void) { twalk((struct utab *)root, print_node); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * acctwtmp reason /var/adm/wtmpx * writes utmpx.h record (with current time) to specific file * acctwtmp `uname` /var/adm/wtmpx as part of startup * acctwtmp pm /var/adm/wtmpx (taken down for pm, for example) */ #include #include #include "acctdef.h" #include #include #include struct utmpx wb; int main(int argc, char **argv) { struct utmpx *p; if (argc < 3) (void) fprintf(stderr, "Usage: %s reason wtmpx_file\n", argv[0]), exit(1); (void) strncpy(wb.ut_line, argv[1], sizeof (wb.ut_line)); wb.ut_line[11] = '\0'; wb.ut_type = ACCOUNTING; time(&wb.ut_xtime); utmpxname(argv[2]); setutxent(); if (pututxline(&wb) == NULL) printf("acctwtmp - pututxline failed\n"); endutxent(); exit(0); } #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6 */ # "chargefee login-name number" # "emits tacct.h/ascii record to charge name $number" cd /var/adm PATH=/usr/lib/acct:/usr/bin:/usr/sbin if test $# -lt 2; then echo "usage: chargefee name number" >&2 exit 1 fi _entry="`getent passwd $1`" if test -z "${_entry}"; then echo "can't find login name $1" >&2 exit 2 fi case "$2" in -[0-9]*|[0-9]*);; *) echo "charge invalid: $2" >&2 exit 3 esac if test ! -r fee; then nulladm fee fi _userid=`echo "${_entry}" | cut -d: -f3` # get the UID echo "${_userid} $1 0 0 0 0 0 0 0 0 0 0 $2" >>fee #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # # Copyright 1997 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.9 */ # periodically check the size of /var/adm/pacct # if over $1 blocks (default: maxi 200, mini 500), execute # turnacct switch _adm=/var/adm PATH=/usr/lib/acct:/usr/bin:/usr/sbin trap "rm -f /var/adm/cklock*; exit 0" 0 1 2 3 9 15 export PATH if [ -f uts -a uts ] then _max=${1-200} else _max=${1-500} fi _MIN_BLKS=500 cd /var/adm # set up lock files to prevent simultaneous checking cp /dev/null cklock chmod 400 cklock ln cklock cklock1 if test $? -ne 0 ; then exit 1; fi # If there are less than $_MIN_BLKS free blocks left on the /var/adm # file system, turn off the accounting (unless things improve # the accounting wouldn't run anyway). If something has # returned the file system space, restart accounting. This # feature relies on the fact that ckpacct is kicked off by the # cron at least once per hour. _blocks=`df $_adm | sed 's/.*://' | awk '{ print $1 }'` if [ "$_blocks" -lt $_MIN_BLKS -a -f /tmp/acctoff ];then echo "ckpacct: $_adm still low on space ($_blocks blks); \c" echo "acctg still off" ( echo "ckpacct: $_adm still low on space ($_blocks blks); \c" echo "acctg still off" ) | mailx root adm exit 1 elif [ "$_blocks" -lt $_MIN_BLKS ];then echo "ckpacct: $_adm too low on space ($_blocks blks); \c" echo "turning acctg off" ( echo "ckpacct: $_adm too low on space ($_blocks blks); \c" echo "turning acctg off" ) | mailx root adm nulladm /tmp/acctoff turnacct off exit 1 elif [ -f /tmp/acctoff ];then echo "ckpacct: $_adm free space restored; turning acctg on" echo "ckpacct: $_adm free space restored; turning acctg on" | \ mailx root adm rm /tmp/acctoff turnacct on fi _cursize="`du -s pacct | sed 's/ .*//'`" if [ "${_max}" -lt "${_cursize}" ]; then turnacct switch fi /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* Copyright (c) 1999, 2000 by Sun Microsystems, Inc. */ /* All rights reserved. */ #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ /* * fudge an entry to wtmpx for each user who is still logged on when * acct is being run. This entry marks a DEAD_PROCESS, and the * current time as time stamp. This should be done before connect * time is processed. Called by runacct. */ #include #include #include int main(int argc, char **argv) { struct utmpx *utmpx; setutxent(); while ((utmpx = getutxent()) != NULL) { if (utmpx->ut_type == USER_PROCESS) { utmpx->ut_type = DEAD_PROCESS; time(&utmpx->ut_xtime); (void) updwtmpx(WTMPX_FILE, utmpx); } } endutxent(); return (0); } #!/sbin/sh # # 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 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.13 */ # 'perform disk accounting' PATH=:/usr/lib/acct:/usr/bin:/usr/sbin export PATH if [ -f /usr/bin/uts -a /usr/bin/uts ] then format="dev mnt type comment" else format="special dev mnt fstype fsckpass automnt mntflags" fi _dir=/var/adm _pickup=acct/nite set -- `getopt o $*` if [ $? -ne 0 ] then echo "Usage: $0 [ -o ] [ filesystem ... ]" >&2 exit 1 fi for i in $*; do case $i in -o) SLOW=1; shift;; --) shift; break;; esac done cd ${_dir} if [ "$SLOW" = "" ] then if [ $# -lt 1 ] then if [ -f /usr/bin/uts -a /usr/bin/uts ] then DEVLIST=/etc/checklist else DEVLIST=/etc/vfstab fi while : do if read $format then if [ -z "$special" ] then continue elif [ `expr $special : '\(.\)'` = \# ] then continue fi if [ "$fsckpass" = "-" ] then continue fi if [ $fstype != ufs ] && [ $fstype != vxfs ] then continue fi # Make sure FS is mounted if egrep -s "^${special}[ ]+${mnt}[ ]" /etc/mnttab then find $mnt -mount -print | \ acctdusg > \ `echo $dev | sed s-/-_-g`.dtmp & fi else wait break fi done < $DEVLIST if [ "`echo *.dtmp`" != "*.dtmp" ] then awk -e ' {tot[$1] += $3; if (name[$1] == "") name[$1] = "" $2} END {for (i in tot) printf "%d\t%s\t%d\n", i, name[i], tot[i]}' *.dtmp > dtmp rm -f *.dtmp else > dtmp fi else find $* -mount -print | acctdusg > dtmp fi else if [ $# -lt 1 ] then args="/" else args="$*" fi for i in $args; do if [ ! -d $i ] then echo "$0: $i is not a directory -- ignored" >&2 else dir="$i $dir" fi done if [ "$dir" = "" ] then echo "$0: No data" >&2 > dtmp else find $dir -print | acctdusg > dtmp fi fi sort +0n +1 dtmp | acctdisk > ${_pickup}/disktacct chmod 644 ${_pickup}/disktacct chown adm ${_pickup}/disktacct /* * 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 */ #include #include #include #include "acctdef.h" #include #include #include struct utmpx Ut; static char time_buf[50]; static int inp(FILE *, struct utmpx *); int main(int c, char **v) { int iflg, cflg; (void) setlocale(LC_ALL, ""); iflg = cflg = 0; while (--c > 0) { if (**++v == '-') while (*++*v) switch (**v) { case 'c': cflg++; continue; case 'i': iflg++; continue; } } for (;;) { if (iflg) { if (inp(stdin, &Ut) == EOF) break; } else { if (fread(&Ut, sizeof (Ut), 1, stdin) != 1) break; } if (cflg) fwrite(&Ut, sizeof (Ut), 1, stdout); else { cftime(time_buf, DATE_FMT, &Ut.ut_xtime); printf("%-*.*s %-4.4s %-*.*s %9d %2hd " "%4.4ho %4.4ho %ld %ld %d %hd %-.*s %s", NSZ, NSZ, Ut.ut_name, Ut.ut_id, LINESZ, LINESZ, Ut.ut_line, Ut.ut_pid, Ut.ut_type, Ut.ut_exit.e_termination, Ut.ut_exit.e_exit, Ut.ut_xtime, Ut.ut_tv.tv_usec, Ut.ut_session, Ut.ut_syslen, Ut.ut_syslen, Ut.ut_host, time_buf); } } exit(0); } static int inp(FILE *file, struct utmpx *u) { char buf[BUFSIZ]; char *p; int i; if (fgets((p = buf), BUFSIZ, file) == NULL) return (EOF); for (i = 0; i < NSZ; i++) /* Allow a space in name field */ u->ut_name[i] = *p++; for (i = NSZ-1; i >= 0; i--) { if (u->ut_name[i] == ' ') u->ut_name[i] = '\0'; else break; } p++; for (i = 0; i < 4; i++) if ((u->ut_id[i] = *p++) == ' ') u->ut_id[i] = '\0'; p++; for (i = 0; i < LINESZ; i++) /* Allow a space in line field */ u->ut_line[i] = *p++; for (i = LINESZ-1; i >= 0; i--) { if (u->ut_line[i] == ' ') u->ut_line[i] = '\0'; else break; } sscanf(p, "%d %hd %ho %ho %ld %ld %d %hd", &u->ut_pid, &u->ut_type, &u->ut_exit.e_termination, &u->ut_exit.e_exit, &u->ut_xtime, &u->ut_tv.tv_usec, &u->ut_session, &u->ut_syslen); if (u->ut_syslen > 1) sscanf(p, "%*d %*hd %*ho %*ho %*ld %*ld %*d %*hd %s", u->ut_host); else u->ut_host[0] = '\0'; return (1); } #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6 */ # "lastlogin - keep record of date each person last logged in" # "bug - the date shown is usually 1 more than it should be " # " because lastlogin is run at 4am and checks the last" # " 24 hrs worth of process accounting info (in pacct)" PATH=/usr/lib/acct:/usr/bin:/usr/sbin cd /var/adm/acct if test ! -r sum/loginlog; then nulladm sum/loginlog fi # "cleanup loginlog - delete entries of those no longer in" # "/etc/passwd and add an entry for those recently added" # "line 1 - get file of current logins in same form as loginlog" # "line 2 - merge the 2 files; use uniq to delete common" # "lines resulting in those lines which need to be" # "deleted or added from loginlog" # "line 3 - result of sort will be a file with 2 copies" # "of lines to delete and 1 copy of lines that are " # "valid; use uniq to remove duplicate lines" getent passwd | sed "s/\([^:]*\).*/00-00-00 \1/" |\ sort +1 - sum/loginlog | uniq -u +10 |\ sort +1 - sum/loginlog |uniq -u > sum/tmploginlog cp sum/tmploginlog sum/loginlog # "update loginlog" _d="`date +%y-%m-%d`" _day=`date +%m%d` # "lines 1 and 2 - remove everything from the total" # "acctng records with connect info except login" # "name and adds the date" # "line 3 - sorts in reverse order by login name; gets" # "1st occurrence of each login name and resorts by date" acctmerg -a < nite/ctacct.$_day | \ sed -e "s/^[^ ]*[ ]\([^ ]*\)[ ].*/$_d \1/" | \ nawk '/^00-00-00/ { $0 = "00" $0 } /^[0-9][0-9]-/ { d=substr($0,1,2); if (d<=68) { $0 = "20" $0 } else { $0 = "19" $0 } } { print }' - sum/loginlog | \ sort -r +1 | uniq +10 | sort | \ nawk '/^[0-9][0-9][0-9][0-9]-/ { $0 = substr($0,3) } { print }' > sum/tmploginlog cp sum/tmploginlog sum/loginlog rm -f sum/tmploginlog # # 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 (c) 1989 by Sun Microsystems, Inc. # Copyright (c) 2018, Joyent, Inc. # LIBS= a.a OBJS= pnpsplit.o devtolin.o expand.o lintodev.o \ namtouid.o tmless.o tmsecs.o uidtonam.o substr.o SRCS= $(OBJS:%.o=%.c) include ../../Makefile.cmd CPPFLAGS = -D_LTYPES -I.. $(CPPFLAGS.master) CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-parentheses CERRWARN += -Wno-extra # not linted SMATCH=off .KEEP_STATE: .PARALLEL: $(OBJS) all: $(LIBS) $(LIBS): $(OBJS) $(AR) $(ARFLAGS) $@ $(OBJS) clean: $(RM) $(OBJS) clobber: clean $(RM) $(LIBS) lint: $(LINT.c) $(SRCS) install: .PRECIOUS: $(LIBS) /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * convert device to linename (as in /dev/linename) * return ptr to LSZ-byte string, "?" if not found * device must be character device * maintains small list in tlist structure for speed */ #include #include #include #include "acctdef.h" #include #include #include static int tsize1; static struct tlist { char tname[LSZ]; /* linename */ dev_t tdev; /* device */ } tl[TSIZE1]; char *strncpy(); dev_t lintodev(); static char dev_dir[] = "/dev"; static char *def_srch_dirs[] = { "/dev/term", "/dev/pts", "/dev/xt", NULL }; char file_name[MAX_DEV_PATH]; /* name being returned */ static int srch_dir(); char * devtolin(dev_t device) { struct tlist *tp; char **srch_dirs; /* priority directories to search first */ int found = 0; int dirno = 0; for (tp = tl; tp < &tl[tsize1]; tp++) if (device == tp->tdev) return (tp->tname); srch_dirs = def_srch_dirs; while ((!found) && (srch_dirs[dirno] != NULL)) { /* * if /dev is one of the priority directories we should only * search its top level for now (set depth = MAX_SEARCH_DEPTH) */ found = srch_dir(device, srch_dirs[dirno], ((strcmp(srch_dirs[dirno], dev_dir) == 0) ? MAX_SRCH_DEPTH : 1), NULL); dirno++; } /* * if not yet found search remaining /dev directory skipping the * priority directories */ if (!found) found = srch_dir(device, dev_dir, 0, srch_dirs); /* * if found then put it (without the "/dev/" prefix) in the tlist * structure and return the path name without the "/dev/" prefix */ if (found) { if (tsize1 < TSIZE1) { tp->tdev = device; CPYN(tp->tname, file_name+5); tsize1++; } return (file_name+5); } else { /* * if not found put "?" in the tlist structure for that device * and return "?" */ if (tsize1 < TSIZE1) { tp->tdev = device; CPYN(tp->tname, "?"); tsize1++; } } return ("?"); } /* * Arguments: * device device we are looking for * path current path * depth current depth * skip_dirs directories that don't need searched */ static int srch_dir(dev_t device, char *path, int depth, char *skip_dirs[]) { DIR *fdev; struct dirent *d; int dirno = 0; int found = 0; int path_len; char *last_comp; struct stat sb; /* do we need to search this directory? */ if ((skip_dirs != NULL) && (depth != 0)) while (skip_dirs[dirno] != NULL) if (strcmp(skip_dirs[dirno++], path) == 0) return (0); /* open the directory */ if ((fdev = opendir(path)) == NULL) return (0); /* initialize file name using path name */ path_len = strlen(path); strcpy(file_name, path); last_comp = file_name + path_len; *last_comp++ = '/'; /* start searching this directory */ while ((!found) && ((d = readdir(fdev)) != NULL)) { if (d->d_ino != 0) { /* * if name would not be too long append it to * directory name, otherwise skip this entry */ if ((int)(path_len + strlen(d->d_name) + 2) > MAX_DEV_PATH) continue; else strcpy(last_comp, d->d_name); /* * if this directory entry has the device number we * need, then the name is found. Otherwise if it's a * directory (not . or ..) and we haven't gone too * deep, recurse. */ if (lintodev(file_name+5) == device) { found = 1; break; } else if ((depth < MAX_SRCH_DEPTH) && (strcmp(d->d_name, ".") != 0) && (strcmp(d->d_name, "..") != 0) && (stat(file_name, &sb) != -1) && ((sb.st_mode & S_IFMT) == S_IFDIR)) { found = srch_dir(device, file_name, depth+1, skip_dirs); } } } closedir(fdev); return (found); } /* * 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 */ #include #include #include #ifdef uts float #else /* * For as long as ct is of type comp_t, and comp_t is defined to be of type * unsigned short, the maximum value of ct will be 2^16-1 ie 65535. Based on * this input value, the maximum value that expand() can return will therefore * be 4292870144. A return type of ulong_t ensures we don't overflow. */ ulong_t #endif expand(comp_t ct) { int e; #ifdef uts float f; #else ulong_t f; #endif e = (ct >> 13) & 07; f = ct & 017777; while (e-- > 0) #ifdef uts f *= 8.0; /* can't shift a float */ #else f <<=3; #endif return f; } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.9 */ /* * convert linename to device * return -1 if nonexistent or not character device */ #include #include #include #include #include "acctdef.h" static char devtty[5+LSZ+1] = "/dev/xxxxxxxx"; char *strncpy(); dev_t lintodev(linename) char linename[LSZ]; { struct stat sb; strncpy(&devtty[5], linename, LSZ); if (stat(devtty, &sb) != -1 && (sb.st_mode&S_IFMT) == S_IFCHR) return((dev_t)sb.st_rdev); return((dev_t)NODEV); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * namtouid converts login names to uids * maintains ulist for speed only */ #include #include #include "acctdef.h" #include static int usize; static struct ulist { char uname[NSZ]; uid_t uuid; } ul[A_USIZE]; char ntmp[NSZ+1]; char *strncpy(); uid_t namtouid(name) char name[NSZ]; { register struct ulist *up; register uid_t tuid; struct passwd *getpwnam(), *pp; for (up = ul; up < &ul[usize]; up++) if (strncmp(name, up->uname, NSZ) == 0) return(up->uuid); strncpy(ntmp, name, NSZ); (void) setpwent(); if ((pp = getpwnam(ntmp)) == NULL) tuid = -1; else { tuid = pp->pw_uid; if (usize < A_USIZE) { CPYN(up->uname, name); up->uuid = tuid; usize++; } } return(tuid); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * pnpsplit splits interval into prime & nonprime portions * ONLY ROUTINE THAT KNOWS ABOUT HOLIDAYS AND DEFN OF PRIME/NONPRIME */ #include #include #include #include "acctdef.h" #include #include /* * validate that hours and minutes of prime/non-prime read in * from holidays file fall within proper boundaries. * Time is expected in the form and range of 0000-2400. */ static int thisyear = 1970; /* this is changed by holidays file */ static int holidays[NHOLIDAYS]; /* holidays file day-of-year table */ static int day_tab[2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; /* * prime(0) and nonprime(1) times during a day * for BTL, prime time is 9AM to 5PM */ static struct hours { int h_sec; /* normally always zero */ int h_min; /* initialized from holidays file (time%100) */ int h_hour; /* initialized from holidays file (time/100) */ long h_type; /* prime/nonprime of previous period */ } h[4]; /* the sec, min, hr of the day's end */ struct tm daysend = { .tm_sec = 0, .tm_min = 60, .tm_hour = 23 }; long tmsecs(struct tm *, struct tm *); /* * split interval of length etime, starting at start into prime/nonprime * values, return as result * input values in seconds */ int pnpsplit(long start, ulong_t etime, long result[2]) { struct tm cur, end, hours; time_t tcur, tend; long tmp; int sameday; struct hours *hp; /* once holidays file is read, this is zero */ if(thisyear && (checkhol() == 0)) { return(0); } tcur = start; tend = start + etime; memcpy(&end, localtime(&tend), sizeof(end)); result[PRIME] = 0; result[NONPRIME] = 0; while ( tcur < tend ) { /* one iteration per day or part thereof */ memcpy(&cur, localtime(&tcur), sizeof(cur)); sameday = cur.tm_yday == end.tm_yday; if (ssh(&cur)) { /* ssh:only NONPRIME */ if (sameday) { result[NONPRIME] += tend-tcur; break; } else { tmp = tmsecs(&cur, &daysend); result[NONPRIME] += tmp; tcur += tmp; } } else { /* working day, PRIME or NONPRIME */ for (hp = h; tmless(hp, &cur); hp++); for (; hp->h_sec >= 0; hp++) { if (sameday && tmless(&end, hp)) { /* WHCC mod, change from = to += 3/6/86 Paul */ result[hp->h_type] += tend-tcur; tcur = tend; break; /* all done */ } else { /* time to next PRIME /NONPRIME change */ hours.tm_sec = hp->h_sec; hours.tm_min = hp->h_min; hours.tm_hour = hp->h_hour; tmp = tmsecs(&cur, &hours); result[hp->h_type] += tmp; tcur += tmp; cur.tm_sec = hp->h_sec; cur.tm_min = hp->h_min; cur.tm_hour = hp->h_hour; } } } } return(1); } /* * Starting day after Christmas, complain if holidays not yet updated. * This code is only executed once per program invocation. */ int checkhol(void) { struct tm *tp; time_t t; if(inithol() == 0) { fprintf(stderr, "pnpsplit: holidays table setup failed\n"); thisyear = 0; holidays[0] = -1; return(0); } time(&t); tp = localtime(&t); tp->tm_year += 1900; if ((tp->tm_year == thisyear && tp->tm_yday > 359) || tp->tm_year > thisyear) fprintf(stderr, "***UPDATE %s WITH NEW HOLIDAYS***\n", HOLFILE); thisyear = 0; /* checkhol() will not be called again */ return(1); } /* * ssh returns 1 if Sat, Sun, or Holiday */ int ssh(struct tm *ltp) { int i; if (ltp->tm_wday == 0 || ltp->tm_wday == 6) return(1); for (i = 0; holidays[i] >= 0; i++) if (ltp->tm_yday == holidays[i]) return(1); return(0); } /* * inithol - read from an ascii file and initialize the "thisyear" * variable, the times that prime and non-prime start, and the * holidays array. */ int inithol(void) { FILE *holptr; char holbuf[128]; int line = 0, holindx = 0, errflag = 0; int pstart, npstart; int doy; /* day of the year */ int month, day; char *c; if((holptr=fopen(HOLFILE, "r")) == NULL) { perror(HOLFILE); fclose(holptr); return(0); } while(fgets(holbuf, sizeof(holbuf), holptr) != NULL) { /* skip over blank lines and comments */ if (holbuf[0] == '*') continue; for (c = holbuf; isspace(*c); c++) /* is a space */; if (*c == '\0') continue; else if(++line == 1) { /* format: year p-start np-start */ if(sscanf(holbuf, "%4d %4d %4d", &thisyear, &pstart, &npstart) != 3) { fprintf(stderr, "%s: bad {yr ptime nptime} conversion\n", HOLFILE); errflag++; break; } /* validate year */ if(thisyear < 1970 || thisyear > 2037) { fprintf(stderr, "pnpsplit: invalid year: %d\n", thisyear); errflag++; break; } /* validate prime/nonprime hours */ if((! okay(pstart)) || (! okay(npstart))) { fprintf(stderr, "pnpsplit: invalid p/np hours\n"); errflag++; break; } /* Set up start of prime time; 2400 == 0000 */ h[0].h_sec = 0; h[0].h_min = pstart%100; h[0].h_hour = (pstart/100==24) ? 0 : pstart/100; h[0].h_type = NONPRIME; /* Set up start of non-prime time; 2400 == 2360 */ if ((npstart/100) == 24) { h[1].h_sec = 0; h[1].h_min = 60; h[1].h_hour = 23; } else { h[1].h_sec = 0; h[1].h_min = npstart % 100; h[1].h_hour = npstart / 100; } h[1].h_type = PRIME; /* This is the end of the day */ h[2].h_sec = 0; h[2].h_min = 60; h[2].h_hour = 23; h[2].h_type = NONPRIME; /* The end of the array */ h[3].h_sec = -1; continue; } else if(holindx >= NHOLIDAYS) { fprintf(stderr, "pnpsplit: too many holidays, "); fprintf(stderr, "recompile with larger NHOLIDAYS\n"); errflag++; break; } /* Fill up holidays array from holidays file */ sscanf(holbuf, "%d/%d %*s %*s %*[^\n]\n", &month, &day); if (month < 0 || month > 12) { fprintf(stderr, "pnpsplit: invalid month %d\n", month); errflag++; break; } if (day < 0 || day > 31) { fprintf(stderr, "pnpsplit: invalid day %d\n", day); errflag++; break; } doy = day_of_year(thisyear, month, day); holidays[holindx++] = (doy - 1); } fclose(holptr); if(!errflag && holindx < NHOLIDAYS) { holidays[holindx] = -1; return(1); } else return(0); } /* * tmsecs returns number of seconds from t1 to t2, * times expressed in localtime format. * assumed that t1 <= t2, and are in same day. */ long tmsecs(struct tm *t1, struct tm *t2) { return((t2->tm_sec - t1->tm_sec) + 60*(t2->tm_min - t1->tm_min) + 3600L*(t2->tm_hour - t1->tm_hour)); } /* * return 1 if t1 earlier than t2 (times in localtime format) * assumed that t1 and t2 are in same day */ int tmless(struct tm *t1, struct tm *t2) { if (t1->tm_hour != t2->tm_hour) return(t1->tm_hour < t2->tm_hour); if (t1->tm_min != t2->tm_min) return(t1->tm_min < t2->tm_min); return(t1->tm_sec < t2->tm_sec); } /* set day of year from month and day */ int day_of_year(int year, int month, int day) { int i, leap; leap = year%4 == 0 && year%100 || year%400 == 0; for (i = 1; i < month; i++) day += day_tab[leap][i]; return(day); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.5 */ /* Place the `len' length substring of `as' starting at `as[origin]' in `aresult'. Return `aresult'. Note: The copying of as to aresult stops if either the specified number (len) characters have been copied, or if the end of as is found. A negative len generally guarantees that everything gets copied. */ char *substr(as, aresult, origin, len) char *as, *aresult; int origin; register unsigned len; { register char *s, *result; s = as + origin; result = aresult; ++len; while (--len && (*result++ = *s++)) ; if (len == 0) *result = 0; return(aresult); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * return 1 if t1 earlier than t2 (times in localtime format) * assumed that t1 and t2 are in same day */ #include int tmless(t1, t2) register struct tm *t1, *t2; { if (t1->tm_hour != t2->tm_hour) return(t1->tm_hour < t2->tm_hour); if (t1->tm_min != t2->tm_min) return(t1->tm_min < t2->tm_min); return(t1->tm_sec < t2->tm_sec); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.5 */ /* * tmsecs returns number of seconds from t1 to t2, * times expressed in localtime format. * assumed that t1 <= t2, and are in same day. */ #include long tmsecs(t1, t2) register struct tm *t1, *t2; { return((t2->tm_sec - t1->tm_sec) + 60*(t2->tm_min - t1->tm_min) + 3600L*(t2->tm_hour - t1->tm_hour)); } /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * convert uid to login name; interface to getpwuid that keeps up to USIZE1 * names to avoid unnecessary accesses to passwd file * returns ptr to NSZ-byte name (not necessarily null-terminated) * returns ptr to "?" if cannot convert */ #include #include #include #include "acctdef.h" #include static int usize1; static struct ulist { char uname[NSZ]; uid_t uuid; } ul[USIZE1]; char *strncpy(); char * uidtonam(uid) uid_t uid; { register struct ulist *up; struct passwd *getpwuid(); register struct passwd *pp; for (up = ul; up < &ul[usize1]; up++) if (uid == up->uuid) return(up->uname); setpwent(); if ((pp = getpwuid(uid)) == NULL) return("?"); else { if (usize1 < USIZE1) { up->uuid = uid; CPYN(up->uname, pp->pw_name); usize1++; } return(pp->pw_name); } } #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6 */ # "this procedure should be run periodically ( by month or fiscal )" _adm=/var/adm _sum=${_adm}/acct/sum _fiscal=${_adm}/acct/fiscal PATH=:/usr/lib/acct:/usr/bin:/usr/sbin export PATH #if test $# -ne 1; then # echo "usage: monacct fiscal-number" # exit #fi _period=${1-`date +%m`} cd ${_adm} # "move summary tacct file to fiscal directory" mv ${_sum}/tacct ${_fiscal}/tacct${_period} # "delete the daily tacct files" rm -f ${_sum}/tacct???? # "restart summary tacct file" nulladm ${_sum}/tacct # "move summary cms file to fiscal directory mv ${_sum}/cms ${_fiscal}/cms${_period} # "restart summary cms file" nulladm ${_sum}/cms # "remove old prdaily reports" rm -f ${_sum}/rprt* # "produce monthly reports" prtacct ${_fiscal}/tacct${_period} > ${_fiscal}/fiscrpt${_period} acctcms -a -s ${_fiscal}/cms${_period} | \ pr -h "TOTAL COMMAND SUMMARY FOR FISCAL ${_period}" >> ${_fiscal}/fiscrpt${_period} pr -h "LAST LOGIN" -3 ${_sum}/loginlog >> ${_fiscal}/fiscrpt${_period} # "add commands here to do any charging of fees, etc" exit #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.5 */ # "nulladm name..." # "creates each named file mode 664" # "make sure owned by adm (in case created by root)" for _file do cp /dev/null $_file chmod 664 $_file chgrp adm $_file chown adm $_file done #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.7 */ # "print session record file (ctmp.h/ascii) with headings" # "prctmp file [heading]" PATH=/usr/lib/acct:/usr/bin:/usr/sbin (cat <&2 exit 2;; esac done shift `expr $OPTIND - 1` date=`date +%m%d` _sysname="`uname -n`" _nite=/var/adm/acct/nite _lib=/usr/lib/acct _sum=/var/adm/acct/sum cd ${_nite} if [ `expr "$1" : [01][0-9][0-3][0-9]` -eq 4 -a "$1" != "$date" ]; then if [ "$CMDEXCPT" = "1" ] then echo "Cannot print command exception reports except for `date '+%h %d'`" >&2 exit 5 fi if [ "$LINEEXCPT" = "1" ] then acctmerg -a < ${_sum}/tacct$1 | awk -f ${_lib}/ptelus.awk exit $? fi cat ${_sum}/rprt$1 exit 0 fi if [ "$CMDEXCPT" = 1 ] then acctcms -a -s ${_sum}/daycms | awk -f ${_lib}/ptecms.awk fi if [ "$LINEEXCPT" = 1 ] then acctmerg -a < ${_sum}/tacct${date} | awk -f ${_lib}/ptelus.awk fi if [ "$CMDEXCPT" = 1 -o "$LINEEXCPT" = 1 ] then exit 0 fi (cat reboots; echo ""; cat lineuse) | pr -h "DAILY REPORT FOR ${_sysname}" prtacct daytacct "DAILY USAGE REPORT FOR ${_sysname}" pr -h "DAILY COMMAND SUMMARY" daycms pr -h "MONTHLY TOTAL COMMAND SUMMARY" cms pr -h "LAST LOGIN" -3 ../sum/loginlog exit 0 #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.9 */ # "print daily/summary total accounting (any file in tacct.h format)" # "prtacct file [heading]" PATH=/usr/lib/acct:/usr/bin:/usr/sbin _filename=${1?"missing filename"} (cat < " MAXCPU " or Total KCORE > " MAXKCORE } NF <= 4 && length != 0 { next } $1 == "COMMAND" || $1 == "NAME" { print; next } NF == 10 && ( $4 > MAXCPU || $3 > MAXKCORE ) && $1 != "TOTALS" NF == 13 && ( $5 + $6 > MAXCPU || $4 > MAXKCORE ) && $1 != "TOTALS" length == 0 #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.8 */ # # 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 # BEGIN { MAXCPU = 20. # report if cpu usage is greater than this MAXKCORE = 500. # report is Kcore usage is greater than this MAXCONNECT = 120. # report if connect time is greater than this } NR == 1 { MAXCPU = MAXCPU + 0 MAXKCORE = MAXKCORE + 0 MAXCONNECT = MAXCONNECT + 0 printf "Logins with exceptional Prime/Non-prime Time Usage\n" printf ( "CPU > %d or KCORE > %d or CONNECT > %d\n\n\n", MAXCPU, MAXKCORE, MAXCONNECT) printf "\tLogin\t\tCPU (mins)\tKCORE-mins\tCONNECT-mins\tdisk" printf "\t# of\t# of\t# Disk\tfee\n" printf "UID\tName\t\tPrime\tNprime\tPrime\tNprime\t" printf "Prime\tNprime\tBlocks\tProcs\tSess\tSamples\n\n" } $3 > MAXCPU || $4 > MAXCPU || $5 > MAXKCORE || $6 > MAXKCORE || $7 > MAXCONNECT || $8 > MAXCONNECT { printf("%d\t%-8.8s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) } #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6 */ rm -f /var/adm/acct/sum/wtmp* rm -f /var/adm/acct/sum/pacct* rm -f /var/adm/acct/nite/lock* #!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.9 */ # "nitely accounting shell, should be run from cron (adm) at 4am" # "does process, connect, disk, and fee accounting" # "prepares command summaries" # "shell is restartable and provides reasonable diagnostics" _adm=/var/adm _nite=/var/adm/acct/nite _sum=/var/adm/acct/sum _wtmpx=/var/adm/wtmpx PATH=/usr/lib/acct:/usr/bin:/usr/sbin export PATH _statefile=${_nite}/statefile _active=${_nite}/active _lastdate=${_nite}/lastdate _date="`date +%m%d`" _errormsg="\n\n************ ACCT ERRORS : see ${_active}${_date}********\n\n" _MIN_BLKS=500 cd ${_adm} # "make sure that 2 crons weren't started, or leftover problems" date > ${_nite}/lock1 chmod 400 ${_nite}/lock1 ln ${_nite}/lock1 ${_nite}/lock if test $? -ne 0; then _lnkerr="\n\n*********** 2 CRONS or ACCT PROBLEMS***********\n\n\n" (date ; echo "$_lnkerr" ) | logger -p daemon.err echo "$_lnkerr" | mailx adm root echo "ERROR: locks found, run aborted" >> ${_active} rm -f ${_nite}/lock* exit 1 fi # Check to see if there is enough space in /var/adm to do nitely accounting # _blocks=`df $_adm | sed 's/.*://' | awk '{ print $1 }'` if [ "$_blocks" -le $_MIN_BLKS ];then echo "runacct: Insufficient space in $_adm ($_blocks blks); \c" echo "Terminating procedure" ( echo "runacct: Insufficient space in $_adm ($_blocks blks); \c" echo "Terminating procedure" ) > /tmp/accounting_tmpfile cat /tmp/accounting_tmpfile >> ${_active} cat /tmp/accounting_tmpfile | logger -p daemon.err mailx root adm < /tmp/accounting_tmpfile rm /tmp/accounting_tmpfile rm -f ${_nite}/lock* exit 1 fi case $# in 0) # "as called by the cron each day" if test ! -r ${_lastdate} ; then echo "0000" > ${_lastdate} fi if test "${_date}" = "`cat ${_lastdate}`"; then (date; echo "${_errormsg}") | logger -p daemon.err echo "${_errormsg}" | mailx root adm echo "ERROR: acctg already run for `date`: check ${_lastdate}" >> ${_active} rm -f ${_nite}/lock* mv ${_active} ${_active}${_date} exit 1 fi echo ${_date} > ${_lastdate} echo "SETUP" > ${_statefile} nulladm ${_active} echo ${_date} > ${_active} # debuging echo "\n\n\n\n\n********** SYSTEM ACCOUNTING STARTED `date` **********\n\n\n\n\n" | logger -p daemon.notice echo ${_date} > ${_active} # debuging ;; 1) # "runacct MMDD (date) will restart at current state" _date=$1 _errormsg="\n\n************ ACCT ERRORS : see ${_active}${_date}********\n\n" echo "restarting acctg for ${_date} at `cat ${_statefile}`" >> ${_active} echo "\n\n\n\n\n********** SYSTEM ACCOUNTING RESTARTED `date` **********\n\n\n\n\n" | logger -p daemon.notice ;; 2) # "runacct MMDD STATE restart at specified state" _date=$1 _errormsg="\n\n************ ACCT ERRORS : see ${_active}${_date}********\n\n" echo "restarting acctg for ${_date} at $2" >> ${_active} echo "previous state was `cat ${_statefile}`" >> ${_active} echo "$2" > ${_statefile} echo "\n\n\n\n\n********** SYSTEM ACCOUNTING RESTARTED `date` **********\n\n\n\n\n" | logger -p daemon.notice ;; *) (date; echo "${_errormsg}") | logger -p daemon.err echo "${_errormsg}" | mailx root adm echo "ERROR: runacct called with invalid arguments" > ${_active} rm -f ${_nite}/lock* mv ${_active} ${_active}${_date} exit 1 ;; esac # "processing is broken down into seperate, restartable states" # "the statefile is updated at the end of each state so that the" # "next loop through the while statement switches to the next state" while [ 1 ] do case "`cat ${_statefile}`" in SETUP) cd ${_adm} (date ; ls -l fee pacct* ${_wtmpx}* ) >> ${_active} # "switch current pacct file" turnacct switch _rc=$? if test ${_rc} -ne 0; then (date ; echo "${_errormsg}" ) | logger -p daemon.err echo "${_errormsg}" | mailx root adm echo "ERROR: turnacct switch returned rc=${_rc}" >> ${_active} rm -f ${_nite}/lock* mv ${_active} ${_active}${_date} exit 1 fi # " give pacct files unique names for easy restart " for _i in pacct?* do if [ "${_i}" = "pacct?*" ] then rm -f ${_nite}/lock* mv ${_active} ${_active}${_date} exit 1 fi if test -r S${_i}.${_date} ; then (date ; echo "${_errormsg}" ) | logger -p daemon.err echo "${_errormsg}" | mailx root adm echo "ERROR: S${_i}.${_date} already exists" >> ${_active} echo "file setups probably already run" >> ${_active} rm -f ${_nite}/lock* mv ${_active} ${_active}${_date} exit 1 fi mv ${_i} S${_i}.${_date} done # "add current time on end" if test -r ${_nite}/wtmpx.${_date} ; then (date ; echo "${_errormsg}" ) | logger -p daemon.err echo "${_errormsg}" | mailx root adm echo "ERROR: ${_nite}/wtmpx.${_date} already exists: run setup manually" > ${_active} rm -f ${_nite}/lock* mv ${_active} ${_active}${_date} exit 1 fi closewtmp # fudge a DEAD_PROCESS for /var/wtmpx cp ${_wtmpx} ${_nite}/${_date}.wtmpx acctwtmp "runacct" ${_nite}/${_date}.wtmpx nulladm ${_wtmpx} utmp2wtmp # fudge active user from utmpx to wtmpx echo "files setups complete" >> ${_active} echo "WTMPFIX" > ${_statefile} ;; WTMPFIX) # "verify the integrity of the wtmpx file" # "wtmpfix will automatically fix date changes" cd ${_nite} nulladm tmpwtmp wtmperror wtmpfix < ${_date}.wtmpx > tmpwtmp 2>wtmperror if test $? -ne 0 ; then (date ; echo "${_errormsg}") | mailx root adm echo "${_errormsg}" | logger -p daemon.err echo "ERROR: wtmpfix errors see ${_nite}/wtmperror${_date}" >> ${_active} rm -f ${_nite}/lock* mv ${_active} ${_active}${_date} mv wtmperror wtmperror${_date} exit 1 fi echo "wtmpx processing complete" >> ${_active} echo "CONNECT" > ${_statefile} ;; CONNECT) # "produce connect records" # "the lineuse and reboots files are used by prdaily" cd ${_nite} nulladm lineuse reboots log ctacct.${_date} acctcon -l lineuse -o reboots < tmpwtmp 2> log > ctacct.${_date} # if the following test is true, then pnpsplit complained about # the year and holidays not being up to date. This used to be # a fatal error, but now it will continue to process the accounting. # if test -s log ; then (date ; cat ${_nite}/log) | mailx adm root echo "\n\n************ ACCT ERRORS : see ${_nite}log********\n\n" | logger -p daemon.err cat ${_nite}/log >> ${_active}${_date} fi echo "connect acctg complete" >> ${_active} echo "PROCESS" > ${_statefile} ;; PROCESS) # "correlate Spacct and ptacct files by number" # "will not process Spacct file if corresponding ptacct exists" # "remove the ptacct file to rurun the Spacct file" # "if death occurs here, rerunacct should remove last ptacct file" cd ${_nite} for _Spacct in ${_adm}/Spacct*.${_date} do _ptacct=`basename ${_Spacct} | sed 's/Sp/pt/'` if test -s ${_ptacct}; then echo "WARNING: accounting already run for ${_Spacct}" \ >> ${_active} echo "WARNING: remove ${_nite}/${_ptacct} to rerun" \ >> ${_active} else nulladm ${_ptacct} acctprc < ${_Spacct} > ${_ptacct} echo "process acctg complete for ${_Spacct}" >> ${_active} fi done echo "all process actg complete for ${_date}" >> ${_active} echo "MERGE" > ${_statefile} ;; MERGE) cd ${_nite} # "merge ctacct and ptacct files together" acctmerg ptacct*.${_date} < ctacct.${_date} > daytacct echo "tacct merge to create daytacct complete" >> ${_active} echo "FEES" > ${_statefile} ;; FEES) cd ${_nite} # "merge in fees" if test -s ${_adm}/fee; then cp daytacct tmpdayt sort +0n +2 ${_adm}/fee | acctmerg -i | acctmerg tmpdayt > daytacct echo "merged fees" >> ${_active} rm -f tmpdayt else echo "no fees" >> ${_active} fi echo "DISK" > ${_statefile} ;; DISK) cd ${_nite} # "the last act of any disk acct procedure should be to mv its" # "entire output file to disktacct, where it will be picked up" if test -r disktacct; then cp daytacct tmpdayt acctmerg disktacct < tmpdayt > daytacct echo "merged disk records" >> ${_active} rm -f tmpdayt mv disktacct /tmp/disktacct.${_date} else echo "no disk records" >> ${_active} fi echo "MERGETACCT" > ${_statefile} ;; MERGETACCT) cd ${_adm}/acct # "save each days tacct file in sum/tacct.${_date}" # "if sum/tacct gets corrupted or lost, could recreate easily" # "the monthly acctg procedure should remove all sum/tacct files" cp nite/daytacct sum/tacct${_date} if test ! -r sum/tacct; then echo "WARNING: recreating ${_adm}/sum/tacct " >> ${_active} nulladm sum/tacct fi # "merge in todays tacct with the summary tacct" rm -f sum/tacctprev cp sum/tacct sum/tacctprev acctmerg sum/tacctprev < sum/tacct${_date} > sum/tacct echo "updated sum/tacct" >> ${_active} echo "CMS" > ${_statefile} ;; CMS) cd ${_adm}/acct # "do command summaries" nulladm sum/daycms if test ! -r sum/cms; then nulladm sum/cms echo "WARNING: recreating ${_adm}/sum/cms " >> ${_active} fi cp sum/cms sum/cmsprev acctcms ${_adm}/Spacct*.${_date} > sum/daycms acctcms -s sum/daycms sum/cmsprev > sum/cms acctcms -a -s sum/daycms | sed -n 1,56p > nite/daycms acctcms -a -s sum/cms | sed -n 1,56p > nite/cms lastlogin echo "command summaries complete" >> ${_active} echo "USEREXIT" > ${_statefile} ;; USEREXIT) # "any installation dependant accounting programs should be run here" [ -s /usr/lib/acct/runacct.local ] && /usr/lib/acct/runacct.local echo "CLEANUP" > ${_statefile} ;; CLEANUP) cd ${_adm}/acct # " finally clear files; could be done next morning if desired" nulladm ${_adm}/fee rm -f ${_adm}/Spacct*.${_date} # "put reports onto a file" prdaily >> sum/rprt${_date}; rm -f nite/lock* rm -f nite/ptacct*.${_date} nite/ctacct.${_date} mv -f nite/${_date}.wtmpx nite/owtmpx rm -f nite/wtmperror${_date} nite/active${_date} nite/tmpwtmp echo "system accounting completed at `date`" >> ${_active} echo "********** SYSTEM ACCOUNTING COMPLETED `date` **********" | logger -p daemon.notice echo "COMPLETE" > ${_statefile} exit 0 ;; *) (date;echo "${_errormsg}") | logger -p daemon.err echo "${_errormsg}" | mailx adm root echo "ERROR: invalid state, check ${_statefile}" >> active rm -f ${_nite}/lock* mv ${_active} ${_active}${_date} exit 1 ;; esac done # " runacct is normally called with no arguments from the cron" # " it checks its own locks to make sure that 2 crons or previous" # " problems have not occured" # " runacct uses the statefile to record its progress" # " each state updates the statefile upon completion" # " then the next loop though the while picks up the new state" # " to restart this shell, check the active file for diagnostics" # " fix up any corrupted data (ie. bad pacct or wtmpx files)" # " if runacct detected the error it removes the locks" # " remove the locks if necessary, otherwise runacct will complain" # " the lastdate file should be removed or changed" # " restart runacct at current state with: runacct MMDD" # " to override the statefile: runacct MMDD STATE" # " if runacct has been executed after the latest failure" # " ie. it ran ok today but failed yesterday" # " the statefile will not be correct" # " check the active files and restart properly" # " if runacct failed in the PROCESS state, remove the last" # " ptacct file because it may not be complete" # " if shell has failed several days, do SETUP manually" # " then rerun runacct once for each day failed" # " could use fwtmp here to split up wtmpx file correctly" # " normally not a good idea to restart the SETUP state" # " should be done manually, or just cleanup first" # " FILE USAGE: all files in /var/adm/acct/nite unless specified" # " statefile records progess of runacct" # " lastdate last day runacct ran in date +%m%d format" # " lock lock1 controls serial use of runacct" # " active place for all descriptive and error messages" # " fd2log fd2 output for runacct ( see cron entry ) " # " MMDD.wtmpx owtmpx yesterdays wtmpx file" # " tmpwtmp yesterdays wtmp corrected by wtmpfix" # " wtmperror place for wtmpfix error messages" # " lineuse lineusage report used in prdaily" # " reboots reboots report used in prdaily" # " log place for error messages from acctcon1" # " ctacct.MMDD connect tacct records for MMDD" # " ptacct.n.MMDD process tacct records n files for MMDD" # " daytacct total tacct records for this days accounting" # " disktacct disk tacct records produced by disk shell" # " daycms ascii daily command summary used by prdaily" # " cms acsii total command summary used by prdaily" # " following files in /var/adm directory" # " fee output from chargefee program" # " pacct active pacct file" # " pacctn switched pacct files" # " Spacctn.MMDD pacct files for MMDD after SETUP state" # " wtmpx active wtmpx file" # " following files in /var/adm/acct/sum" # " loginlog output of lastlogin used in prdaily" # " tacct total tacct file for current fiscal" # " tacct.MMDD tacct file for day MMDD" # " cms total cms file for current fiscal" # " rprt.MMDD output of prdaily program" # " MMDD.wtmpx saved copy of wtmpx for MMDD" # " pacct.MMDD concatenated version of all pacct files for MMDD" # " cmsprev total cms file without latest update" # " tacctprev total tacct file without latest update" # " daycms cms files for todays usage" #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # Copyright (c) 1999 by Sun Microsystems, Inc. # All rights reserved. #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6 */ # "shutacct [arg] - shuts down acct, called from /sbin/shutdown" # "whenever system taken down" # "arg added to /var/wtmpx to record reason, defaults to shutdown" PATH=/usr/lib/acct:/usr/bin:/usr/sbin _reason=${1-"acctg off"} acctwtmp "${_reason}" /var/adm/wtmpx turnacct off #!/sbin/sh # # 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # Copyright (c) 1999 by Sun Microsystems, Inc. # All rights reserved. #ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.7 */ # "startup (acct) - should be called from /etc/rc" # "whenever system is brought up" PATH=/usr/lib/acct:/usr/bin:/usr/sbin acctwtmp "acctg on" /var/adm/wtmpx turnacct switch # "clean up yesterdays accounting files" rm -f /var/adm/acct/sum/wtmp* rm -f /var/adm/acct/sum/pacct* rm -f /var/adm/acct/nite/lock* #!/sbin/sh # # 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 1991-2002 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved #ident "%Z%%M% %I% %E% SMI" # "control process accounting (must be root)" # "turnacct on makes sure it's on" # "turnacct off turns it off" # "turnacct switch switches pacct to pacct?, starts fresh one" # "/var/adm/pacct is always the current pacct file" PATH=/usr/lib/acct:/usr/bin:/usr/sbin cd /var/adm case "$1" in on) if test ! -r pacct then nulladm pacct fi accton pacct _rc=$? ;; off) accton _rc=$? ;; switch) pfexec /usr/sbin/logadm -p now /var/adm/pacct if test ! -r pacct then nulladm pacct accton pacct fi _rc=$? ;; *) echo "Usage: turnacct on|off|switch" >&2 _rc=1 ;; esac exit ${_rc} /* * 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * create entries for users who are still logged on when accounting * is being run. Look at utmpx, and update the time stamp. New info * goes to wtmpx. Called by runacct. */ #include #include #include #include #include #include #include int main(int argc, char **argv) { struct utmpx *utmpx; FILE *fp; fp = fopen(WTMPX_FILE, "a+"); if (fp == NULL) { fprintf(stderr, "%s: %s: %s\n", argv[0], WTMPX_FILE, strerror(errno)); exit(1); } while ((utmpx = getutxent()) != NULL) { if ((utmpx->ut_type == USER_PROCESS) && !(nonuserx(*utmpx))) { time(&utmpx->ut_xtime); fwrite(utmpx, sizeof (*utmpx), 1, fp); } } fclose(fp); exit(0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * wtmpfix - adjust wtmpx file and remove date changes. * wtmpfix wtmpx2 * * Can recover to some extent from wtmpx corruption. */ #include #include #include #include #include "acctdef.h" #include #include #include #include #include #include #include #define DAYEPOCH (60 * 60 * 24) #define UTRSZ (sizeof (struct futmpx)) /* file record size */ /* * The acctsh(8) shell scripts startup(8) and shutacct(8) as well as the * runacct script each pass their own specific reason strings in the first * argument to acctwtmp(8), to be propagated into ut_line fields. Additional * reasons (RUNLVL_MSG, ..., DOWN_MSG), used by compiled code, are defined in * as preprocessor constants. * For simplicity we predefine similar constants for the scripted strings * here, as no other compiled code uses those. * Moreover, we need a variant of RUNLVL_MSG without the "%c" at the end. * We shall use the fact that ut_line[RLVLMSG_LEN] will extract the char * in the %c position ('S', '2', ...). * Since all of these string constants are '\0' terminated, they can safely * be used with strcmp() even when ut_line is not. */ #define RUN_LEVEL_MSG "run-level " #define ACCTG_ON_MSG "acctg on" #define ACCTG_OFF_MSG "acctg off" #define RUNACCT_MSG "runacct" #define RLVLMSG_LEN (sizeof (RUN_LEVEL_MSG) - 1) /* * Records encountered are classified as one of the following: corrupted; * ok but devoid of interest to acctcon downstream; ok and interesting; * or ok and even redundant enough to latch onto a new alignment whilst * recovering from a corruption. * The ordering among these four symbolic values is significant. */ typedef enum { INRANGE_ERR = -1, INRANGE_DROP, INRANGE_PASS, INRANGE_ALIGNED } inrange_t; /* input filenames and record numbers, for diagnostics only */ #define STDIN_NAME "" static char *cur_input_name; static off_t recin; static FILE *Wtmpx, *Temp; struct dtab { off_t d_off1; /* file offset start */ off_t d_off2; /* file offset stop */ time_t d_adj; /* time adjustment */ struct dtab *d_ndp; /* next record */ }; static struct dtab *Fdp; /* list header */ static struct dtab *Ldp; /* list trailer */ static time_t lastmonth, nextmonth; static struct futmpx Ut, Ut2; static int winp(FILE *, struct futmpx *); static void mkdtab(off_t); static void setdtab(off_t, struct futmpx *, struct futmpx *); static void adjust(off_t, struct futmpx *); static int invalid(char *); static void scanfile(void); static inrange_t inrange(void); static void wcomplain(char *); int main(int argc, char **argv) { time_t tloc; struct tm *tmp; int year; int month; off_t rectmpin; (void) setlocale(LC_ALL, ""); setbuf(stdout, NULL); (void) time(&tloc); tmp = localtime(&tloc); year = tmp->tm_year; month = tmp->tm_mon + 1; lastmonth = ((year + 1900 - 1970) * 365 + (month - 1) * 30) * DAYEPOCH; nextmonth = ((year + 1900 - 1970) * 365 + (month + 1) * 30) * DAYEPOCH; if (argc < 2) { argv[argc] = "-"; argc++; } /* * Almost all system call failures in this program are unrecoverable * and therefore fatal. Typical causes might be lack of memory or * of space in a filesystem. If necessary, the system administrator * can invoke /usr/lib/acct/runacct interactively after making room * to complete the remaining phases of last night's accounting. */ if ((Temp = tmpfile()) == NULL) { perror("Cannot create temporary file"); return (EXIT_FAILURE); } while (--argc > 0) { argv++; if (strcmp(*argv, "-") == 0) { Wtmpx = stdin; cur_input_name = STDIN_NAME; } else if ((Wtmpx = fopen(*argv, "r")) == NULL) { (void) fprintf(stderr, "Cannot open %s: %s\n", *argv, strerror(errno)); return (EXIT_FAILURE); } else { cur_input_name = *argv; } /* * Filter records reading from current input stream Wtmpx, * writing to Temp. */ scanfile(); if (Wtmpx != stdin) (void) fclose(Wtmpx); } /* flush and rewind Temp for readback */ if (fflush(Temp) != 0) { perror(": fflush"); return (EXIT_FAILURE); } if (fseeko(Temp, (off_t)0L, SEEK_SET) != 0) { perror(": seek"); return (EXIT_FAILURE); } /* second pass: apply time adjustments */ rectmpin = 0; while (winp(Temp, &Ut)) { adjust(rectmpin, &Ut); rectmpin += UTRSZ; if (fwrite(&Ut, UTRSZ, 1, stdout) < 1) { perror(": fwrite"); return (EXIT_FAILURE); } } (void) fclose(Temp); /* * Detect if we've run out of space (say) and exit unsuccessfully * so that downstream accounting utilities won't start processing an * incomplete tmpwtmp file. */ if (fflush(stdout) != 0) { perror(": fflush"); return (EXIT_FAILURE); } return (EXIT_SUCCESS); } static int winp(FILE *f, struct futmpx *w) { if (fread(w, (size_t)UTRSZ, (size_t)1, f) != 1) return (0); if ((w->ut_type >= EMPTY) && (w->ut_type <= UTMAXTYPE)) return (1); else { (void) fprintf(stderr, "Bad temp file at offset %lld\n", (longlong_t)(ftell(f) - UTRSZ)); /* * If input was corrupt, neither ut_line nor ut_user can be * relied on to be \0-terminated. Even fixing the precision * does not entirely guard against this. */ (void) fprintf(stderr, "ut_line \"%-12.12s\" ut_user \"%-8.8s\" ut_xtime %ld\n", w->ut_line, w->ut_user, (long)w->ut_xtime); exit(EXIT_FAILURE); } /* NOTREACHED */ } static void mkdtab(off_t p) { struct dtab *dp; dp = Ldp; if (dp == NULL) { dp = calloc(sizeof (struct dtab), 1); if (dp == NULL) { (void) fprintf(stderr, "out of memory\n"); exit(EXIT_FAILURE); } Fdp = Ldp = dp; } dp->d_off1 = p; } static void setdtab(off_t p, struct futmpx *w1, struct futmpx *w2) { struct dtab *dp; if ((dp = Ldp) == NULL) { (void) fprintf(stderr, "no dtab\n"); exit(EXIT_FAILURE); } dp->d_off2 = p; dp->d_adj = w2->ut_xtime - w1->ut_xtime; if ((Ldp = calloc(sizeof (struct dtab), 1)) == NULL) { (void) fprintf(stderr, "out of memory\n"); exit(EXIT_FAILURE); } Ldp->d_off1 = dp->d_off1; dp->d_ndp = Ldp; } static void adjust(off_t p, struct futmpx *w) { off_t pp; struct dtab *dp; pp = p; for (dp = Fdp; dp != NULL; dp = dp->d_ndp) { if (dp->d_adj == 0) continue; if (pp >= dp->d_off1 && pp <= dp->d_off2) w->ut_xtime += dp->d_adj; } } /* * invalid() determines whether the name field adheres to the criteria * set forth in acctcon1. If returns VALID if the name is ok, or * INVALID if the name violates conventions. */ static int invalid(char *name) { int i; for (i = 0; i < NSZ; i++) { if (name[i] == '\0') return (VALID); if (! (isalnum(name[i]) || (name[i] == '$') || (name[i] == ' ') || (name[i] == '.') || (name[i] == '_') || (name[i] == '-'))) { return (INVALID); } } return (VALID); } /* * scanfile: * 1) reads the current input file * 2) filters for process records in time range of interest and for * other types of records deemed interesting to acctcon downstream * 3) picks up time changes with setdtab() if in multiuser mode, which * will be applied when the temp file is read back * 4) changes bad login names to INVALID * 5) recovers from common cases of wtmpx corruption (loss of record * alignment). * All of the static globals are used directly or indirectly. * * When wtmpfix is asked to process several input files in succession, * some state needs to be preserved from one scanfile() invocation to the * next. Aside from the temp file position, we remember whether we were * in multi-user mode or not. Absent evidence to the contrary, we begin * processing assuming multi-user mode, because runacct's wtmpx rotation * normally gives us a file recently initialized by utmp2wtmp(8) with no * older RUN_LVL records surviving. */ static void scanfile() { struct stat Wtstat; off_t residue = 0; /* input file size mod UTRSZ */ /* * lastok will be the offset of the beginning of the most recent * manifestly plausible and interesting input record in the current * input file, if any. * An invariant at loop entry is -UTRSZ <= lastok <= recin - UTRSZ. */ off_t lastok = -(off_t)UTRSZ; static off_t rectmp; /* current temp file position */ static boolean_t multimode = B_TRUE; /* multi-user RUN_LVL in force */ inrange_t is_ok; /* caches inrange() result */ /* * During normal operation, records are of interest and copied to * the output when is_ok >= INRANGE_PASS, ignored and dropped when * is_ok == INRANGE_DROP, and evidence of corruption otherwise. * While we are trying to recover from a corruption and hunting for * records with sufficient redundancy to confirm that we have reached * proper alignment again, we'll want is_ok >= INRANGE_ALIGNED. * The value of want_ok is the minimum inrange() result of current * interest. It is raised to INRANGE_ALIGNED during ongoing recovery * and dropped back to INRANGE_PASS when we have recovered alignment. */ inrange_t want_ok = INRANGE_PASS; boolean_t recovered = B_FALSE; /* true after a successful recovery */ int n; if (fstat(fileno(Wtmpx), &Wtstat) == -1) { (void) fprintf(stderr, "Cannot stat %s (will read sequentially): %s\n", cur_input_name, strerror(errno)); } else if ((Wtstat.st_mode & S_IFMT) == S_IFREG) { residue = Wtstat.st_size % UTRSZ; } /* if residue != 0, part of the file may be misaligned */ for (recin = 0; ((n = fread(&Ut, (size_t)UTRSZ, (size_t)1, Wtmpx)) > 0) || (residue > 0); recin += UTRSZ) { if (n == 0) { /* * Implying residue > 0 and want_ok == INRANGE_PASS. * It isn't worth telling an I/O error from EOF here. * But one case is worth catching to avoid issuing a * confusing message below. When the previous record * had been ok, we just drop the current truncated * record and bail out of the loop -- no seeking back. */ if (lastok == recin - UTRSZ) { wcomplain("file ends in mid-record, " "final partial record dropped"); break; } else { wcomplain("file ends in mid-record"); /* handled below like a corrupted record */ is_ok = INRANGE_ERR; } } else is_ok = inrange(); /* alignment recovery logic */ if ((residue > 0) && (is_ok == INRANGE_ERR)) { /* * "Let's go back to the last place where we knew * where we were..." * In fact, if the last record had been fine and we * know there's at least one whole record ahead, we * might move forward here (by residue bytes, less * than one record's worth). In any case, we align * ourselves to an integral number of records before * the end of the file. */ wcomplain("suspecting misaligned records, " "repositioning"); recin = lastok + UTRSZ + residue; residue = 0; if (fseeko(Wtmpx, recin, SEEK_SET) != 0) { (void) fprintf(stderr, "%s: seek: %s\n", cur_input_name, strerror(errno)); exit(EXIT_FAILURE); } wcomplain("starting re-scan"); /* * While want_ok is elevated, only unequivocal records * with inrange() == INRANGE_ALIGNED will be admitted * to latch onto the tentative new alignment. */ want_ok = INRANGE_ALIGNED; /* * Compensate for the loop continuation. Doing * it this way gets the correct offset reported * in the re-scan message above. */ recin -= UTRSZ; continue; } /* assert: residue == 0 or is_ok >= INRANGE_DROP here */ if (is_ok < want_ok) /* record of no further interest */ continue; if (want_ok == INRANGE_ALIGNED) { wcomplain("now recognizing aligned records again"); want_ok = INRANGE_PASS; recovered = B_TRUE; } /* * lastok must track recin whenever the current record is * being processed and written out to our temp file, to avoid * reprocessing any bits already done when we readjust our * alignment. */ lastok = recin; /* now we have a good wtmpx record, do more processing */ if (rectmp == 0 || Ut.ut_type == BOOT_TIME) mkdtab(rectmp); if (Ut.ut_type == RUN_LVL) { /* inrange() already checked the "run-level " part */ if (Ut.ut_line[RLVLMSG_LEN] == 'S') multimode = B_FALSE; else if ((Ut.ut_line[RLVLMSG_LEN] == '2') || (Ut.ut_line[RLVLMSG_LEN] == '3') || (Ut.ut_line[RLVLMSG_LEN] == '4')) multimode = B_TRUE; } if (invalid(Ut.ut_name) == INVALID) { (void) fprintf(stderr, "wtmpfix: logname \"%*.*s\" changed " "to \"INVALID\"\n", OUTPUT_NSZ, OUTPUT_NSZ, Ut.ut_name); (void) strncpy(Ut.ut_name, "INVALID", NSZ); } /* * Special case: OLD_TIME should be immediately followed by * NEW_TIME. * We make no attempt at alignment recovery between these * two: if there's junk at this point in the input, then * a NEW_TIME seen after the junk probably won't be the one * we are looking for. */ if (Ut.ut_type == OLD_TIME) { /* * Make recin refer to the expected NEW_TIME. * Loop continuation will increment it again * for the record we're about to read now. */ recin += UTRSZ; if (!fread(&Ut2, (size_t)UTRSZ, (size_t)1, Wtmpx)) { wcomplain("input truncated after OLD_TIME - " "giving up"); exit(EXIT_FAILURE); } /* * Rudimentary NEW_TIME sanity check. Not as thorough * as in inrange(), but then we have redundancy from * context here, since we're just after a plausible * OLD_TIME record. */ if ((Ut2.ut_type != NEW_TIME) || (strcmp(Ut2.ut_line, NTIME_MSG) != 0)) { wcomplain("NEW_TIME expected but missing " "after OLD_TIME - giving up"); exit(EXIT_FAILURE); } lastok = recin; if (multimode == B_TRUE) setdtab(rectmp, &Ut, &Ut2); rectmp += 2 * UTRSZ; if ((fwrite(&Ut, UTRSZ, 1, Temp) < 1) || (fwrite(&Ut2, UTRSZ, 1, Temp) < 1)) { perror(": fwrite"); exit(EXIT_FAILURE); } continue; } if (fwrite(&Ut, UTRSZ, 1, Temp) < 1) { perror(": fwrite"); exit(EXIT_FAILURE); } rectmp += UTRSZ; } if (want_ok == INRANGE_ALIGNED) { wcomplain("EOF reached without recognizing another aligned " "record with certainty. This file may need to be " "repaired by hand.\n"); } else if (recovered == B_TRUE) { /* * There may have been a number of wcomplain() messages * since we reported about the re-scan, so it bears repeating * at the end that not all was well. */ wcomplain("EOF reached after recovering from corruption " "in the middle of the file. This file may need to be " "repaired by hand.\n"); } } /* * inrange: inspect what we hope to be one wtmpx record. * Globals: Ut, lastmonth, nextmonth; recin, cur_input_name (diagnostics) * Return values: * INRANGE_ERR -- an inconsistency was detected, input file corrupted * INRANGE_DROP -- Ut appears consistent but isn't of interest * (of process type and outside the time range we want) * INRANGE_PASS -- Ut appears consistent and this record is of interest * INRANGE_ALIGNED -- same, and it is also redundant enough to be sure * that we're correctly aligned on record boundaries */ #define UNEXPECTED_UT_PID \ (Ut.ut_pid != 0) || \ (Ut.ut_exit.e_termination != 0) || \ (Ut.ut_exit.e_exit != 0) static inrange_t inrange() { /* pid_t is signed so that fork() can return -1. Exploit this. */ if (Ut.ut_pid < 0) { wcomplain("negative pid"); return (INRANGE_ERR); } /* the legal values for ut_type are enumerated in */ switch (Ut.ut_type) { case EMPTY: if (UNEXPECTED_UT_PID) { wcomplain("nonzero pid or status in EMPTY record"); return (INRANGE_ERR); } /* * We'd like to have Ut.ut_user[0] == '\0' here, but sadly * this isn't always so, so we can't rely on it. */ return (INRANGE_DROP); case RUN_LVL: /* ut_line must have come from the RUNLVL_MSG pattern */ if (strncmp(Ut.ut_line, RUN_LEVEL_MSG, RLVLMSG_LEN) != 0) { wcomplain("RUN_LVL record doesn't say `" RUN_LEVEL_MSG "'"); return (INRANGE_ERR); } /* * The ut_pid, termination, and exit status fields have * special meaning in this case, and none of them is * suitable for checking. And we won't insist on ut_user * to always be an empty string. */ return (INRANGE_ALIGNED); case BOOT_TIME: if (UNEXPECTED_UT_PID) { wcomplain("nonzero pid or status in BOOT_TIME record"); return (INRANGE_ERR); } if (strcmp(Ut.ut_line, BOOT_MSG) != 0) { wcomplain("BOOT_TIME record doesn't say `" BOOT_MSG "'"); return (INRANGE_ERR); } return (INRANGE_ALIGNED); case OLD_TIME: if (UNEXPECTED_UT_PID) { wcomplain("nonzero pid or status in OLD_TIME record"); return (INRANGE_ERR); } if (strcmp(Ut.ut_line, OTIME_MSG) != 0) { wcomplain("OLD_TIME record doesn't say `" OTIME_MSG "'"); return (INRANGE_ERR); } return (INRANGE_ALIGNED); case NEW_TIME: /* * We don't actually expect to see any here. If they follow * an OLD_TIME record as they should, they'll be handled on * the fly in scanfile(). But we might still run into one * if the input is somehow corrupted. */ if (UNEXPECTED_UT_PID) { wcomplain("nonzero pid or status in NEW_TIME record"); return (INRANGE_ERR); } if (strcmp(Ut.ut_line, NTIME_MSG) != 0) { wcomplain("NEW_TIME record doesn't say `" NTIME_MSG "'"); return (INRANGE_ERR); } return (INRANGE_ALIGNED); /* the four *_PROCESS ut_types have a lot in common */ case USER_PROCESS: /* * Catch two special cases first: psradm records have no id * and no pid, while root login over FTP may not have a * valid ut_user and may have garbage in ut_id[3]. */ if ((strcmp(Ut.ut_user, "psradm") == 0) && (Ut.ut_id[0] == '\0') && (Ut.ut_pid > 0)) { if ((Ut.ut_xtime > lastmonth) && (Ut.ut_xtime < nextmonth)) { return (INRANGE_ALIGNED); } else { return (INRANGE_DROP); } } if ((Ut.ut_user[0] == '\0') && (strncmp(Ut.ut_id, "ftp", 3) == 0) && (strncmp(Ut.ut_line, "ftp", 3) == 0)) { if ((Ut.ut_xtime > lastmonth) && (Ut.ut_xtime < nextmonth)) { return (INRANGE_ALIGNED); } else { return (INRANGE_DROP); } } /* FALLTHROUGH */ case LOGIN_PROCESS: if (Ut.ut_user[0] == '\0') { wcomplain("missing username in process record"); return (INRANGE_ERR); } /* FALLTHROUGH */ case INIT_PROCESS: /* * INIT_PROCESS and DEAD_PROCESS records can come with an * empty ut_user in degenerate cases (e.g. syntax errors * like a comment-only process field in /etc/inittab). * But in an INIT_PROCESS, LOGIN_PROCESS, or USER_PROCESS * record, we expect a respectable ut_pid. */ if (Ut.ut_pid == 0) { wcomplain("null pid in process record"); return (INRANGE_ERR); } /* FALLTHROUGH */ case DEAD_PROCESS: /* * DEAD_PROCESS records with a null ut_pid can be produced * by gnome-terminal (normally seen in utmpx only, but they * can leak into wtmpx in rare circumstances). * Unfortunately, ut_id can't be relied on to contain * anything in particular. (E.g., sshd might leave it * 0-initialized.) This leaves almost no verifiable * redundancy here beyond the ut_type. * At least we insist on a reasonable timestamp. */ if (Ut.ut_xtime <= 0) { wcomplain("non-positive time in process record"); return (INRANGE_ERR); } if ((Ut.ut_xtime > lastmonth) && (Ut.ut_xtime < nextmonth)) { return (INRANGE_PASS); } else { return (INRANGE_DROP); } case ACCOUNTING: /* * If we recognize one of the three reason strings passed * by the /usr/lib/acct shell scripts to acctwtmp, we * exploit the available redundancy they offer. But * acctwtmp could have been invoked by custom scripts or * interactively with other reason strings in the first * argument, so anything we don't recognize does not * constitute evidence for corruption. */ if ((strcmp(Ut.ut_line, RUNACCT_MSG) != 0) && (strcmp(Ut.ut_line, ACCTG_ON_MSG) != 0) && (strcmp(Ut.ut_line, ACCTG_OFF_MSG) != 0)) { return (INRANGE_DROP); } return (INRANGE_ALIGNED); case DOWN_TIME: if (UNEXPECTED_UT_PID) { wcomplain("nonzero pid or status in DOWN_TIME record"); return (INRANGE_ERR); } if (strcmp(Ut.ut_line, DOWN_MSG) != 0) { wcomplain("DOWN_TIME record doesn't say `" DOWN_MSG "'"); return (INRANGE_ERR); } return (INRANGE_ALIGNED); default: wcomplain("ut_type out of range"); return (INRANGE_ERR); } /* NOTREACHED */ } static void wcomplain(char *msg) { (void) fprintf(stderr, "%s: offset %lld: %s\n", cur_input_name, (longlong_t)recin, msg); }