usr/src/lib/libc/port/gen/getut.c
author Jon Tibble <meths@btinternet.com>
Thu, 09 Dec 2010 22:32:39 +0100
changeset 13255 4afa820d78b9
parent 7767 905db6a104b6
permissions -rw-r--r--
298 SPARC build fails in smt_pause.o 478 Build needs fixing for pkgdepend flag day Reviewed by: [email protected] Reviewed by: [email protected] Reviewed by: [email protected] Approved by: [email protected]

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1988 AT&T	*/
/*	  All Rights Reserved  	*/

/*
 * Compatibility routines to read and write alternate
 * utmp-like files.  These routines are only used in
 * the case where utmpname() is used to change to a file
 * other than /var/adm/utmp or /var/adm/wtmp.  In this case,
 * we assume that someone really wants to read old utmp-format
 * files.  Otherwise, the getutent, setutent, getutid, setutline,
 * and pututline functions are actually wrappers around the
 * equivalent function operating on utmpx-like files.
 */

#include "lint.h"
#include <stdio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utmpx.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <utime.h>
#include <sys/wait.h>

#define	IDLEN	4	/* length of id field in utmp */
#define	SC_WILDC	0xff	/* wild char for utmp ids */
#define	MAXVAL	255	/* max value for an id 'character' */

#ifdef ut_time
#undef ut_time
#endif

static void	utmp_frec2api(const struct futmp *, struct utmp *);
static void	utmp_api2frec(const struct utmp *, struct futmp *);
struct utmp 	*_compat_getutent(void);
struct utmp	*_compat_getutid(const struct utmp *);
struct utmp	*_compat_getutline(const struct utmp *);
struct utmp	*_compat_pututline(const struct utmp *);
void		_compat_setutent(void);
void		_compat_endutent(void);
void		_compat_updwtmp(const char *, struct utmp *);
struct utmp	*_compat_makeut(struct utmp *);
struct utmp	*_compat_modut(struct utmp *);

static void	unlockut(void);
static int	idcmp(const char *, const char *);
static int	allocid(char *, unsigned char *);
static int	lockut(void);


static int fd = -1;	/* File descriptor for the utmp file. */
/*
 * name of the current utmp-like file - set by utmpname (getutx.c)
 * only if running in backward compatibility mode
 * We don't modify this, but we can't declare it const or lint will freak.
 */
extern char _compat_utmpfile[];

#ifdef ERRDEBUG
static long loc_utmp;	/* Where in "utmp" the current "ubuf" was found. */
#endif

static struct futmp fubuf;	/* Copy of last entry read in. */
static struct utmp ubuf;	/* Last entry returned to client */

/*
 * In the 64-bit world, the utmp data structure grows because of
 * the ut_time field (a time_t) at the end of it.
 */
