chiark / gitweb /
* Redact command name characters outside 33-126.
[chiark-utils.git] / cprogs / acctdump.c
index c6392e7287a775dbe7b81fac28b8c1ccde394dc2..b9040ac710f91eebe6a83af73262a2027a4e4e29 100644 (file)
 #include <grp.h>
 #include <dirent.h>
 #include <ctype.h>
+#include <errno.h>
 #include <sys/stat.h>
+
+typedef unsigned long long u64;
+
+
+/* Sadly this thing is not very portable */
+
+#if defined(__linux__)
+
+#include <sys/types.h>
+#include <sys/acct.h>
+
+typedef struct acct_v3 struct_acct;
+#define HAVE_AC_EXITCODE
+#define HAVE_AC_FLT
+#define FIELD_AC_FLAG(as) ((as)->ac_flag)
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+
+#include <sys/param.h>
+#include <sys/types.h>
 #include <sys/acct.h>
 
+typedef struct acctv2 struct_acct;
+#define HAVE_AC_IO_MEM
+#define FIELD_AC_FLAG(as) ((as)->ac_flagx & ~ANVER)
+
+#else
+
+#error Do not know what struct_acct to use on this platform
+
+#endif
+
+
 #include "myopt.h"
 
 static int forwards, nobanner, usestdin, raw, usages;
@@ -50,30 +82,20 @@ static const struct cmdinfo cmdinfos[]= {
   {  0                                                 }
 };
 
+#ifdef HAVE_AC_EXITCODE
 static const char *sigabbrev[]= {
   "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS", "FPE",
   "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT",
   "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG", "XCPU",
   "XFSZ", "VTALRM", "PROF", "WINCH", "IO"
 };
+#endif
 
