wtmpfix.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1993-2002 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* wtmpfix - adjust wtmpx file and remove date changes.
* wtmpfix <wtmpx1 >wtmpx2
*
* code are added to really fix wtmpx if it is corrupted ..
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include "acctdef.h"
#include <utmpx.h>
#include <signal.h>
#include <time.h>
#include <ctype.h>
#include <locale.h>
#include <limits.h>
#include <stdlib.h>
#define MAXRUNTIME 3600 /* time out after 1 hour */
#define DAYEPOCH (60 * 60 * 24)
#define wout(f, w) fwrite(w, sizeof (struct utmpx), 1, f);
FILE *Wtmpx, *Opw;
FILE *fp;
char Ofile[] = "/tmp/wXXXXXX";
static char time_buf[50];
struct dtab
{
off_t d_off1; /* file offset start */
off_t d_off2; /* file offset stop */
time_t d_adj; /* time adjustment */
struct dtab *d_ndp; /* next record */
};
struct dtab *Fdp; /* list header */
struct dtab *Ldp; /* list trailer */
time_t lastmonth, nextmonth;
off_t recno;
struct utmpx Ut, Ut2;
int year, month;
int ch;
int n;
int multimode; /* multi user mode WHCC */
static int winp(FILE *, struct utmpx *);
static void mkdtab(off_t);
static void setdtab(off_t, struct utmpx *, struct utmpx *);
static void adjust(off_t, struct utmpx *);
static int invalid(char *);
static void intr(int);
static void scanfile(void);
static int inrange(void);
static void wabort(int);
int
main(int argc, char **argv)
{
time_t tloc;
struct tm *tmp;
int fd;
(void) setlocale(LC_ALL, "");
setbuf(stdout, NULL);
alarm(MAXRUNTIME);
if (signal(SIGALRM, wabort) == SIG_ERR) {
perror("signal");
return (1);
}
if (signal(SIGINT, intr) == SIG_ERR) {
perror("signal");
return (1);
}
time(&tloc);
tmp = localtime(&tloc);
year = tmp->tm_year;
month = tmp->tm_mon + 1;
lastmonth = ((year + 1900 - 1970) * 365 +
(month - 1) * 30) * DAYEPOCH;
nextmonth = ((year + 1900 - 1970) * 365 +
(month + 1) * 30) * DAYEPOCH;
if (argc < 2) {
argv[argc] = "-";
argc++;
}
if ((fd = mkstemp(Ofile)) == -1) {
fprintf(stderr, "cannot make temporary: %s\n", Ofile);
intr(0);
}
if ((Opw = fdopen(fd, "w")) == NULL) {
fprintf(stderr, "cannot open temporary: %s\n", Ofile);
intr(0);
}
while (--argc > 0) {
argv++;
if (strcmp(*argv, "-") == 0)
Wtmpx = stdin;
else if ((Wtmpx = fopen(*argv, "r")) == NULL) {
fprintf(stderr, "Cannot open: %s\n", *argv);
intr(0);
}
scanfile();
if (Wtmpx != stdin)
fclose(Wtmpx);
}
fclose(Opw);
if ((Opw = fopen(Ofile, "r")) == NULL) {
fprintf(stderr, "Cannot read from temp: %s\n", Ofile);
intr(0);
}
recno = 0;
while (winp(Opw, &Ut)) {
adjust(recno, &Ut);
recno += sizeof (struct utmpx);
wout(stdout, &Ut);
}
fclose(Opw);
unlink(Ofile);
return (0);
}
static int
winp(FILE *f, struct utmpx *w)
{
if (fread(w, sizeof (struct utmpx), 1, f) != 1)
return (0);
if ((w->ut_type >= EMPTY) && (w->ut_type <= UTMAXTYPE))
return (1);
else {
fprintf(stderr, "Bad file at offset %ld\n",
ftell(f) - sizeof (struct utmpx));
cftime(time_buf, DATE_FMT, &w->ut_xtime);
fprintf(stderr, "%-12s %-8s %lu %s",
w->ut_line, w->ut_user, w->ut_xtime, time_buf);
intr(0);
}
/* NOTREACHED */
}
static void
mkdtab(off_t p)
{
struct dtab *dp;
dp = Ldp;
if (dp == NULL) {
dp = calloc(sizeof (struct dtab), 1);
if (dp == NULL) {
fprintf(stderr, "out of core\n");
intr(0);
}
Fdp = Ldp = dp;
}
dp->d_off1 = p;
}
static void
setdtab(off_t p, struct utmpx *w1, struct utmpx *w2)
{
struct dtab *dp;
if ((dp = Ldp) == NULL) {
fprintf(stderr, "no dtab\n");
intr(0);
}
dp->d_off2 = p;
dp->d_adj = w2->ut_xtime - w1->ut_xtime;
if ((Ldp = calloc(sizeof (struct dtab), 1)) == NULL) {
fprintf(stderr, "out of core\n");
intr(0);
}
Ldp->d_off1 = dp->d_off1;
dp->d_ndp = Ldp;
}
static void
adjust(off_t p, struct utmpx *w)
{
off_t pp;
struct dtab *dp;
pp = p;
for (dp = Fdp; dp != NULL; dp = dp->d_ndp) {
if (dp->d_adj == 0)
continue;
if (pp >= dp->d_off1 && pp < dp->d_off2)
w->ut_xtime += dp->d_adj;
}
}
/*
* invalid() determines whether the name field adheres to
* the criteria set forth in acctcon1. If the name violates
* conventions, it returns a truth value meaning the name is
* invalid; if the name is okay, it returns false indicating
* the name is not invalid.
*/
static int
invalid(char *name)
{
int i;
for (i = 0; i < NSZ; i++) {
if (name[i] == '\0')
return (VALID);
if (! (isalnum(name[i]) || (name[i] == '$') ||
(name[i] == ' ') || (name[i] == '.') ||
(name[i] == '_') || (name[i] == '-'))) {
return (INVALID);
}
}
return (VALID);
}
static void
intr(int sig)
{
signal(SIGINT, SIG_IGN);
unlink(Ofile);
exit(1);
}
/*
* scanfile:
* 1) reads the file, to see if the record is within reasonable
* range; if not, then it will scan the file, delete foreign stuff.
* 2) enter setdtab if in multiuser mode
* 3) change bad login names to INVALID
*/
static void
scanfile()
{
while ((n = fread(&Ut, sizeof (Ut), 1, Wtmpx)) > 0) {
if (n == 0) {
unlink(Ofile);
exit(0);
}
if (!inrange()) {
for (;;) {
if (fseek(Wtmpx,
-(off_t)sizeof (Ut), 1) != 0) {
perror("seek error\n");
exit(1);
}
if ((ch = getc(Wtmpx)) == EOF) {
perror("read\n");
exit(1);
}
fprintf(stderr, "checking offset %lo\n",
ftell(Wtmpx));
if (fread(&Ut, sizeof (Ut), 1, Wtmpx) == 0) {
exit(1);
}
if (inrange())
break;
}
}
/* Now we have a good utmpx record, do more processing */
#define UTYPE Ut.ut_type
#define ULINE Ut.ut_line
if (recno == 0 || UTYPE == BOOT_TIME)
mkdtab(recno);
if (UTYPE == RUN_LVL) {
if (strncmp(ULINE, "run-level S", 11) == 0)
multimode = 0;
if (strncmp(ULINE, "run-level 2", 11) == 0)
multimode++;
}
if (invalid(Ut.ut_name)) {
fprintf(stderr,
"wtmpfix: logname \"%*.*s\" changed "
"to \"INVALID\"\n", OUTPUT_NSZ,
OUTPUT_NSZ, Ut.ut_name);
(void) strncpy(Ut.ut_name, "INVALID", NSZ);
}
if (UTYPE == OLD_TIME) {
if (!winp(Wtmpx, &Ut2)) {
fprintf(stderr, "Input truncated at "
"offset %ld\n", recno);
intr(0);
}
if (Ut2.ut_type != NEW_TIME) {
fprintf(stderr, "New date expected at "
"offset %ld", recno);
intr(0);
}
if (multimode) /* multiuser */
setdtab(recno, &Ut, &Ut2);
recno += (2 * sizeof (struct utmpx));
wout(Opw, &Ut);
wout(Opw, &Ut2);
continue;
}
wout(Opw, &Ut);
recno += sizeof (struct utmpx);
}
}
static int
inrange()
{
if ((strcmp(Ut.ut_line, RUNLVL_MSG) == 0) ||
(strcmp(Ut.ut_line, BOOT_MSG) == 0) ||
(strcmp(Ut.ut_line, "acctg on") == 0) ||
(strcmp(Ut.ut_line, OTIME_MSG) == 0) ||
(strcmp(Ut.ut_line, NTIME_MSG) == 0))
return (1);
if (Ut.ut_id != 0 &&
Ut.ut_xtime > 0 &&
Ut.ut_xtime > lastmonth &&
Ut.ut_xtime < nextmonth &&
Ut.ut_type >= EMPTY &&
Ut.ut_type <= UTMAXTYPE &&
Ut.ut_pid >= 0)
return (1);
return (0);
}
static void
wabort(int sig)
{
fprintf(stderr, "give up\n");
exit(1);
}