2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1988, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A/*
2N/A * Compatibility routines to read and write alternate
2N/A * utmp-like files. These routines are only used in
2N/A * the case where utmpname() is used to change to a file
2N/A * other than /var/adm/utmp or /var/adm/wtmp. In this case,
2N/A * we assume that someone really wants to read old utmp-format
2N/A * files. Otherwise, the getutent, setutent, getutid, setutline,
2N/A * and pututline functions are actually wrappers around the
2N/A * equivalent function operating on utmpx-like files.
2N/A */
2N/A
2N/A#include "lint.h"
2N/A#include <stdio.h>
2N/A#include <sys/param.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <utmpx.h>
2N/A#include <errno.h>
2N/A#include <fcntl.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <ctype.h>
2N/A#include <utime.h>
2N/A#include <sys/wait.h>
2N/A
2N/A#define MAXVAL 255 /* max value for an id 'character' */
2N/A
2N/A#ifdef ut_time
2N/A#undef ut_time
2N/A#endif
2N/A
2N/Astatic void utmp_frec2api(const struct futmp *, struct utmp *);
2N/Astatic void utmp_api2frec(const struct utmp *, struct futmp *);
2N/Astruct utmp *_compat_getutent(void);
2N/Astruct utmp *_compat_getutid(const struct utmp *);
2N/Astruct utmp *_compat_getutline(const struct utmp *);
2N/Astruct utmp *_compat_pututline(const struct utmp *);
2N/Avoid _compat_setutent(void);
2N/Avoid _compat_endutent(void);
2N/Avoid _compat_updwtmp(const char *, struct utmp *);
2N/Astruct utmp *_compat_makeut(struct utmp *);
2N/Astruct utmp *_compat_modut(struct utmp *);
2N/A
2N/Astatic void unlockut(void);
2N/Astatic int idcmp(const char *, const char *);
2N/Astatic int allocid(char *, unsigned char *);
2N/Astatic int lockut(void);
2N/A
2N/A
2N/Astatic int fd = -1; /* File descriptor for the utmp file. */
2N/A/*
2N/A * name of the current utmp-like file - set by utmpname (getutx.c)
2N/A * only if running in backward compatibility mode
2N/A * We don't modify this, but we can't declare it const or lint will freak.
2N/A */
2N/Aextern char _compat_utmpfile[];
2N/A
2N/A#ifdef ERRDEBUG
2N/Astatic long loc_utmp; /* Where in "utmp" the current "ubuf" was found. */
2N/A#endif
2N/A
2N/Astatic struct futmp fubuf; /* Copy of last entry read in. */
2N/Astatic struct utmp ubuf; /* Last entry returned to client */
2N/A
2N/A/*
2N/A * In the 64-bit world, the utmp data structure grows because of
2N/A * the ut_time field (a time_t) at the end of it.
2N/A */
2N/Astatic void
2N/Autmp_frec2api(const struct futmp *src, struct utmp *dst)
2N/A{
2N/A if (src == NULL)
2N/A return;
2N/A
2N/A (void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
2N/A (void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
2N/A (void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
2N/A dst->ut_pid = src->ut_pid;
2N/A dst->ut_type = src->ut_type;
2N/A dst->ut_exit.e_termination = src->ut_exit.e_termination;
2N/A dst->ut_exit.e_exit = src->ut_exit.e_exit;
2N/A dst->ut_time = (time_t)src->ut_time;
2N/A}
2N/A
2N/Astatic void
2N/Autmp_api2frec(const struct utmp *src, struct futmp *dst)
2N/A{
2N/A if (src == NULL)
2N/A return;
2N/A
2N/A (void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
2N/A (void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
2N/A (void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
2N/A dst->ut_pid = src->ut_pid;
2N/A dst->ut_type = src->ut_type;
2N/A dst->ut_exit.e_termination = src->ut_exit.e_termination;
2N/A dst->ut_exit.e_exit = src->ut_exit.e_exit;
2N/A dst->ut_time = (time32_t)src->ut_time;
2N/A}
2N/A
2N/A/*
2N/A * "getutent_frec" gets the raw version of the next entry in the utmp file.
2N/A */
2N/Astatic struct futmp *
2N/Agetutent_frec(void)
2N/A{
2N/A /*
2N/A * If the "utmp" file is not open, attempt to open it for
2N/A * reading. If there is no file, attempt to create one. If
2N/A * both attempts fail, return NULL. If the file exists, but
2N/A * isn't readable and writeable, do not attempt to create.
2N/A */
2N/A if (fd < 0) {
2N/A if ((fd = open(_compat_utmpfile, O_RDWR|O_CREAT, 0644)) < 0) {
2N/A
2N/A /*
2N/A * If the open failed for permissions, try opening
2N/A * it only for reading. All "pututline()" later
2N/A * will fail the writes.
2N/A */
2N/A if ((fd = open(_compat_utmpfile, O_RDONLY)) < 0)
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A /* Try to read in the next entry from the utmp file. */
2N/A
2N/A if (read(fd, &fubuf, sizeof (fubuf)) != sizeof (fubuf)) {
2N/A bzero(&fubuf, sizeof (fubuf));
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Save the location in the file where this entry was found. */
2N/A
2N/A (void) lseek(fd, 0L, 1);
2N/A return (&fubuf);
2N/A}
2N/A
2N/A/*
2N/A * "_compat_getutent" gets the next entry in the utmp file.
2N/A */
2N/Astruct utmp *
2N/A_compat_getutent(void)
2N/A{
2N/A struct futmp *futp;
2N/A
2N/A futp = getutent_frec();
2N/A utmp_frec2api(&fubuf, &ubuf);
2N/A if (futp == NULL)
2N/A return (NULL);
2N/A return (&ubuf);
2N/A}
2N/A
2N/A/*
2N/A * "_compat_getutid" finds the specified entry in the utmp file. If
2N/A * it can't find it, it returns NULL.
2N/A */
2N/Astruct utmp *
2N/A_compat_getutid(const struct utmp *entry)
2N/A{
2N/A short type;
2N/A
2N/A utmp_api2frec(&ubuf, &fubuf);
2N/A
2N/A /*
2N/A * Start looking for entry. Look in our current buffer before
2N/A * reading in new entries.
2N/A */
2N/A do {
2N/A /*
2N/A * If there is no entry in "ubuf", skip to the read.
2N/A */
2N/A if (fubuf.ut_type != EMPTY) {
2N/A switch (entry->ut_type) {
2N/A
2N/A /*
2N/A * Do not look for an entry if the user sent
2N/A * us an EMPTY entry.
2N/A */
2N/A case EMPTY:
2N/A return (NULL);
2N/A
2N/A /*
2N/A * For RUN_LVL, BOOT_TIME, DOWN_TIME,
2N/A * OLD_TIME, and NEW_TIME entries, only the
2N/A * types have to match. If they do, return
2N/A * the address of internal buffer.
2N/A */
2N/A case RUN_LVL:
2N/A case BOOT_TIME:
2N/A case DOWN_TIME:
2N/A case OLD_TIME:
2N/A case NEW_TIME:
2N/A if (entry->ut_type == fubuf.ut_type) {
2N/A utmp_frec2api(&fubuf, &ubuf);
2N/A return (&ubuf);
2N/A }
2N/A break;
2N/A
2N/A /*
2N/A * For INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS,
2N/A * and DEAD_PROCESS the type of the entry in "fubuf",
2N/A * must be one of the above and id's must match.
2N/A */
2N/A case INIT_PROCESS:
2N/A case LOGIN_PROCESS:
2N/A case USER_PROCESS:
2N/A case DEAD_PROCESS:
2N/A if (((type = fubuf.ut_type) == INIT_PROCESS ||
2N/A type == LOGIN_PROCESS ||
2N/A type == USER_PROCESS ||
2N/A type == DEAD_PROCESS) &&
2N/A fubuf.ut_id[0] == entry->ut_id[0] &&
2N/A fubuf.ut_id[1] == entry->ut_id[1] &&
2N/A fubuf.ut_id[2] == entry->ut_id[2] &&
2N/A fubuf.ut_id[3] == entry->ut_id[3]) {
2N/A utmp_frec2api(&fubuf, &ubuf);
2N/A return (&ubuf);
2N/A }
2N/A break;
2N/A
2N/A /* Do not search for illegal types of entry. */
2N/A default:
2N/A return (NULL);
2N/A }
2N/A }
2N/A } while (getutent_frec() != NULL);
2N/A
2N/A /* the proper entry wasn't found. */
2N/A
2N/A utmp_frec2api(&fubuf, &ubuf);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * "_compat_getutline" searches the "utmp" file for a LOGIN_PROCESS or
2N/A * USER_PROCESS with the same "line" as the specified "entry".
2N/A */
2N/Astruct utmp *
2N/A_compat_getutline(const struct utmp *entry)
2N/A{
2N/A utmp_api2frec(&ubuf, &fubuf);
2N/A
2N/A do {
2N/A /*
2N/A * If the current entry is the one we are interested in,
2N/A * return a pointer to it.
2N/A */
2N/A if (fubuf.ut_type != EMPTY &&
2N/A (fubuf.ut_type == LOGIN_PROCESS ||
2N/A fubuf.ut_type == USER_PROCESS) &&
2N/A strncmp(&entry->ut_line[0], &fubuf.ut_line[0],
2N/A sizeof (fubuf.ut_line)) == 0) {
2N/A utmp_frec2api(&fubuf, &ubuf);
2N/A return (&ubuf);
2N/A }
2N/A } while (getutent_frec() != NULL);
2N/A
2N/A utmp_frec2api(&fubuf, &ubuf);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * "_compat_pututline" writes the structure sent into the utmp file
2N/A * If there is already an entry with the same id, then it is
2N/A * overwritten, otherwise a new entry is made at the end of the
2N/A * utmp file.
2N/A */
2N/Astruct utmp *
2N/A_compat_pututline(const struct utmp *entry)
2N/A{
2N/A int fc;
2N/A struct utmp *answer;
2N/A struct utmp tmpbuf;
2N/A struct futmp ftmpbuf;
2N/A
2N/A /*
2N/A * Copy the user supplied entry into our temporary buffer to
2N/A * avoid the possibility that the user is actually passing us
2N/A * the address of "ubuf".
2N/A */
2N/A tmpbuf = *entry;
2N/A utmp_api2frec(entry, &ftmpbuf);
2N/A
2N/A (void) getutent_frec();
2N/A if (fd < 0) {
2N/A#ifdef ERRDEBUG
2N/A gdebug("pututline: Unable to create utmp file.\n");
2N/A#endif
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Make sure file is writable */
2N/A
2N/A if ((fc = fcntl(fd, F_GETFL, NULL)) == -1 || (fc & O_RDWR) != O_RDWR)
2N/A return (NULL);
2N/A
2N/A /*
2N/A * Find the proper entry in the utmp file. Start at the current
2N/A * location. If it isn't found from here to the end of the
2N/A * file, then reset to the beginning of the file and try again.
2N/A * If it still isn't found, then write a new entry at the end of
2N/A * the file. (Making sure the location is an integral number of
2N/A * utmp structures into the file incase the file is scribbled.)
2N/A */
2N/A
2N/A if (_compat_getutid(&tmpbuf) == NULL) {
2N/A#ifdef ERRDEBUG
2N/A gdebug("1st getutid() failed. fd: %d", fd);
2N/A#endif
2N/A _compat_setutent();
2N/A if (_compat_getutid(&tmpbuf) == NULL) {
2N/A#ifdef ERRDEBUG
2N/A loc_utmp = lseek(fd, 0L, 1);
2N/A gdebug("2nd getutid() failed. fd: %d loc_utmp: %ld\n",
2N/A fd, loc_utmp);
2N/A#endif
2N/A (void) fcntl(fd, F_SETFL, fc | O_APPEND);
2N/A } else
2N/A (void) lseek(fd, -(long)sizeof (struct futmp), 1);
2N/A } else
2N/A (void) lseek(fd, -(long)sizeof (struct futmp), 1);
2N/A
2N/A /*
2N/A * Write out the user supplied structure. If the write fails,
2N/A * then the user probably doesn't have permission to write the
2N/A * utmp file.
2N/A */
2N/A if (write(fd, &ftmpbuf, sizeof (ftmpbuf)) != sizeof (ftmpbuf)) {
2N/A#ifdef ERRDEBUG
2N/A gdebug("pututline failed: write-%d\n", errno);
2N/A#endif
2N/A answer = NULL;
2N/A } else {
2N/A /*
2N/A * Copy the new user structure into ubuf so that it will
2N/A * be up to date in the future.
2N/A */
2N/A fubuf = ftmpbuf;
2N/A utmp_frec2api(&fubuf, &ubuf);
2N/A answer = &ubuf;
2N/A
2N/A#ifdef ERRDEBUG
2N/A gdebug("id: %c%c loc: %ld\n", fubuf.ut_id[0],
2N/A fubuf.ut_id[1], fubuf.ut_id[2], fubuf.ut_id[3],
2N/A loc_utmp);
2N/A#endif
2N/A }
2N/A
2N/A (void) fcntl(fd, F_SETFL, fc);
2N/A
2N/A return (answer);
2N/A}
2N/A
2N/A/*
2N/A * "_compat_setutent" just resets the utmp file back to the beginning.
2N/A */
2N/Avoid
2N/A_compat_setutent(void)
2N/A{
2N/A if (fd != -1)
2N/A (void) lseek(fd, 0L, 0);
2N/A
2N/A /*
2N/A * Zero the stored copy of the last entry read, since we are
2N/A * resetting to the beginning of the file.
2N/A */
2N/A bzero(&ubuf, sizeof (ubuf));
2N/A bzero(&fubuf, sizeof (fubuf));
2N/A}
2N/A
2N/A/*
2N/A * "_compat_endutent" closes the utmp file.
2N/A */
2N/Avoid
2N/A_compat_endutent(void)
2N/A{
2N/A if (fd != -1)
2N/A (void) close(fd);
2N/A fd = -1;
2N/A bzero(&ubuf, sizeof (ubuf));
2N/A bzero(&fubuf, sizeof (fubuf));
2N/A}
2N/A
2N/A
2N/A/*
2N/A * If one of wtmp and wtmpx files exist, create the other, and the record.
2N/A * If they both exist add the record.
2N/A */
2N/Avoid
2N/A_compat_updwtmp(const char *file, struct utmp *ut)
2N/A{
2N/A struct futmp fut;
2N/A int fd;
2N/A
2N/A
2N/A fd = open(file, O_WRONLY | O_APPEND);
2N/A
2N/A if (fd < 0) {
2N/A if ((fd = open(file, O_WRONLY|O_CREAT, 0644)) < 0)
2N/A return;
2N/A }
2N/A
2N/A (void) lseek(fd, 0, 2);
2N/A
2N/A utmp_api2frec(ut, &fut);
2N/A (void) write(fd, &fut, sizeof (fut));
2N/A
2N/A (void) close(fd);
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * makeut - create a utmp entry, recycling an id if a wild card is
2N/A * specified.
2N/A *
2N/A * args: utmp - point to utmp structure to be created
2N/A */
2N/Astruct utmp *
2N/A_compat_makeut(struct utmp *utmp)
2N/A{
2N/A int i;
2N/A struct utmp *utp; /* "current" utmp entry being examined */
2N/A int wild; /* flag, true iff wild card char seen */
2N/A
2N/A /* the last id we matched that was NOT a dead proc */
2N/A unsigned char saveid[_UTMP_ID_LEN];
2N/A
2N/A wild = 0;
2N/A for (i = 0; i < _UTMP_ID_LEN; i++)
2N/A if (utmp->ut_id[i] == _UTMP_ID_WILDCARD) {
2N/A wild = 1;
2N/A break;
2N/A }
2N/A
2N/A if (wild) {
2N/A
2N/A /*
2N/A * try to lock the utmp file, only needed if we're
2N/A * doing wildcard matching
2N/A */
2N/A
2N/A if (lockut())
2N/A return (0);
2N/A _compat_setutent();
2N/A
2N/A /* find the first alphanumeric character */
2N/A for (i = 0; i < MAXVAL; ++i)
2N/A if (isalnum(i))
2N/A break;
2N/A
2N/A (void) memset(saveid, i, _UTMP_ID_LEN);
2N/A
2N/A while ((utp = _compat_getutent()) != 0) {
2N/A if (idcmp(utmp->ut_id, utp->ut_id))
2N/A continue;
2N/A if (utp->ut_type == DEAD_PROCESS)
2N/A break;
2N/A (void) memcpy(saveid, utp->ut_id, _UTMP_ID_LEN);
2N/A }
2N/A
2N/A if (utp) {
2N/A /*
2N/A * found an unused entry, reuse it
2N/A */
2N/A (void) memcpy(utmp->ut_id, utp->ut_id, _UTMP_ID_LEN);
2N/A utp = _compat_pututline(utmp);
2N/A if (utp)
2N/A _compat_updwtmp(WTMP_FILE, utp);
2N/A _compat_endutent();
2N/A unlockut();
2N/A return (utp);
2N/A
2N/A } else {
2N/A /*
2N/A * nothing available, try to allocate an id
2N/A */
2N/A if (allocid(utmp->ut_id, saveid)) {
2N/A _compat_endutent();
2N/A unlockut();
2N/A return (NULL);
2N/A } else {
2N/A utp = _compat_pututline(utmp);
2N/A if (utp)
2N/A _compat_updwtmp(WTMP_FILE, utp);
2N/A _compat_endutent();
2N/A unlockut();
2N/A return (utp);
2N/A }
2N/A }
2N/A } else {
2N/A utp = _compat_pututline(utmp);
2N/A if (utp)
2N/A _compat_updwtmp(WTMP_FILE, utp);
2N/A _compat_endutent();
2N/A return (utp);
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * _compat_modut - modify a utmp entry.
2N/A *
2N/A * args: utmp - point to utmp structure to be created
2N/A */
2N/Astruct utmp *
2N/A_compat_modut(struct utmp *utp)
2N/A{
2N/A int i; /* scratch variable */
2N/A struct utmp utmp; /* holding area */
2N/A struct utmp *ucp = &utmp; /* and a pointer to it */
2N/A struct utmp *up; /* "current" utmp entry being examined */
2N/A struct futmp *fup;
2N/A
2N/A for (i = 0; i < _UTMP_ID_LEN; ++i)
2N/A if (utp->ut_id[i] == _UTMP_ID_WILDCARD)
2N/A return (0);
2N/A
2N/A /* copy the supplied utmp structure someplace safe */
2N/A utmp = *utp;
2N/A _compat_setutent();
2N/A while (fup = getutent_frec()) {
2N/A if (idcmp(ucp->ut_id, fup->ut_id))
2N/A continue;
2N/A break;
2N/A }
2N/A up = _compat_pututline(ucp);
2N/A if (up)
2N/A _compat_updwtmp(WTMP_FILE, up);
2N/A _compat_endutent();
2N/A return (up);
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * idcmp - compare two id strings, return 0 if same, non-zero if not *
2N/A * args: s1 - first id string
2N/A * s2 - second id string
2N/A */
2N/Astatic int
2N/Aidcmp(const char *s1, const char *s2)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < _UTMP_ID_LEN; ++i)
2N/A if (*s1 != _UTMP_ID_WILDCARD && (*s1++ != *s2++))
2N/A return (-1);
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * allocid - allocate an unused id for utmp, either by recycling a
2N/A * DEAD_PROCESS entry or creating a new one. This routine only
2N/A * gets called if a wild card character was specified.
2N/A *
2N/A * args: srcid - pattern for new id
2N/A * saveid - last id matching pattern for a non-dead process
2N/A */
2N/Astatic int
2N/Aallocid(char *srcid, unsigned char *saveid)
2N/A{
2N/A int i; /* scratch variable */
2N/A int changed; /* flag to indicate that a new id has been generated */
2N/A char copyid[_UTMP_ID_LEN]; /* work area */
2N/A
2N/A (void) memcpy(copyid, srcid, _UTMP_ID_LEN);
2N/A changed = 0;
2N/A for (i = 0; i < _UTMP_ID_LEN; ++i) {
2N/A /*
2N/A * if this character isn't wild, it'll
2N/A * be part of the generated id
2N/A */
2N/A if (copyid[i] != _UTMP_ID_WILDCARD)
2N/A continue;
2N/A /*
2N/A * it's a wild character, retrieve the
2N/A * character from the saved id
2N/A */
2N/A copyid[i] = saveid[i];
2N/A /*
2N/A * if we haven't changed anything yet,
2N/A * try to find a new char to use
2N/A */
2N/A if (!changed && (saveid[i] < MAXVAL)) {
2N/A
2N/A/*
2N/A * Note: this algorithm is taking the "last matched" id and trying to make
2N/A * a 1 character change to it to create a new one. Rather than special-case
2N/A * the first time (when no perturbation is really necessary), just don't
2N/A * allocate the first valid id.
2N/A */
2N/A
2N/A while (++saveid[i] < MAXVAL) {
2N/A /* make sure new char is alphanumeric */
2N/A if (isalnum(saveid[i])) {
2N/A copyid[i] = saveid[i];
2N/A changed = 1;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (!changed) {
2N/A /*
2N/A * Then 'reset' the current count at
2N/A * this position to it's lowest valid
2N/A * value, and propagate the carry to
2N/A * the next wild-card slot
2N/A *
2N/A * See 1113208.
2N/A */
2N/A saveid[i] = 0;
2N/A while (!isalnum(saveid[i]))
2N/A saveid[i]++;
2N/A copyid[i] = ++saveid[i];
2N/A }
2N/A }
2N/A }
2N/A /* changed is true if we were successful in allocating an id */
2N/A if (changed) {
2N/A (void) memcpy(srcid, copyid, _UTMP_ID_LEN);
2N/A return (0);
2N/A } else
2N/A return (-1);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * lockut - lock utmp file
2N/A */
2N/Astatic int
2N/Alockut(void)
2N/A{
2N/A if ((fd = open(_compat_utmpfile, O_RDWR|O_CREAT, 0644)) < 0)
2N/A return (-1);
2N/A
2N/A if (lockf(fd, F_LOCK, 0) < 0) {
2N/A (void) close(fd);
2N/A fd = -1;
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * unlockut - unlock utmp file
2N/A */
2N/Astatic void
2N/Aunlockut(void)
2N/A{
2N/A (void) lockf(fd, F_ULOCK, 0);
2N/A (void) close(fd);
2N/A fd = -1;
2N/A}
2N/A
2N/A
2N/A
2N/A#ifdef ERRDEBUG
2N/A
2N/A#include <stdarg.h>
2N/A#include <stdio.h>
2N/A
2N/Astatic void
2N/Agdebug(const char *fmt, ...)
2N/A{
2N/A FILE *fp;
2N/A int errnum;
2N/A va_list ap;
2N/A
2N/A if ((fp = fopen("/etc/dbg.getut", "a+F")) == NULL)
2N/A return;
2N/A va_start(ap, fmt);
2N/A (void) vfprintf(fp, fmt, ap);
2N/A va_end(ap);
2N/A (void) fclose(fp);
2N/A}
2N/A#endif