
/*  utmp.c    Shareware Copyright by Sam Lantinga	10/6/93	*/

#include	<sys/types.h>
#include	<sys/stat.h>
#include	<fcntl.h>
#include	<utmp.h>
#include	<stdio.h>

#ifdef DEBUG
#undef  UTMP_FILE
#define UTMP_FILE  "/tmp/utmp"
#else
#ifndef UTMP_FILE
#define UTMP_FILE  "/etc/utmp"
#endif /* UTMP_FILE */
#endif /* DEBUG */

char utmp_name[14];	/* Temporary storage for the utmp user */
char utmp_host[64];	/* Temporary storage for the ut_host field */
time_t utmp_time;	/* Temporary storage for the login time */


/* Remove us from the utmp file, saving our entry to replace later */

static char tmphost[64];	/* Temporary storage for our ut_host field */
static char tmpuser[14];	/* Temporary storage for our utmp user */
static time_t tmptime;		/* Temporary storage for our login time */
static char tmptty[64];		/* Temporary storage for our tty name */

int remove_me()
{
	char *tmpptr;

	utmp_host[0]='\0';
	utmp_time=0;

	tmphost[0]='\0';
	tmptime=0L;
	tmpuser[0]='\0';
	tmptty[0]='\0';

	if ( (! isatty(0)) || ((tmpptr=(char *)ttyname(0)) == NULL) )
		return(-1);

	if ( utmp(tmpptr, "", NULL, 0L, 1) < 0 )
		return(-1);

	strcpy(tmpuser, utmp_name);
	strcpy(tmptty, tmpptr);
	strcpy(tmphost, utmp_host);
	tmptime=utmp_time;

	return(0);
}

/* You MUST call this AFTER you call remove_me() */

int replace_me()
{
	return(utmp(tmptty, tmpuser, tmphost, tmptime, 0));
}
	

/* Set up a utmp entry and tty for a user and a tty */

int addutmp(user, uid, tty)
char *user;		/* The user to add to the utmp file */
int uid;		/* The uid corresponding to user */
char *tty;		/* /dev/ttyxx */
{
	struct stat sb;
	time_t now;
	int retval;

	/* Add the utmp entry */
	(void) time(&now);
	retval=utmp(tty, user, NULL, now, 0);

#if !defined(SOLARIS) && !defined(IRIX)
	/* Solaris and Irix machines do this automatically */
	/* Change the ownership and mode of the tty */
	if ( stat(tty, &sb) == 0 ) {
		(void) chmod(tty, 0620);  /* crw--w---- */
		(void) chown(tty, uid, sb.st_gid);
	}
#endif
	return(retval);
}
	

/* End a utmp entry and tty for a user and a tty */

int delutmp(user, tty)
char *user;
char *tty;		/* /dev/ttyxx */
{
	struct stat sb;
	time_t now;
	int retval;

	/* Add the utmp entry */
	(void) time(&now);
	retval=utmp(tty, user, NULL, now, 1);

#if !defined(SOLARIS) && !defined(IRIX)
	/* Solaris and Irix machines do this automatically */
	/* Reset the owner and mode of the tty */
	if ( stat(tty, &sb) == 0 ) {
		(void) chmod(tty, 0666);	/* crw-rw-rw- */
		(void) chown(tty, 0, sb.st_gid);
	}
#endif
	return(retval);
}


/* This utmp function was adapted from pty's logs.c  */

int utmp(line, name, host, date, cleanup)
char *line;
char name[];
char *host;
time_t date;
int cleanup;
{
	struct utmp ut;
	struct utmp xt;
	char *lineptr;
	int fd, found=0;

	if ( (fd=open(UTMP_FILE, O_RDWR)) < 0 )
		return(-1);

	/* Initialize the utmp structure */
	d_zero((char *)&ut, sizeof(struct utmp));

	/* Get the ttyxy form of the tty pathname if possible. */
	if ( *line == '/' )
	{
		for ( lineptr=(line+1); *lineptr; ++lineptr )
		{
			if ( *lineptr == '/' )
				break;
		}
		if ( *lineptr == '/' )
			++lineptr;
	}
	else
		lineptr=line;

	(void) strncpy(ut.ut_line, lineptr, sizeof(ut.ut_line));

	if ( cleanup )
	{
		ut.ut_name[0]='\0';
#ifdef USER_PROCESS
		ut.ut_type = DEAD_PROCESS;
#endif
	}
	else
	{
#ifdef USER_PROCESS
		ut.ut_type = USER_PROCESS;
		ut.ut_pid = getpid();
#endif
		strncpy(ut.ut_name, name, sizeof(ut.ut_name));
	}
	ut.ut_time=date;

#ifdef HAVE_UTHOST
	if ( ! cleanup && host )
		strncpy(ut.ut_host, host, sizeof(ut.ut_host));
	else
		ut.ut_host[0]='\0';
#endif

	while (read(fd,(char *) &xt, sizeof(xt)) == sizeof(xt))
 	{
#ifdef USER_PROCESS
		if ( cleanup && xt.ut_type != USER_PROCESS )
			continue;	/* Only grab the active lines */
#endif
  		if (strncmp(xt.ut_line, ut.ut_line, sizeof(xt.ut_line)) == 0)
  	  	{
#ifdef HAVE_UTHOST	/* Save the host entry */
			strcpy(utmp_host, xt.ut_host);
#else
			utmp_host[0]='\0';
#endif /* HAVE_UTHOST */
			/* Save the login time and user name */
			utmp_time=xt.ut_time;
			strcpy(utmp_name, xt.ut_name);

			/* Break out; we've found our entry! */
			found=1;
			lseek(fd, -(long)sizeof(struct utmp), 1);
     			break;
    		}
  	}

	/* Add a new entry to the utmp file if we can't find our entry */
	if ( (!found) && (!cleanup) )
  	{ /* Reopen to avoid a race with other end-of-utmp entries. */
   		(void) close(fd);
   		if ( (fd=open(UTMP_FILE, (O_RDWR|O_APPEND))) < 0 )
     			return -1;
  	}

 	if (write(fd, (char *) &ut, sizeof(ut)) < sizeof(ut))
  	{
   		(void) close(fd);
   		return -1;
  	}
 	return(close(fd));
}
