chiark / gitweb /
8745f0810e55cbacec8d4299ab54be710693a2ff
[chiark-utils.git] / cprogs / acctdump.c
1 /*
2  * acctdump.c - accounting data dump utility
3  *
4  * Copyright (C) 1998 Ian Jackson <ian@chiark.greenend.org.uk>
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <time.h>
24 #include <stdarg.h>
25 #include <wait.h>
26 #include <string.h>
27 #include <pwd.h>
28 #include <grp.h>
29 #include <dirent.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <sys/stat.h>
33
34 typedef unsigned long long u64;
35 #include <sys/acct.h>
36 typedef struct acct_v3 struct_acct;
37
38 #include "myopt.h"
39
40 static int forwards, nobanner, usestdin, raw, usages;
41
42 static int de_used, de_allocd;
43 static struct deventry {
44   const char *fn;
45   dev_t dev;
46 } *deventries;
47
48 static const struct cmdinfo cmdinfos[]= {
49   { "--forwards",  'f',  0, &forwards, 0, 0, 1, 0, 0 },
50   { "--no-banner", 'q',  0, &nobanner, 0, 0, 1, 0, 0 },
51   { "--stdin",     'p',  0, &usestdin, 0, 0, 1, 0, 0 },
52   { "--raw",       'r',  0, &raw,      0, 0, 1, 0, 0 },
53   { "--resource",  'u',  0, &usages,   0, 0, 1, 0, 0 },
54   {  0                                                 }
55 };
56
57 static const char *sigabbrev[]= {
58   "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS", "FPE",
59   "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT",
60   "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG", "XCPU",
61   "XFSZ", "VTALRM", "PROF", "WINCH", "IO"
62 };
63
64 void usagemessage(void) {
65   fputs("usage: acctdump [<options>] [<file> ...]\n"
66         "options: -f|--forwards -q|--no-banner -p|--stdin -r|--raw -u|--resource\n",
67         stderr);
68   if (ferror(stderr)) { perror("print usage"); exit(8); }
69 }
70
71 static void checkstdout(void) {
72   if (ferror(stdout)) { perror("stdout"); exit(8); }
73 }
74
75 static void scandev(const char *basename, int levelsleft) {
76   /* We deliberately ignore most errors */
77   DIR *dir;
78   struct dirent *de;
79   struct stat stab;
80   int fnbufalloc, fnbufreq, r, basel, nallocd;
81   char *fnbuf, *nfnbuf;
82   struct deventry *ndeventries;
83   
84   if (levelsleft==0) return;
85
86   dir= opendir(basename); 
87   if (!dir) {
88     fprintf(stderr, "%s: opendir: %s\n", basename, strerror(errno));
89     return;
90   }
91   fnbufalloc= 0;
92   fnbuf= 0;
93   basel= strlen(basename);
94
95   while ((errno=0, de= readdir(dir))) {
96     fnbufreq= basel+1+strlen(de->d_name)+1;
97     if (fnbufalloc<fnbufreq) {
98       fnbufalloc= fnbufreq+10;
99       nfnbuf= realloc(fnbuf,fnbufalloc);
100       if (!nfnbuf) { free(fnbuf); fnbufalloc=0; continue; }
101       fnbuf= nfnbuf;
102     }
103     sprintf(fnbuf,"%s/%s",basename,de->d_name);
104     r= lstat(fnbuf,&stab);
105     if (r) {
106       fprintf(stderr, "%s: %s\n", fnbuf, strerror(errno));
107       continue;
108     }
109     if (S_ISCHR(stab.st_mode)) {
110       if (de_used >= de_allocd) {
111         nallocd= (de_allocd+10)<<1;
112         ndeventries= realloc(deventries,nallocd*sizeof(*deventries));
113         if (!ndeventries) continue;
114         de_allocd= nallocd;
115         deventries= ndeventries;
116       }
117       deventries[de_used].fn= strdup(fnbuf+5); /* remove /dev */
118       if (!deventries[de_used].fn) continue;
119       deventries[de_used].dev= stab.st_rdev;
120       de_used++;
121     } else if (S_ISDIR(stab.st_mode) && de->d_name[0] != '.') {
122       scandev(fnbuf,levelsleft-1);
123     }
124   }
125   if (errno)
126       fprintf(stderr, "%s: readdir: %s\n", basename, strerror(errno));
127   closedir(dir);
128   free(fnbuf);
129 }
130
131 static int walkdev_cptr(const void *av, const void *bv) {
132   const struct deventry *a= av;
133   const struct deventry *b= bv;
134   return a->dev - b->dev;
135 }
136   
137 static void printbanner(void) {
138   if (raw) {
139     fputs("begin date command          "
140           "uid      gid      tty dev  FSDX exit",
141           stdout);
142   } else {
143     fputs("begin date and time command          "
144           "user     group    tty dev    FSDX sigexit",
145           stdout);
146   }
147   if (usages) {
148     fputs("  user time   sys time  elap time   minflt   maxflt",
149           stdout);
150   }
151   putchar('\n');
152   checkstdout();
153 }
154
155 static void printrecord(const struct_acct *as, const char *filename) {
156   static int walkeddev;
157
158   int i, dc, r;
159   const char *fp;
160   char buf[100];
161   struct tm *tm;
162   struct deventry *deve, devlookfor;
163   struct passwd *pw;
164   struct group *gr;
165   time_t btime;
166
167   if (raw) {
168     printf("%10lu ",(unsigned long)as->ac_btime);
169   } else {
170     btime= as->ac_btime;
171     tm= localtime(&btime);
172     strftime(buf,sizeof(buf),"%Y-%m-%d %H:%M:%S",tm); buf[sizeof(buf)-1]= 0;
173     printf("%19s ",buf);
174   }
175   
176   printf("%-16.16s ", as->ac_comm);
177   
178   pw= raw ? 0 : getpwuid(as->ac_uid);
179   if (pw) printf("%-8s ",pw->pw_name);
180   else printf("%-8ld ",(long)as->ac_uid);
181   
182   gr= raw ? 0 : getgrgid(as->ac_gid);
183   if (gr) printf("%-8s ",gr->gr_name);
184   else printf("%-8ld ",(long)as->ac_gid);
185
186   if (raw) {
187     if (!(as->ac_tty + 1) /* check for -1 without knowing type */) {
188       printf("-        ");
189     } else {
190       printf("%08lx ",(unsigned long)as->ac_tty);
191     }
192   } else {
193     if (!(as->ac_tty + 1)) {
194       printf("-          ");
195     } else {
196       if (!walkeddev) {
197         scandev("/dev",4);
198         qsort(deventries,de_used,sizeof(*deventries),walkdev_cptr);
199         walkeddev= 1;
200       }
201       devlookfor.fn= 0;
202       devlookfor.dev= as->ac_tty;
203       deve= bsearch(&devlookfor,deventries,de_used,sizeof(*deventries),walkdev_cptr);
204       if (deve) {
205         printf("%-10s ",deve->fn);
206       } else {
207         printf("%08lx   ",(unsigned long)as->ac_tty);
208       }
209     }
210   }
211
212   r= as->ac_flag;
213   for (i=1, fp= "FS4DX"; *fp; fp++, i<<=1) {
214     if (r&i) {
215       putchar(*fp);
216       r &= ~i;
217     } else if (!isdigit(*fp)) {
218       putchar(' ');
219     }
220   }
221   if (r) {
222     printf("#%x",r);
223   }
224   putchar(' ');
225   
226   dc= WCOREDUMP(as->ac_exitcode) ? 'd' : 'k';
227   if (raw) {
228     if (WIFEXITED(as->ac_exitcode)) {
229       printf(" %3d",WEXITSTATUS(as->ac_exitcode));
230     } else if (WIFSIGNALED(as->ac_exitcode)) {
231       printf("%c%3d",
232              dc,
233              WTERMSIG(as->ac_exitcode));
234     } else {
235       printf("%04lx",(unsigned long)as->ac_exitcode);
236     }
237   } else {
238     if (WIFEXITED(as->ac_exitcode)) {
239       printf(" %6d",WEXITSTATUS(as->ac_exitcode));
240     } else if (WIFSIGNALED(as->ac_exitcode)) {
241       r= WTERMSIG(as->ac_exitcode);
242       if (r>0 && r<=sizeof(sigabbrev)/sizeof(*sigabbrev)) {
243         printf("%c%6s",
244                dc,
245                sigabbrev[r-1]);
246       } else {
247         printf("%cSIG%-3d",
248                dc,
249                r);
250       }
251     } else {
252       printf("#%04lx",(unsigned long)as->ac_exitcode);
253     }
254   }
255
256   if (usages) {
257     printf(" %10lu %10lu %10lu %8ld %8ld",
258            (unsigned long)as->ac_utime,
259            (unsigned long)as->ac_stime,
260            (unsigned long)as->ac_etime,
261            (unsigned long)as->ac_minflt,
262            (unsigned long)as->ac_majflt);
263   }
264   putchar('\n');
265
266   checkstdout();
267 }
268
269 static void processfile(FILE *file, const char *filename) {
270   struct_acct as;
271   long pos;
272   int r;
273   
274   if (forwards) {
275     while ((r= fread(&as,1,sizeof(as),file)) == sizeof(as)) {
276       printrecord(&as,filename);
277     }
278   } else {
279     r= fseek(file,0,SEEK_END); if (r) { perror(filename); exit(8); }
280     pos= ftell(file); if (pos==-1) { perror(filename); exit(8); }
281     if (pos % sizeof(as)) { 
282       fprintf(stderr, "%s: File size is not an integral number "
283               "of accounting records\n", filename);
284       exit(8);
285     }
286     for (;;) {
287       if (pos<sizeof(as)) break;
288       pos -= sizeof(as);
289       r= fseek(file,pos,SEEK_SET); if (r==-1) { perror(filename); exit(8); }
290       r= fread(&as,1,sizeof(as),file); if (r!=sizeof(as)) { perror(filename); exit(8); }
291       printrecord(&as,filename);
292     }
293   }
294   if (ferror(file) || fclose(file)) { perror(filename); exit(8); }
295 }
296
297 static void processnamedfile(const char *filename) {
298   FILE *file;
299
300   file= fopen(filename,"rb"); if (!file) { perror(filename); exit(8); }
301   processfile(file,filename);
302 }
303
304 int main(int argc, const char *const *argv) {
305   myopt(&argv,cmdinfos);
306   if (!nobanner) printbanner();
307   if (usestdin) {
308     processfile(stdin,"<standard input>");
309   } else if (!*argv) {
310     processnamedfile("/var/log/account/pacct");
311   } else {
312     while (*argv) {
313       processnamedfile(*argv);
314       argv++;
315     }
316   }
317   checkstdout();
318   if (fflush(stdout)) { perror("flush stdout"); exit(8); }
319   return 0;
320 }