/*
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
        }
    }
}
