#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/file.h>
#include <utmp.h>

#define WARP_FACTOR	(60*60*24*120)	/* 120 days */

/* Simple fixtmp does this:
 * - Scan forward until it hits bad utmp.
 * - Reposition at end of wtmp and scan backwards until it hits bad utmp.
 * - Ouput the good stuff.
 * |------> [bad entries] <------|
 * This works OK with just one corrupted area, not so good with lots of corruption/
 * libc5/glib wtmp sharing.
 */
/*
 * usage: fixtmp <corrupted file> [ <output file> | - ]
 */
int main(int argc, char **argv)
{
  struct utmp u;
  int n, in, out;
  time_t lastent;
  long rpos, lpos, pos;
  

  if (argc < 3) {
    fprintf(stderr,"Usage: %s <infile> [ <outfile> | - ]\n",argv[0]);
    exit(1);
  }
  if ((in = open(argv[1],O_RDONLY,0)) < 0) {
    fprintf(stderr,"unable to open '%s' for reading.\n",argv[1]);
    exit(1);
  }

  if (!strcmp("-",argv[2])) out = 1;
  else {
    if ((out = open(argv[2],O_WRONLY | O_CREAT, 0666)) < 0) {
      fprintf(stderr,"unable to open '%s' for writing.\n",argv[2]);
      exit(1);
    }
  }

  lastent = 0;
  rpos = 0;
  while((n=read(in,&u,sizeof(struct utmp))) == sizeof(struct utmp)) {
    if (u.ut_time == 0) continue;
    if (lastent && (u.ut_time < (lastent-WARP_FACTOR) || u.ut_time > (lastent+WARP_FACTOR))) break;
    lastent = u.ut_time;
    if (write(out,&u,sizeof(struct utmp)) < 0) {
      fprintf(stderr,"error writing output.\n");
      exit(1);
    }
    rpos += sizeof(struct utmp);
  }

  if (n == 0) {
    fprintf(stderr,"no corruption detected.\n");
    exit(0);
  }
  if (n < 0) {
    fprintf(stderr,"error reading input file.\n");
    exit(1);
  }
  if (n < sizeof(struct utmp)) {
    fprintf(stderr,"premature EOF, truncating.\n");
    exit(0);
  }

  fprintf(stderr,"corruption detected, scanning from end.\n");
  if ((pos = lseek(in,0,SEEK_END)) < 0) {
    fprintf(stderr,"error seeking to EOF -- aborting.\n");
    exit(1);
  }

  lpos = pos - sizeof(struct utmp);
  lastent = 0;

  while(lpos > 0) {
    if ((pos = lseek(in,lpos,SEEK_SET)) < 0) {
      fprintf(stderr,"error scanning backwards.\n");
      exit(1);
    }
    if ((n = read(in,&u,sizeof(struct utmp))) != sizeof(struct utmp)) {
      fprintf(stderr,"error reading input.\n");
      exit(1);
    }
    if (lastent && (u.ut_time < (lastent-WARP_FACTOR) || u.ut_time > (lastent+WARP_FACTOR))) break;

    lastent = u.ut_time;
    lpos -= sizeof(struct utmp);
  }
  lpos += sizeof(struct utmp);

  while((n=read(in,&u,sizeof(struct utmp))) == sizeof(struct utmp)) {
    if (write(out,&u,sizeof(struct utmp)) < 0) {
      fprintf(stderr,"error writing output.\n");
      exit(1);
    }
  }
  fprintf(stderr,"%ld bytes removed.\n",lpos-rpos);
  exit(0);
}
