/*
The problem often seems to arise in dealing with terminated
xterms.  (This is yet another reason for why we need a real
session manager.)  Rebooting to fix utmp is unduly harsh!
Just use a binary editor of some sort or write a single-purpose
script to hammer a null at the beginning of the entry.  In
either case, you'll need to understand the format of utmp(5).

Or you could use the following program, written by a younger
version of myself long ago and far away.  It's aesthetically
bothersome in various ways, but it still works well enough to
get the job done on the machines I have to deal with, even today.

To clear an entry on say ttyp5, as root type:

    chusr '' ttyp5

--tom
*/

/*
 * chusr.c -- change user entry in utmp 
 *
 * Tom Christiansen (1984), for BSD4.2 systems
 *
 * (various minor hacks since then, like 4.3 compat stuff)
 */

#include <stdio.h>
#include <pwd.h>
#include <sys/file.h>
#include <sysexits.h>
#include <syslog.h>
#include <utmp.h>

#define SCPYN(to,from) (void) strncpy(to,from,sizeof(to))

#ifndef BSD42
#	define BSD43 1
#endif


char   *strncpy ();
char   *ttyname ();
char   *index ();
char   *rindex ();
int     strncmp ();

main (ac, av)
    char  **av;
{
    struct utmp utmp;			/* new utmp entry for utmp file */

    int     nentries,			/* how many we got */
            ufd,			/* utmp's fd */
            tslot,			/* where to put the entry */
            tflag = 0;			/* update time? */

    char   *mytty = "/dev/ttyXXXX",    
	   *ufile = "/etc/utmp", 
	   *s;

    if (ac < 2 || ac > 4)
	usage (*av);


    if (!strcmp (av[1], "-t")) {
	tflag++;
	av++;
	ac--;
    }
    s = av[2];

    if ((ufd = open (ufile, O_RDWR)) < 0) {
	perror (ufile);
	exit (EX_OSFILE);
    }
    if (ac == 2) {
	if ((mytty = ttyname (fileno (stdin))) == NULL) {
	    fprintf (stderr, "%s: no tty\n", *av);
	    exit (EX_UNAVAILABLE);
	}
    } else {
	if (strlen (s) == 2) {
	    if (s[0] == 'c' && s[1] == 'o')
		strcpy (rindex (mytty, '/') + 1, "console");
	    else if (s[0] == 'e' && s[1] == 'x')
		strcpy (rindex (mytty, '/') + 1, "express");
	    else
		strcpy (index (mytty, 'X'), s);
	} else {
	    char *i1 = index (mytty, 't');
	    char *i2 = index (s, 't');
	    if (!i1) {
		printf(stderr,"no t in %s\n", mytty);
		exit(17);
	    } 
	    if (!i2) {
		printf(stderr,"no t in %s\n", s);
		exit(18);
	    } 
	    strncpy (i1, i2, 7);
	}
    }

    mytty += 5;				/* skip "/dev/" */

    if (!(tslot = ttyslot (mytty))) {
	fprintf (stderr, "no tty\n");
	exit (1);
    }
    lseek (ufd, (long) (tslot * sizeof (utmp)), 0);
    read (ufd, (char *) &utmp, sizeof (utmp));

#ifdef LOG_AUTH
    openlog ("chusr", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
#else
    openlog ("chusr", LOG_PID);
#endif

    syslog (LOG_NOTICE, "\"%.*s\" changed to \"%s\" on %s\n",
	    sizeof (utmp.ut_name), utmp.ut_name, av[1], mytty);
    closelog();
    SCPYN (utmp.ut_name, av[1]);
    SCPYN (utmp.ut_line, mytty);
    if (ac == 4)
	SCPYN (utmp.ut_host, av[3]);
    if (tflag)
	time (&utmp.ut_time);
    lseek (ufd, (long) (tslot * sizeof (utmp)), 0);
    write (ufd, (char *) &utmp, sizeof (utmp));
    (void) close (ufd);
}


usage (name)
    char   *name;
{
    fprintf (stderr, "usage: %s user [-t] [ tty [whence] ]\n", name);
    exit (EX_USAGE);
}

/* 
/*
 * Return the number of the slot in the utmp file
 * corresponding to the passed tty.
 * returns 0 on error.
 */

char   *getttys ();
static char ttys[] = "/etc/ttys";

#define	NULL	0

ttyslot (tty) 
    register char *tty;
{
    register char *tp, *p;
    register s;
    FILE   *tf;
    tp = tty;

    if ((p = rindex (tp, '/')) == NULL)
	p = tp;
    else
	p++;
    if ((tf = fopen (ttys, "r")) == NULL)
	return (0);
    s = 0;
    while (tp = getttys (tf)) {
	s++;
	if (strncmp (p, tp, strlen (p)) == 0) {
	    fclose (tf);
	    return (s);
	}
    }
    fclose (tf);
    return (0);
}

static char *
getttys (f)
    FILE   *f;
{
    static char line[256];
    register char *lp;

    do {
	if (fgets (line, 256, f) == NULL)
	    return NULL;
    } while (*line == '#');		/* ignore comment lines */

    for (lp = line;; lp++) {
	if ((*lp == '\t') || (*lp == ' ') || (*lp == '\n')) {
	    *lp = '\0';
#ifdef BSD43
	    return (line);
#else
	    return (line + 2);
#endif
	}
    }
}