static void
utmp_frec2api(const struct futmp *src, struct utmp *dst)
{
	if (src == NULL)
		return;

	(void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
	(void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
	(void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
	dst->ut_pid = src->ut_pid;
	dst->ut_type = src->ut_type;
	dst->ut_exit.e_termination = src->ut_exit.e_termination;
	dst->ut_exit.e_exit = src->ut_exit.e_exit;
	dst->ut_time = (time_t)src->ut_time;
}

static void
utmp_api2frec(const struct utmp *src, struct futmp *dst)
{
	if (src == NULL)
		return;

	(void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
	(void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
	(void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
	dst->ut_pid = src->ut_pid;
	dst->ut_type = src->ut_type;
	dst->ut_exit.e_termination = src->ut_exit.e_termination;
	dst->ut_exit.e_exit = src->ut_exit.e_exit;
	dst->ut_time = (time32_t)src->ut_time;
}

/*
 * "getutent_frec" gets the raw version of the next entry in the utmp file.
 */
static struct futmp *
getutent_frec(void)
{
	/*
	 * If the "utmp" file is not open, attempt to open it for
	 * reading.  If there is no file, attempt to create one.  If
	 * both attempts fail, return NULL.  If the file exists, but
	 * isn't readable and writeable, do not attempt to create.
	 */
	if (fd < 0) {
		if ((fd = open(_compat_utmpfile, O_RDWR|O_CREAT, 0644)) < 0) {

			/*
			 * If the open failed for permissions, try opening
			 * it only for reading.  All "pututline()" later
			 * will fail the writes.
			 */
			if ((fd = open(_compat_utmpfile, O_RDONLY)) < 0)
				return (NULL);
		}
	}

	/* Try to read in the next entry from the utmp file.  */

	if (read(fd, &fubuf, sizeof (fubuf)) != sizeof (fubuf)) {
		bzero(&fubuf, sizeof (fubuf));
		return (NULL);
	}

	/* Save the location in the file where this entry was found. */

	(void) lseek(fd, 0L, 1);
	return (&fubuf);
}

/*
 * "_compat_getutent" gets the next entry in the utmp file.
 */
struct utmp *
_compat_getutent(void)
{
	struct futmp *futp;

	futp = getutent_frec();
	utmp_frec2api(&fubuf, &ubuf);
	if (futp == NULL)
		return (NULL);
	return (&ubuf);
}

/*
 * "_compat_getutid" finds the specified entry in the utmp file.  If
 * it can't find it, it returns NULL.
 */
struct utmp *
_compat_getutid(const struct utmp *entry)
{
	short type;

	utmp_api2frec(&ubuf, &fubuf);

	/*
	 * Start looking for entry.  Look in our current buffer before
	 * reading in new entries.
	 */
	do {
		/*
		 * If there is no entry in "ubuf", skip to the read.
		 */
		if (fubuf.ut_type != EMPTY) {
			switch (entry->ut_type) {

			/*
			 * Do not look for an entry if the user sent
			 * us an EMPTY entry.
			 */
			case EMPTY:
				return (NULL);

			/*
			 * For RUN_LVL, BOOT_TIME, DOWN_TIME,
			 * OLD_TIME, and NEW_TIME entries, only the
			 * types have to match.  If they do, return
			 * the address of internal buffer.
			 */
			case RUN_LVL:
			case BOOT_TIME:
			case DOWN_TIME:
			case OLD_TIME:
			case NEW_TIME:
				if (entry->ut_type == fubuf.ut_type) {
					utmp_frec2api(&fubuf, &ubuf);
					return (&ubuf);
				}
				break;

			/*
			 * For INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS,
			 * and DEAD_PROCESS the type of the entry in "fubuf",
			 * must be one of the above and id's must match.
			 */
			case INIT_PROCESS:
			case LOGIN_PROCESS:
			case USER_PROCESS:
			case DEAD_PROCESS:
				if (((type = fubuf.ut_type) == INIT_PROCESS ||
				    type == LOGIN_PROCESS ||
				    type == USER_PROCESS ||
				    type == DEAD_PROCESS) &&
				    fubuf.ut_id[0] == entry->ut_id[0] &&
				    fubuf.ut_id[1] == entry->ut_id[1] &&
				    fubuf.ut_id[2] == entry->ut_id[2] &&
				    fubuf.ut_id[3] == entry->ut_id[3]) {
					utmp_frec2api(&fubuf, &ubuf);
					return (&ubuf);
				}
				break;

			/* Do not search for illegal types of entry. */
			default:
				return (NULL);
			}
		}
	} while (getutent_frec() != NULL);

	/* the proper entry wasn't found. */

	utmp_frec2api(&fubuf, &ubuf);
	return (NULL);
}

/*
 * "_compat_getutline" searches the "utmp" file for a LOGIN_PROCESS or
 * USER_PROCESS with the same "line" as the specified "entry".
 */
struct utmp *
_compat_getutline(const struct utmp *entry)
{
	utmp_api2frec(&ubuf, &fubuf);

	do {
		/*
		 * If the current entry is the one we are interested in,
		 * return a pointer to it.
		 */
		if (fubuf.ut_type != EMPTY &&
		    (fubuf.ut_type == LOGIN_PROCESS ||
		    fubuf.ut_type == USER_PROCESS) &&
		    strncmp(&entry->ut_line[0], &fubuf.ut_line[0],
		    sizeof (fubuf.ut_line)) == 0) {
			utmp_frec2api(&fubuf, &ubuf);
			return (&ubuf);
		}
	} while (getutent_frec() != NULL);

	utmp_frec2api(&fubuf, &ubuf);
	return (NULL);
}

/*
 * "_compat_pututline" writes the structure sent into the utmp file
 * If there is already an entry with the same id, then it is
 * overwritten, otherwise a new entry is made at the end of the
 * utmp file.
 */
struct utmp *
_compat_pututline(const struct utmp *entry)
{
	int fc;
	struct utmp *answer;
	struct utmp tmpbuf;
	struct futmp ftmpbuf;

	/*
	 * Copy the user supplied entry into our temporary buffer to
	 * avoid the possibility that the user is actually passing us
	 * the address of "ubuf".
	 */
	tmpbuf = *entry;
	utmp_api2frec(entry, &ftmpbuf);

	(void) getutent_frec();
	if (fd < 0) {
#ifdef	ERRDEBUG
		gdebug("pututline: Unable to create utmp file.\n");
#endif
		return (NULL);
	}

	/* Make sure file is writable */

	if ((fc = fcntl(fd, F_GETFL, NULL)) == -1 || (fc & O_RDWR) != O_RDWR)
		return (NULL);

	/*
	 * Find the proper entry in the utmp file.  Start at the current
	 * location.  If it isn't found from here to the end of the
	 * file, then reset to the beginning of the file and try again.
	 * If it still isn't found, then write a new entry at the end of
	 * the file.  (Making sure the location is an integral number of
	 * utmp structures into the file incase the file is scribbled.)
	 */

	if (_compat_getutid(&tmpbuf) == NULL) {
#ifdef	ERRDEBUG
		gdebug("1st getutid() failed. fd: %d", fd);
#endif
		_compat_setutent();
		if (_compat_getutid(&tmpbuf) == NULL) {
#ifdef	ERRDEBUG
			loc_utmp = lseek(fd, 0L, 1);
			gdebug("2nd getutid() failed. fd: %d loc_utmp: %ld\n",
			    fd, loc_utmp);
#endif
			(void) fcntl(fd, F_SETFL, fc | O_APPEND);
		} else
			(void) lseek(fd, -(long)sizeof (struct futmp), 1);
	} else
		(void) lseek(fd, -(long)sizeof (struct futmp), 1);

	/*
	 * Write out the user supplied structure.  If the write fails,
	 * then the user probably doesn't have permission to write the
	 * utmp file.
	 */
	if (write(fd, &ftmpbuf, sizeof (ftmpbuf)) != sizeof (ftmpbuf)) {
#ifdef	ERRDEBUG
		gdebug("pututline failed: write-%d\n", errno);
#endif
		answer = NULL;
	} else {
		/*
		 * Copy the new user structure into ubuf so that it will
		 * be up to date in the future.
		 */
		fubuf = ftmpbuf;
		utmp_frec2api(&fubuf, &ubuf);
		answer = &ubuf;

#ifdef	ERRDEBUG
		gdebug("id: %c%c loc: %ld\n", fubuf.ut_id[0],
		    fubuf.ut_id[1], fubuf.ut_id[2], fubuf.ut_id[3],
		    loc_utmp);
#endif
	}

	(void) fcntl(fd, F_SETFL, fc);

	return (answer);
}

/*
 * "_compat_setutent" just resets the utmp file back to the beginning.
 */
void
_compat_setutent(void)
{
	if (fd != -1)
		(void) lseek(fd, 0L, 0);

	/*
	 * Zero the stored copy of the last entry read, since we are
	 * resetting to the beginning of the file.
	 */
	bzero(&ubuf, sizeof (ubuf));
	bzero(&fubuf, sizeof (fubuf));
}

/*
 * "_compat_endutent" closes the utmp file.
 */
void
_compat_endutent(void)
{
	if (fd != -1)
		(void) close(fd);
	fd = -1;
	bzero(&ubuf, sizeof (ubuf));
	bzero(&fubuf, sizeof (fubuf));
}


/*
 * If one of wtmp and wtmpx files exist, create the other, and the record.
 * If they both exist add the record.
 */
void
_compat_updwtmp(const char *file, struct utmp *ut)
{
	struct futmp fut;
	int fd;


	fd = open(file, O_WRONLY | O_APPEND);

	if (fd < 0) {
		if ((fd = open(file, O_WRONLY|O_CREAT, 0644)) < 0)
			return;
	}

	(void) lseek(fd, 0, 2);

	utmp_api2frec(ut, &fut);
	(void) write(fd, &fut, sizeof (fut));

	(void) close(fd);
}



/*
 * makeut - create a utmp entry, recycling an id if a wild card is
 *	specified.
 *
 *	args:	utmp - point to utmp structure to be created
 */
struct utmp *
_compat_makeut(struct utmp *utmp)
{
	int i;
	struct utmp *utp;	/* "current" utmp entry being examined */
	int wild;		/* flag, true iff wild card char seen */

	/* the last id we matched that was NOT a dead proc */
	unsigned char saveid[IDLEN];

	wild = 0;
	for (i = 0; i < IDLEN; i++)
		if ((unsigned char)utmp->ut_id[i] == SC_WILDC) {
			wild = 1;
			break;
		}

	if (wild) {

		/*
		 * try to lock the utmp file, only needed if we're
		 * doing wildcard matching
		 */

		if (lockut())
			return (0);
		_compat_setutent();

		/* find the first alphanumeric character */
		for (i = 0; i < MAXVAL; ++i)
			if (isalnum(i))
				break;

		(void) memset(saveid, i, IDLEN);

		while ((utp = _compat_getutent()) != 0) {
			if (idcmp(utmp->ut_id, utp->ut_id))
				continue;
			if (utp->ut_type == DEAD_PROCESS)
				break;
			(void) memcpy(saveid, utp->ut_id, IDLEN);
		}

		if (utp) {
			/*
			 * found an unused entry, reuse it
			 */
			(void) memcpy(utmp->ut_id, utp->ut_id, IDLEN);
			utp = _compat_pututline(utmp);
			if (utp)
				_compat_updwtmp(WTMP_FILE, utp);
			_compat_endutent();
			unlockut();
			return (utp);

		} else {
			/*
			 * nothing available, try to allocate an id
			 */
			if (allocid(utmp->ut_id, saveid)) {
				_compat_endutent();
				unlockut();
				return (NULL);
			} else {
				utp = _compat_pututline(utmp);
				if (utp)
					_compat_updwtmp(WTMP_FILE, utp);
				_compat_endutent();
				unlockut();
				return (utp);
			}
		}
	} else {
		utp = _compat_pututline(utmp);
		if (utp)
			_compat_updwtmp(WTMP_FILE, utp);
		_compat_endutent();
		return (utp);
	}
}


/*
 * _compat_modut - modify a utmp entry.
 *
 *	args:	utmp - point to utmp structure to be created
 */
struct utmp *
_compat_modut(struct utmp *utp)
{
	int i;					/* scratch variable */
	struct utmp utmp;			/* holding area */
	struct utmp *ucp = &utmp;		/* and a pointer to it */
	struct utmp *up;	/* "current" utmp entry being examined */
	struct futmp *fup;

	for (i = 0; i < IDLEN; ++i)
		if ((unsigned char)utp->ut_id[i] == SC_WILDC)
			return (0);

	/* copy the supplied utmp structure someplace safe */
	utmp = *utp;
	_compat_setutent();
	while (fup = getutent_frec()) {
		if (idcmp(ucp->ut_id, fup->ut_id))
			continue;
		break;
	}
	up = _compat_pututline(ucp);
	if (up)
		_compat_updwtmp(WTMP_FILE, up);
	_compat_endutent();
	return (up);
}



/*
 * idcmp - compare two id strings, return 0 if same, non-zero if not *
 *	args:	s1 - first id string
 *		s2 - second id string
 */
static int
idcmp(const char *s1, const char *s2)
{
	int i;

	for (i = 0; i < IDLEN; ++i)
		if ((unsigned char)*s1 != SC_WILDC && (*s1++ != *s2++))
			return (-1);
	return (0);
}


/*
 * allocid - allocate an unused id for utmp, either by recycling a
 *	DEAD_PROCESS entry or creating a new one.  This routine only
 *	gets called if a wild card character was specified.
 *
 *	args:	srcid - pattern for new id
 *		saveid - last id matching pattern for a non-dead process
 */
static int
allocid(char *srcid, unsigned char *saveid)
{
	int i;		/* scratch variable */
	int changed;	/* flag to indicate that a new id has been generated */
	char copyid[IDLEN];	/* work area */

	(void) memcpy(copyid, srcid, IDLEN);
	changed = 0;
	for (i = 0; i < IDLEN; ++i) {
		/*
		 * if this character isn't wild, it'll
		 * be part of the generated id
		 */
		if ((unsigned char) copyid[i] != SC_WILDC)
			continue;
		/*
		 * it's a wild character, retrieve the
		 * character from the saved id
		 */
		copyid[i] = saveid[i];
		/*
		 * if we haven't changed anything yet,
		 * try to find a new char to use
		 */
		if (!changed && (saveid[i] < MAXVAL)) {

/*
 * Note: this algorithm is taking the "last matched" id and trying to make
 * a 1 character change to it to create a new one.  Rather than special-case
 * the first time (when no perturbation is really necessary), just don't
 * allocate the first valid id.
 */

			while (++saveid[i] < MAXVAL) {
				/* make sure new char is alphanumeric */
				if (isalnum(saveid[i])) {
					copyid[i] = saveid[i];
					changed = 1;
					break;
				}
			}

			if (!changed) {
				/*
				 * Then 'reset' the current count at
				 * this position to it's lowest valid
				 * value, and propagate the carry to
				 * the next wild-card slot
				 *
				 * See 1113208.
				 */
				saveid[i] = 0;
				while (!isalnum(saveid[i]))
					saveid[i]++;
				copyid[i] = ++saveid[i];
			}
		}
	}
	/* changed is true if we were successful in allocating an id */
	if (changed) {
		(void) memcpy(srcid, copyid, IDLEN);
		return (0);
	} else
		return (-1);
}


/*
 * lockut - lock utmp file
 */
static int
lockut(void)
{
	if ((fd = open(_compat_utmpfile, O_RDWR|O_CREAT, 0644)) < 0)
		return (-1);

	if (lockf(fd, F_LOCK, 0) < 0) {
		(void) close(fd);
		fd = -1;
		return (-1);
	}
	return (0);
}


/*
 * unlockut - unlock utmp file
 */
static void
unlockut(void)
{
	(void) lockf(fd, F_ULOCK, 0);
	(void) close(fd);
	fd = -1;
}



#ifdef  ERRDEBUG

#include <stdarg.h>
#include <stdio.h>

static void
gdebug(const char *fmt, ...)
{
	FILE *fp;
	int errnum;
	va_list ap;

	if ((fp = fopen("/etc/dbg.getut", "a+F")) == NULL)
		return;
	va_start(ap, fmt);
	(void) vfprintf(fp, fmt, ap);
	va_end(ap);
	(void) fclose(fp);
}
#endif