2 * acctdump.c - accounting data dump utility
4 * Copyright (C) 1998 Ian Jackson <ian@chiark.greenend.org.uk>
6 * This is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2,
9 * or (at your option) any later version.
11 * This is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this file; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 typedef unsigned long long u64;
36 /*#include <sys/acct.h>*/
37 typedef struct acct_v3 struct_acct;
41 static int forwards, nobanner, usestdin, raw, usages;
43 static int de_used, de_allocd;
44 static struct deventry {
49 static const struct cmdinfo cmdinfos[]= {
50 { "--forwards", 'f', 0, &forwards, 0, 0, 1, 0, 0 },
51 { "--no-banner", 'q', 0, &nobanner, 0, 0, 1, 0, 0 },
52 { "--stdin", 'p', 0, &usestdin, 0, 0, 1, 0, 0 },
53 { "--raw", 'r', 0, &raw, 0, 0, 1, 0, 0 },
54 { "--resource", 'u', 0, &usages, 0, 0, 1, 0, 0 },
58 static const char *sigabbrev[]= {
59 "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS", "FPE",
60 "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT",
61 "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG", "XCPU",
62 "XFSZ", "VTALRM", "PROF", "WINCH", "IO"
65 static void usage(FILE *file) {
66 fputs("usage: acctdump [<options>] [<file> ...]\n"
67 "options: -f|--forwards -q|--no-banner -p|--stdin -r|--raw -u|--resource\n",
69 if (ferror(file)) { perror("print usage"); exit(8); }
72 void badusage(const char *fmt, ...) {
75 fputs("usage error: ",stderr);
77 vfprintf(stderr,fmt,al);
84 static void checkstdout(void) {
85 if (ferror(stdout)) { perror("stdout"); exit(8); }
88 static void scandev(const char *basename, int levelsleft) {
89 /* We deliberately ignore most errors */
93 int fnbufalloc, fnbufreq, r, basel, nallocd;
95 struct deventry *ndeventries;
97 if (levelsleft==0) return;
99 dir= opendir(basename); if (!dir) return;
102 basel= strlen(basename);
104 while ((de= readdir(dir))) {
105 fnbufreq= basel+1+strlen(de->d_name)+1;
106 if (fnbufalloc<fnbufreq) {
107 fnbufalloc= fnbufreq+10;
108 nfnbuf= realloc(fnbuf,fnbufalloc);
109 if (!nfnbuf) { free(fnbuf); fnbufalloc=0; continue; }
112 sprintf(fnbuf,"%s/%s",basename,de->d_name);
113 r= lstat(fnbuf,&stab);
114 if (S_ISCHR(stab.st_mode)) {
115 if (de_used >= de_allocd) {
116 nallocd= (de_allocd+10)<<1;
117 ndeventries= realloc(deventries,nallocd*sizeof(*deventries));
118 if (!ndeventries) continue;
120 deventries= ndeventries;
122 deventries[de_used].fn= strdup(fnbuf+5); /* remove /dev */
123 if (!deventries[de_used].fn) continue;
124 deventries[de_used].dev= stab.st_rdev;
126 } else if (S_ISDIR(stab.st_mode) && de->d_name[0] != '.') {
127 scandev(fnbuf,levelsleft-1);
134 static int walkdev_cptr(const void *av, const void *bv) {
135 const struct deventry *a= av;
136 const struct deventry *b= bv;
137 return a->dev - b->dev;
140 static void printbanner(void) {
142 fputs("begin date command "
143 "uid gid tty dev FSDX exit",
146 fputs("begin date and time command "
147 "user group tty dev FSDX sigexit",
151 fputs(" user time sys time elap time minflt maxflt",
158 static void printrecord(const struct_acct *as, const char *filename) {
159 static int walkeddev;
165 struct deventry *deve, devlookfor;
171 printf("%10lu ",(unsigned long)as->ac_btime);
174 tm= localtime(&btime);
175 strftime(buf,sizeof(buf),"%Y-%m-%d %H:%M:%S",tm); buf[sizeof(buf)-1]= 0;
179 printf("%-16.16s ", as->ac_comm);
181 pw= raw ? 0 : getpwuid(as->ac_uid);
182 if (pw) printf("%-8s ",pw->pw_name);
183 else printf("%-8ld ",(long)as->ac_uid);
185 gr= raw ? 0 : getgrgid(as->ac_gid);
186 if (gr) printf("%-8s ",gr->gr_name);
187 else printf("%-8ld ",(long)as->ac_gid);
190 if (!(as->ac_tty + 1) /* check for -1 without knowing type */) {
193 printf("%08lx ",(unsigned long)as->ac_tty);
196 if (!(as->ac_tty + 1)) {
201 qsort(deventries,de_used,sizeof(*deventries),walkdev_cptr);
205 devlookfor.dev= as->ac_tty;
206 deve= bsearch(&devlookfor,deventries,de_used,sizeof(*deventries),walkdev_cptr);
208 printf("%-10s ",deve->fn);
210 printf("%08lx ",(unsigned long)as->ac_tty);
216 for (i=1, fp= "FS4DX"; *fp; fp++, i<<=1) {
220 } else if (!isdigit(*fp)) {
229 dc= WCOREDUMP(as->ac_exitcode) ? 'd' : 'k';
231 if (WIFEXITED(as->ac_exitcode)) {
232 printf(" %3d",WEXITSTATUS(as->ac_exitcode));
233 } else if (WIFSIGNALED(as->ac_exitcode)) {
236 WTERMSIG(as->ac_exitcode));
238 printf("%04lx",(unsigned long)as->ac_exitcode);
241 if (WIFEXITED(as->ac_exitcode)) {
242 printf(" %6d",WEXITSTATUS(as->ac_exitcode));
243 } else if (WIFSIGNALED(as->ac_exitcode)) {
244 r= WTERMSIG(as->ac_exitcode);
245 if (r>0 && r<=sizeof(sigabbrev)/sizeof(*sigabbrev)) {
255 printf("#%04lx",(unsigned long)as->ac_exitcode);
260 printf(" %10lu %10lu %10lu %8ld %8ld",
261 (unsigned long)as->ac_utime,
262 (unsigned long)as->ac_stime,
263 (unsigned long)as->ac_etime,
264 (unsigned long)as->ac_minflt,
265 (unsigned long)as->ac_majflt);
272 static void processfile(FILE *file, const char *filename) {
277 while ((r= fread(&as,1,sizeof(as),file)) == sizeof(as)) {
278 printrecord(&as,filename);
281 long seekdist= -(long)sizeof(as);
282 r= fseek(file,0,SEEK_END); if (r) { perror(filename); exit(8); }
284 r= fseek(file,seekdist,SEEK_CUR);
286 if (errno=EINVAL) break;
287 perror(filename); exit(8);
289 r= fread(&as,1,sizeof(as),file);
290 if (r!=sizeof(as)) { perror(filename); exit(8); }
291 printrecord(&as,filename);
292 seekdist= -2*(long)sizeof(as);
295 if (ferror(file) || fclose(file)) { perror(filename); exit(8); }
298 static void processnamedfile(const char *filename) {
301 file= fopen(filename,"rb"); if (!file) { perror(filename); exit(8); }
302 processfile(file,filename);
305 int main(int argc, const char *const *argv) {
306 union { struct_acct ac; char c[1]; } xu;
307 myopt(&argv,cmdinfos);
308 if (!nobanner) printbanner();
309 fprintf(stdout,"s=%d %d\n",(int)sizeof(struct_acct),
310 (int)((char*)&xu.ac.ac_comm - xu.c));
312 processfile(stdin,"<standard input>");
314 processnamedfile("/var/account/pacct");
317 processnamedfile(*argv);
322 if (fflush(stdout)) { perror("flush stdout"); exit(8); }