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;
37 /* Sadly this thing is not very portable */
39 #if defined(__linux__)
41 #include <sys/types.h>
44 typedef struct acct_v3 struct_acct;
45 #define HAVE_AC_EXITCODE
47 #define FIELD_AC_FLAG(as) ((as)->ac_flag)
49 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
51 #include <sys/param.h>
52 #include <sys/types.h>
55 typedef struct acctv2 struct_acct;
56 #define HAVE_AC_IO_MEM
57 #define FIELD_AC_FLAG(as) ((as)->ac_flagx & ~ANVER)
61 #error Do not know what struct_acct to use on this platform
68 static int forwards, nobanner, usestdin, raw, usages;
70 static int de_used, de_allocd;
71 static struct deventry {
76 static const struct cmdinfo cmdinfos[]= {
77 { "--forwards", 'f', 0, &forwards, 0, 0, 1, 0, 0 },
78 { "--no-banner", 'q', 0, &nobanner, 0, 0, 1, 0, 0 },
79 { "--stdin", 'p', 0, &usestdin, 0, 0, 1, 0, 0 },
80 { "--raw", 'r', 0, &raw, 0, 0, 1, 0, 0 },
81 { "--resource", 'u', 0, &usages, 0, 0, 1, 0, 0 },
85 #ifdef HAVE_AC_EXITCODE
86 static const char *sigabbrev[]= {
87 "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS", "FPE",
88 "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT",
89 "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG", "XCPU",
90 "XFSZ", "VTALRM", "PROF", "WINCH", "IO"
94 void usagemessage(void) {
95 fputs("usage: acctdump [<options>] [<file> ...]\n"
96 "options: -f|--forwards -q|--no-banner -p|--stdin -r|--raw -u|--resource\n",
98 if (ferror(stderr)) { perror("print usage"); exit(8); }
101 static void checkstdout(void) {
102 if (ferror(stdout)) { perror("stdout"); exit(8); }
105 static void scandev(const char *basename, int levelsleft) {
106 /* We deliberately ignore most errors */
110 int fnbufalloc, fnbufreq, r, basel, nallocd;
111 char *fnbuf, *nfnbuf;
112 struct deventry *ndeventries;
114 if (levelsleft==0) return;
116 dir= opendir(basename);
118 fprintf(stderr, "%s: opendir: %s\n", basename, strerror(errno));
123 basel= strlen(basename);
125 while ((errno=0, de= readdir(dir))) {
126 fnbufreq= basel+1+strlen(de->d_name)+1;
127 if (fnbufalloc<fnbufreq) {
128 fnbufalloc= fnbufreq+10;
129 nfnbuf= realloc(fnbuf,fnbufalloc);
130 if (!nfnbuf) { free(fnbuf); fnbufalloc=0; continue; }
133 sprintf(fnbuf,"%s/%s",basename,de->d_name);
134 r= lstat(fnbuf,&stab);
136 fprintf(stderr, "%s: %s\n", fnbuf, strerror(errno));
139 if (S_ISCHR(stab.st_mode)) {
140 if (de_used >= de_allocd) {
141 nallocd= (de_allocd+10)<<1;
142 ndeventries= realloc(deventries,nallocd*sizeof(*deventries));
143 if (!ndeventries) continue;
145 deventries= ndeventries;
147 deventries[de_used].fn= strdup(fnbuf+5); /* remove /dev */
148 if (!deventries[de_used].fn) continue;
149 deventries[de_used].dev= stab.st_rdev;
151 } else if (S_ISDIR(stab.st_mode) && de->d_name[0] != '.') {
152 scandev(fnbuf,levelsleft-1);
156 fprintf(stderr, "%s: readdir: %s\n", basename, strerror(errno));
161 static int walkdev_cptr(const void *av, const void *bv) {
162 const struct deventry *a= av;
163 const struct deventry *b= bv;
164 return a->dev - b->dev;
167 static void printbanner(void) {
169 fputs("begin date command "
170 "uid gid tty dev FSDX "
171 #ifdef HAVE_AC_EXITCODE
176 fputs("begin date and time command "
177 "user group tty dev FSDX "
178 #ifdef HAVE_AC_EXITCODE
184 fputs(" user time sys time elap time "
188 #ifdef HAVE_AC_IO_MEM
197 static void printrecord(const struct_acct *as, const char *filename) {
198 static int walkeddev;
204 struct deventry *deve, devlookfor;
208 char commbuf[sizeof(as->ac_comm)];
211 printf("%10lu ",(unsigned long)as->ac_btime);
214 tm= localtime(&btime);
216 strftime(buf,sizeof(buf),"%Y-%m-%d %H:%M:%S",tm); buf[sizeof(buf)-1]= 0;
218 snprintf(buf,sizeof(buf),"@%lu",(unsigned long)btime);
223 for (i=0; i<sizeof(as->ac_comm); i++) {
224 int c=as->ac_comm[i];
225 commbuf[i]= ((c!=0 && c<=32) || c>=127) ? '?' : c;
227 printf("%-*.*s ", (int)sizeof(commbuf),(int)sizeof(commbuf), commbuf);
229 pw= raw ? 0 : getpwuid(as->ac_uid);
230 if (pw) printf("%-8s ",pw->pw_name);
231 else printf("%-8ld ",(long)as->ac_uid);
233 gr= raw ? 0 : getgrgid(as->ac_gid);
234 if (gr) printf("%-8s ",gr->gr_name);
235 else printf("%-8ld ",(long)as->ac_gid);
238 if (!(as->ac_tty + 1) /* check for -1 without knowing type */) {
241 printf("%08lx ",(unsigned long)as->ac_tty);
244 if (!(as->ac_tty + 1)) {
249 qsort(deventries,de_used,sizeof(*deventries),walkdev_cptr);
253 devlookfor.dev= as->ac_tty;
254 deve= bsearch(&devlookfor,deventries,de_used,sizeof(*deventries),walkdev_cptr);
256 printf("%-10s ",deve->fn);
258 printf("%08lx ",(unsigned long)as->ac_tty);
263 r= FIELD_AC_FLAG(as);
264 for (i=1, fp= "FS4DX"; *fp; fp++, i<<=1) {
268 } else if (!isdigit(*fp)) {
277 #ifdef HAVE_AC_EXITCODE
279 dc= WCOREDUMP(as->ac_exitcode) ? 'd' : 'k';
281 if (WIFEXITED(as->ac_exitcode)) {
282 printf(" %3d",WEXITSTATUS(as->ac_exitcode));
283 } else if (WIFSIGNALED(as->ac_exitcode)) {
286 WTERMSIG(as->ac_exitcode));
288 printf("%04lx",(unsigned long)as->ac_exitcode);
291 if (WIFEXITED(as->ac_exitcode)) {
292 printf(" %6d",WEXITSTATUS(as->ac_exitcode));
293 } else if (WIFSIGNALED(as->ac_exitcode)) {
294 r= WTERMSIG(as->ac_exitcode);
295 if (r>0 && r<=sizeof(sigabbrev)/sizeof(*sigabbrev)) {
305 printf("#%04lx",(unsigned long)as->ac_exitcode);
308 #endif /*HAVE_AC_EXITCODE*/
311 printf(" %10lu %10lu %10lu",
312 (unsigned long)as->ac_utime,
313 (unsigned long)as->ac_stime,
314 (unsigned long)as->ac_etime);
317 (unsigned long)as->ac_minflt,
318 (unsigned long)as->ac_majflt);
320 #ifdef HAVE_AC_IO_MEM
331 static void processfile(FILE *file, const char *filename) {
337 while ((r= fread(&as,1,sizeof(as),file)) == sizeof(as)) {
338 printrecord(&as,filename);
341 r= fseek(file,0,SEEK_END); if (r) { perror(filename); exit(8); }
342 pos= ftell(file); if (pos==-1) { perror(filename); exit(8); }
343 if (pos % sizeof(as)) {
344 fprintf(stderr, "%s: File size is not an integral number "
345 "of accounting records\n", filename);
349 if (pos<sizeof(as)) break;
351 r= fseek(file,pos,SEEK_SET); if (r==-1) { perror(filename); exit(8); }
352 r= fread(&as,1,sizeof(as),file); if (r!=sizeof(as)) { perror(filename); exit(8); }
353 printrecord(&as,filename);
356 if (ferror(file) || fclose(file)) { perror(filename); exit(8); }
359 static void processnamedfile(const char *filename) {
362 file= fopen(filename,"rb"); if (!file) { perror(filename); exit(8); }
363 processfile(file,filename);
366 int main(int argc, const char *const *argv) {
367 myopt(&argv,cmdinfos);
368 if (!nobanner) printbanner();
370 processfile(stdin,"<standard input>");
372 processnamedfile("/var/log/account/pacct");
375 processnamedfile(*argv);
380 if (fflush(stdout)) { perror("flush stdout"); exit(8); }