Code:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <utmp.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
/* error macro */
#define ck(x) if ((x)==NULL) \
{barf("Memory/File error");}
/* limits */
#define USER_NAME_LEN 8
#define MAX_USERS 2000
/* array of usernames and times */
typedef struct
{
char user[12];
time_t logtime;
} ulog_t;
ulog_t array[MAX_USERS]={ {0x0}, 0 };
/* exit point for errors */
void barf(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
/* return size of file in bytes */
size_t filesize(const int fd)
{
struct stat st;
if(fstat(fd, &st) == (-1) )
barf("Cannot stat file");
return st.st_size;
}
/* return time_t seconds for ndays in the past */
time_t days_ago(const time_t ndays)
{
time_t then=0;
errno=0;
then=time(NULL);
if(then == (time_t)-1 && errno)
barf("");
then-=(86400 * ndays);
return then;
}
/* map the utmp file into memory */
void *mappit(FILE *u, size_t ulen)
{
void *addr=mmap((void *)0, ulen, PROT_READ, MAP_PRIVATE, fileno(u),0);
if (addr==MAP_FAILED)
barf("mmap failed");
return addr;
}
/* unmap file */
int unmappit(void *addr, size_t len)
{
if( munmap(addr,len)==(-1))
return 1;
return 0;
}
/* find user in array, change time or (if not found) add to array */
void find(const struct utmp *src, int *cnt)
{
int i=0;
int found=0;
int limit=*cnt;
char tmp[10]={0x0};
snprintf(tmp, USER_NAME_LEN+1, "%s",src->ut_user);
if(strlen(tmp)> 0 )
{
for(i=0; !found && i < limit; i++)
{
if(strncmp(tmp, array[i].user, USER_NAME_LEN)==0)
{
found=1;
if(src->ut_time > array[i].logtime)
array[i].logtime=src->ut_time;
}
}
if(found==0)
{
memcpy(array[limit].user, tmp, USER_NAME_LEN);
array[limit].logtime=src->ut_time;
limit++;
}
*cnt=limit;
}
}
void setup_array(const void *addr, int *arrlen, const size_t ulen)
{
int i=0;
int max=ulen/sizeof(struct utmp);
int cnt=0;
const struct utmp *f=(const struct utmp *)addr;
for(i=0; i<max; i++, f++)
{
if(f->ut_type==USER_PROCESS && strlen(f->ut_user))
{
find(f, &cnt);
}
}
*arrlen=cnt;
}
int between(const time_t ckval, const time_t low, const time_t high)
{
int retval=(ckval >= low && ckval <= high);
return retval;
}
int search(const time_t low_limit, const time_t high_limit, int arrlen)
{
int i=0;
char tmp[80]={0x0};
time_t lt=0;
ulog_t *p=array;
for(i=0; i < arrlen; i++, p++)
{
if( (high_limit && between(p->logtime, low_limit, high_limit)) ||
(!high_limit && p->logtime < low_limit) )
{
lt=p->logtime;
strftime(tmp, sizeof(tmp), "%c", localtime(<));
printf("%s: last login %s\n", p->user, tmp);
}
}
return 0;
}
/*************************
*
* search for the last time each one logged on
* if logon in time range prt it
***************************/
int process(FILE *u, const time_t low_limit, const time_t high_limit)
{
int retval=0;
size_t ulen=filesize(fileno(u));
int arrlen=0;
void *addr=mappit(u, ulen);
setup_array(addr, &arrlen, ulen);
retval=search(low_limit, high_limit, arrlen);
retval|=unmappit(addr, ulen);
return retval;
}
/*
first argument req'd: # days
read from /var/adm/wtmp - (file used by last)
print all users with last logins < low_limit or with
last logins between low_limit and high_limit when
high_limit is non-zero
*/
int main(int argc, char **argv)
{
int retval=0;
const time_t low_limit= (argc>1) ? days_ago(atoi(argv[1])): 0;
const time_t high_limit=(argc>2) ? days_ago(atoi(argv[2])): 0;
FILE *u=fopen("/var/adm/wtmp", "r");
ck(u);
if ( low_limit > 0 && (high_limit==0 || high_limit >= low_limit))
retval=process(u, low_limit, high_limit);
else
{
fprintf(stderr,
"Bad parameters.\n\tONE required: <number days ago>\n\tOptional: <most recent day>\n");
fprintf(stderr," usage: utmp 10 5\n");
retval=1;
}
return retval;
}