-static void usage(FILE *file) {
+void usagemessage(void) {
   fputs("usage: acctdump [<options>] [<file> ...]\n"
        "options: -f|--forwards -q|--no-banner -p|--stdin -r|--raw -u|--resource\n",
-       file);
-  if (ferror(file)) { perror("print usage"); exit(8); }
-}
-
-void badusage(const char *fmt, ...) {
-  va_list al;
-
-  fputs("usage error: ",stderr);
-  va_start(al,fmt);
-  vfprintf(stderr,fmt,al);
-  va_end(al);
-  fputs("\n",stderr);
-  usage(stderr);
-  exit(12);
+       stderr);
+  if (ferror(stderr)) { perror("print usage"); exit(8); }
 }
 
 static void checkstdout(void) {
@@ -91,12 +113,16 @@ static void scandev(const char *basename, int levelsleft) {
   
   if (levelsleft==0) return;
 
-  dir= opendir(basename); if (!dir) return;
+  dir= opendir(basename); 
+  if (!dir) {
+    fprintf(stderr, "%s: opendir: %s\n", basename, strerror(errno));
+    return;
+  }
   fnbufalloc= 0;
   fnbuf= 0;
   basel= strlen(basename);
 
-  while ((de= readdir(dir))) {
+  while ((errno=0, de= readdir(dir))) {
     fnbufreq= basel+1+strlen(de->d_name)+1;
     if (fnbufalloc<fnbufreq) {
       fnbufalloc= fnbufreq+10;
@@ -106,6 +132,10 @@ static void scandev(const char *basename, int levelsleft) {
     }
     sprintf(fnbuf,"%s/%s",basename,de->d_name);
     r= lstat(fnbuf,&stab);
+    if (r) {
+      fprintf(stderr, "%s: %s\n", fnbuf, strerror(errno));
+      continue;
+    }
     if (S_ISCHR(stab.st_mode)) {
       if (de_used >= de_allocd) {
        nallocd= (de_allocd+10)<<1;
@@ -122,6 +152,8 @@ static void scandev(const char *basename, int levelsleft) {
       scandev(fnbuf,levelsleft-1);
     }
   }
+  if (errno)
+      fprintf(stderr, "%s: readdir: %s\n", basename, strerror(errno));
   closedir(dir);
   free(fnbuf);
 }
@@ -135,41 +167,64 @@ static int walkdev_cptr(const void *av, const void *bv) {
 static void printbanner(void) {
   if (raw) {
     fputs("begin date command          "
-         "uid      gid      tty dev  FSDX exit",
-         stdout);
+         "uid      gid      tty dev  FSDX "
+#ifdef HAVE_AC_EXITCODE
+         "exit"
+#endif
+         , stdout);
   } else {
     fputs("begin date and time command          "
-         "user     group    tty dev    FSDX sigexit",
-         stdout);
+         "user     group    tty dev    FSDX "
+#ifdef HAVE_AC_EXITCODE
+         "sigexit"
+#endif
+         , stdout);
   }
   if (usages) {
-    fputs("  user time   sys time  elap time   minflt   maxflt",
-         stdout);
+    fputs("  user time   sys time  elap time "
+#ifdef HAVE_AC_FLT
+         "  minflt   maxflt"
+#endif
+#ifdef HAVE_AC_IO_MEM
+         "  avg.mem      io"
+#endif
+         , stdout);
   }
   putchar('\n');
   checkstdout();
 }
 
-static void printrecord(const struct acct *as, const char *filename) {
+static void printrecord(const struct_acct *as, const char *filename) {
   static int walkeddev;
 
-  int i, dc, r;
+  int i, r;
   const char *fp;
   char buf[100];
   struct tm *tm;
   struct deventry *deve, devlookfor;
   struct passwd *pw;
   struct group *gr;
+  time_t btime;
+  char commbuf[sizeof(as->ac_comm)];
 
   if (raw) {
     printf("%10lu ",(unsigned long)as->ac_btime);
   } else {
-    tm= localtime(&as->ac_btime);
-    strftime(buf,sizeof(buf),"%Y-%m-%d %H:%M:%S",tm); buf[sizeof(buf)-1]= 0;
+    btime= as->ac_btime;
+    tm= localtime(&btime);
+    if (tm) {
+      strftime(buf,sizeof(buf),"%Y-%m-%d %H:%M:%S",tm); buf[sizeof(buf)-1]= 0;
+    } else {
+      snprintf(buf,sizeof(buf),"@%lu",(unsigned long)btime);
+    }
     printf("%19s ",buf);
   }
-  
-  printf("%-16.16s ", as->ac_comm);
+
+  for (i=0; i<sizeof(as->ac_comm); i++) {
+    int c=as->ac_comm[i];
+    commbuf[i]= ((c!=0 && c<=32) || c>=127) ? '?' : c;
+  }
+  printf("%-*.*s ", (int)sizeof(commbuf),(int)sizeof(commbuf), commbuf);
   
   pw= raw ? 0 : getpwuid(as->ac_uid);
   if (pw) printf("%-8s ",pw->pw_name);
@@ -180,13 +235,13 @@ static void printrecord(const struct acct *as, const char *filename) {
   else printf("%-8ld ",(long)as->ac_gid);
 
   if (raw) {
-    if (as->ac_tty == (dev_t)-1) {
+    if (!(as->ac_tty + 1) /* check for -1 without knowing type */) {
       printf("-        ");
     } else {
       printf("%08lx ",(unsigned long)as->ac_tty);
     }
   } else {
-    if (as->ac_tty == (dev_t)-1) {
+    if (!(as->ac_tty + 1)) {
       printf("-          ");
     } else {
       if (!walkeddev) {
@@ -205,7 +260,7 @@ static void printrecord(const struct acct *as, const char *filename) {
     }
   }
 
-  r= as->ac_flag;
+  r= FIELD_AC_FLAG(as);
   for (i=1, fp= "FS4DX"; *fp; fp++, i<<=1) {
     if (r&i) {
       putchar(*fp);
@@ -218,7 +273,9 @@ static void printrecord(const struct acct *as, const char *filename) {
     printf("#%x",r);
   }
   putchar(' ');
-  
+
+#ifdef HAVE_AC_EXITCODE
+  int dc;
   dc= WCOREDUMP(as->ac_exitcode) ? 'd' : 'k';
   if (raw) {
     if (WIFEXITED(as->ac_exitcode)) {
@@ -248,14 +305,23 @@ static void printrecord(const struct acct *as, const char *filename) {
       printf("#%04lx",(unsigned long)as->ac_exitcode);
     }
   }
+#endif /*HAVE_AC_EXITCODE*/
 
   if (usages) {
-    printf(" %10lu %10lu %10lu %8ld %8ld",
+    printf(" %10lu %10lu %10lu",
           (unsigned long)as->ac_utime,
           (unsigned long)as->ac_stime,
-          (unsigned long)as->ac_etime,
+          (unsigned long)as->ac_etime);
+#ifdef HAVE_AC_FLT
+    printf(" %8lu %8lu",
           (unsigned long)as->ac_minflt,
           (unsigned long)as->ac_majflt);
+#endif
+#ifdef HAVE_AC_IO_MEM
+    printf(" %4e %4e",
+          as->ac_mem,
+          as->ac_io);
+#endif
   }
   putchar('\n');
 
@@ -263,8 +329,8 @@ static void printrecord(const struct acct *as, const char *filename) {
 }
 
 static void processfile(FILE *file, const char *filename) {
-  fpos_t pos;
-  struct acct as;
+  struct_acct as;
+  long pos;
   int r;
   
   if (forwards) {
@@ -273,11 +339,16 @@ static void processfile(FILE *file, const char *filename) {
     }
   } else {
     r= fseek(file,0,SEEK_END); if (r) { perror(filename); exit(8); }
-    r= fgetpos(file,&pos); if (r) { perror(filename); exit(8); }
+    pos= ftell(file); if (pos==-1) { perror(filename); exit(8); }
+    if (pos % sizeof(as)) { 
+      fprintf(stderr, "%s: File size is not an integral number "
+             "of accounting records\n", filename);
+      exit(8);
+    }
     for (;;) {
       if (pos<sizeof(as)) break;
       pos -= sizeof(as);
-      r= fsetpos(file,&pos); if (r) { perror(filename); exit(8); }
+      r= fseek(file,pos,SEEK_SET); if (r==-1) { perror(filename); exit(8); }
       r= fread(&as,1,sizeof(as),file); if (r!=sizeof(as)) { perror(filename); exit(8); }
       printrecord(&as,filename);
     }
@@ -298,7 +369,7 @@ int main(int argc, const char *const *argv) {
   if (usestdin) {
     processfile(stdin,"<standard input>");
   } else if (!*argv) {
-    processnamedfile("/var/account/pacct");
+    processnamedfile("/var/log/account/pacct");
   } else {
     while (*argv) {
       processnamedfile(*argv);