chiark / gitweb /
current state of chiark version
[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 "acct.h"
36 /*#include <sys/acct.h>*/
37 typedef struct acct_v3 struct_acct;
38
39 #include "myopt.h"
40
41 static int forwards, nobanner, usestdin, raw, usages;
42
43 static int de_used, de_allocd;
44 static struct deventry {
45   const char *fn;
46   dev_t dev;
47 } *deventries;
48
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 },
55   {  0                                                 }
56 };
57
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"
63 };
64
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",
68         file);
69   if (ferror(file)) { perror("print usage"); exit(8); }
70 }
71
72 void badusage(const char *fmt, ...) {
73   va_list al;
74
75   fputs("usage error: ",stderr);
76   va_start(al,fmt);
77   vfprintf(stderr,fmt,al);
78   va_end(al);
79   fputs("\n",stderr);
80   usage(stderr);
81   exit(12);
82 }
83
84 static void checkstdout(void) {
85   if (ferror(stdout)) { perror("stdout"); exit(8); }
86 }
87
88 static void scandev(const char *basename, int levelsleft) {
89   /* We deliberately ignore most errors */
90   DIR *dir;
91   struct dirent *de;
92   struct stat stab;
93   int fnbufalloc, fnbufreq, r, basel, nallocd;
94   char *fnbuf, *nfnbuf;
95   struct deventry *ndeventries;
96   
97   if (levelsleft==0) return;
98
99   dir= opendir(basename); if (!dir) return;
100   fnbufalloc= 0;
101   fnbuf= 0;
102   basel= strlen(basename);
103
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; }
110       fnbuf= nfnbuf;
111     }
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;
119         de_allocd= nallocd;
120         deventries= ndeventries;
121       }
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;
125       de_used++;
126     } else if (S_ISDIR(stab.st_mode) && de->d_name[0] != '.') {
127       scandev(fnbuf,levelsleft-1);
128     }
129   }
130   closedir(dir);
131   free(fnbuf);
132 }
133
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;
138 }
139   
140 static void printbanner(void) {
141   if (raw) {
142     fputs("begin date command          "
143           "uid      gid      tty dev  FSDX exit",
144           stdout);
145   } else {
146     fputs("begin date and time command          "
147           "user     group    tty dev    FSDX sigexit",
148           stdout);
149   }
150   if (usages) {
151     fputs("  user time   sys time  elap time   minflt   maxflt",
152           stdout);
153   }
154   putchar('\n');
155   checkstdout();
156 }
157
158 static void printrecord(const struct_acct *as, const char *filename) {
159   static int walkeddev;
160
161   int i, dc, r;
162   const char *fp;
163   char buf[100];
164   struct tm *tm;
165   struct deventry *deve, devlookfor;
166   struct passwd *pw;
167   struct group *gr;
168   time_t btime;
169
170   if (raw) {
171     printf("%10lu ",(unsigned long)as->ac_btime);
172   } else {
173     btime= as->ac_btime;
174     tm= localtime(&btime);
175     strftime(buf,sizeof(buf),"%Y-%m-%d %H:%M:%S",tm); buf[sizeof(buf)-1]= 0;
176     printf("%19s ",buf);
177   }
178   
179   printf("%-16.16s ", as->ac_comm);
180   
181   pw= raw ? 0 : getpwuid(as->ac_uid);
182   if (pw) printf("%-8s ",pw->pw_name);
183   else printf("%-8ld ",(long)as->ac_uid);
184   
185   gr= raw ? 0 : getgrgid(as->ac_gid);
186   if (gr) printf("%-8s ",gr->gr_name);
187   else printf("%-8ld ",(long)as->ac_gid);
188
189   if (raw) {
190     if (!(as->ac_tty + 1) /* check for -1 without knowing type */) {
191       printf("-        ");
192     } else {
193       printf("%08lx ",(unsigned long)as->ac_tty);
194     }
195   } else {
196     if (!(as->ac_tty + 1)) {
197       printf("-          ");
198     } else {
199       if (!walkeddev) {
200         scandev("/dev",4);
201         qsort(deventries,de_used,sizeof(*deventries),walkdev_cptr);
202         walkeddev= 1;
203       }
204       devlookfor.fn= 0;
205       devlookfor.dev= as->ac_tty;
206       deve= bsearch(&devlookfor,deventries,de_used,sizeof(*deventries),walkdev_cptr);
207       if (deve) {
208         printf("%-10s ",deve->fn);
209       } else {
210         printf("%08lx   ",(unsigned long)as->ac_tty);
211       }
212     }
213   }
214
215   r= as->ac_flag;
216   for (i=1, fp= "FS4DX"; *fp; fp++, i<<=1) {
217     if (r&i) {
218       putchar(*fp);
219       r &= ~i;
220     } else if (!isdigit(*fp)) {
221       putchar(' ');
222     }
223   }
224   if (r) {
225     printf("#%x",r);
226   }
227   putchar(' ');
228   
229   dc= WCOREDUMP(as->ac_exitcode) ? 'd' : 'k';
230   if (raw) {
231     if (WIFEXITED(as->ac_exitcode)) {
232       printf(" %3d",WEXITSTATUS(as->ac_exitcode));
233     } else if (WIFSIGNALED(as->ac_exitcode)) {
234       printf("%c%3d",
235              dc,
236              WTERMSIG(as->ac_exitcode));
237     } else {
238       printf("%04lx",(unsigned long)as->ac_exitcode);
239     }
240   } else {
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)) {
246         printf("%c%6s",
247                dc,
248                sigabbrev[r-1]);
249       } else {
250         printf("%cSIG%-3d",
251                dc,
252                r);
253       }
254     } else {
255       printf("#%04lx",(unsigned long)as->ac_exitcode);
256     }
257   }
258
259   if (usages) {
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);
266   }
267   putchar('\n');
268
269   checkstdout();
270 }
271
272 static void processfile(FILE *file, const char *filename) {
273   struct_acct as;
274   int r;
275   
276   if (forwards) {
277     while ((r= fread(&as,1,sizeof(as),file)) == sizeof(as)) {
278       printrecord(&as,filename);
279     }
280   } else {
281     long seekdist= -(long)sizeof(as);
282     r= fseek(file,0,SEEK_END); if (r) { perror(filename); exit(8); }
283     for (;;) {
284       r= fseek(file,seekdist,SEEK_CUR);
285       if (r) {
286         if (errno=EINVAL) break;
287         perror(filename); exit(8);
288       }
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);
293     }
294   }
295   if (ferror(file) || fclose(file)) { perror(filename); exit(8); }
296 }
297
298 static void processnamedfile(const char *filename) {
299   FILE *file;
300
301   file= fopen(filename,"rb"); if (!file) { perror(filename); exit(8); }
302   processfile(file,filename);
303 }
304
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));
311   if (usestdin) {
312     processfile(stdin,"<standard input>");
313   } else if (!*argv) {
314     processnamedfile("/var/account/pacct");
315   } else {
316     while (*argv) {
317       processnamedfile(*argv);
318       argv++;
319     }
320   }
321   checkstdout();
322   if (fflush(stdout)) { perror("flush stdout"); exit(8); }
323   return 0;
324 }