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