/*
* 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
* 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 (c) 1988 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
/*
* Routines to read and write the UTMPX_FILE file. Also contains
* binary compatibility routines to support the old utmp interfaces
* on systems with MAXPID <= SHRT_MAX.
*/
#include "lint.h"
#include <stdio.h>
#include <utmpx.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <paths.h>
#include <pthread.h>
#include <limits.h>
#include <signal.h>
#include <spawn.h>
/* old file only, for sanity check */
/*
* format of message sent to init
*/
typedef struct pidrec {
} pidrec_t;
/*
* pd_type's
*/
static void unlockutx(void);
static int idcmp(const char *, const char *);
static int allocid(char *, unsigned char *);
static int lockutx(void);
static int big_pids_in_use(void);
/*
* prototypes for utmp compatibility routines (in getut.c)
*/
extern struct utmp *_compat_getutent(void);
extern void _compat_setutent(void);
extern void _compat_endutent(void);
extern void _compat_updwtmp(const char *, struct utmp *);
/*
* In the 64-bit world, the utmpx data structure grows because of
* the ut_time field (a struct timeval) grows in the middle of it.
*/
static void
{
return;
}
static void
{
return;
}
/*
* "getutxent_frec" gets the raw version of the next entry in the utmpx file.
*/
static struct futmpx *
getutxent_frec(void)
{
/*
* If the "utmpx" 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 the open failed for permissions, try opening
* it only for reading. All "pututxline()" later
* will fail the writes.
*/
return (NULL);
fd = -1;
return (NULL);
}
} else {
/*
* Get the stream pointer
*/
fd = -1;
return (NULL);
}
}
}
/*
* Try to read in the next entry from the utmpx file.
*/
/*
* Make sure fubuf is zeroed.
*/
return (NULL);
}
return (&fubuf);
}
/*
* "big_pids_in_use" determines whether large pid numbers are in use
* or not. If MAXPID won't fit in a signed short, the utmp.ut_pid
* field will overflow.
*
* Returns 0 if small pids are in use, 1 otherwise
*/
static int
big_pids_in_use(void)
{
if (!ut_got_maxpid) {
}
}
/*
* "getutxent" gets the next entry in the utmpx file.
*/
struct utmpx *
getutxent(void)
{
futxp = getutxent_frec();
return (NULL);
return (&ubuf);
}
/*
* "getutent" gets the next entry in the utmp file.
*/
struct utmp *
getutent(void)
{
if (compat_utmpflag)
return (_compat_getutent());
/* fail if we can't represent maxpid properly */
if (big_pids_in_use()) {
return (NULL);
}
return (NULL);
return (&utmpcompat);
}
/*
* "getutxid" finds the specified entry in the utmpx file. If
* it can't find it, it returns NULL.
*/
struct utmpx *
{
short type;
/*
* From XPG5: "The getutxid() or getutxline() may cache data.
* For this reason, to use getutxline() to search for multiple
* occurrences, it is necessary to zero out the static data after
* each success, or getutxline() could just return a pointer to
* the same utmpx structure over and over again."
*/
/*
* Start looking for entry. Look in our current buffer before
* reading in new entries.
*/
do {
/*
* If there is no entry in "fubuf", skip to the read.
*/
/*
* Do not look for an entry if the user sent
* us an EMPTY entry.
*/
case EMPTY:
return (NULL);
/*
* For RUN_LVL, BOOT_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:
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:
type == LOGIN_PROCESS ||
type == USER_PROCESS ||
type == DEAD_PROCESS) &&
return (&ubuf);
}
break;
/*
* Do not search for illegal types of entry.
*/
default:
return (NULL);
}
}
} while (getutxent_frec() != NULL);
/*
* Return NULL since the proper entry wasn't found.
*/
return (NULL);
}
/*
* "getutid" finds the specified entry in the utmp file. If
* it can't find it, it returns NULL.
*/
struct utmp *
{
if (compat_utmpflag)
return (_compat_getutid(entry));
/* fail if we can't represent maxpid properly */
if (big_pids_in_use()) {
return (NULL);
}
return (NULL);
return (&utmpcompat);
}
/*
* "getutxline" searches the "utmpx" file for a LOGIN_PROCESS or
* USER_PROCESS with the same "line" as the specified "entry".
*/
struct utmpx *
{
/*
* From XPG5: "The getutxid() or getutxline() may cache data.
* For this reason, to use getutxline() to search for multiple
* occurrences, it is necessary to zero out the static data after
* each success, or getutxline() could just return a pointer to
* the same utmpx structure over and over again."
*/
do {
/*
* If the current entry is the one we are interested in,
* return a pointer to it.
*/
return (&ubuf);
}
} while (getutxent_frec() != NULL);
/*
* Since entry wasn't found, return NULL.
*/
return (NULL);
}
/*
* "getutline" searches the "utmp" file for a LOGIN_PROCESS or
* USER_PROCESS with the same "line" as the specified "entry".
*/
struct utmp *
{
if (compat_utmpflag)
return (_compat_getutline(entry));
/* fail if we can't represent maxpid properly */
if (big_pids_in_use()) {
return (NULL);
}
/* call getutxline */
return (NULL);
return (&utmpcompat);
}
/*
* invoke_utmp_update
*
* Invokes the utmp_update program which has the privilege to write
*/
static struct utmpx *
{
extern char **_environ;
int status;
int cancel_state;
pid_t w;
int i;
unsigned char *cp;
int error;
/*
* Convert the utmp struct to strings for command line arguments.
*/
}
argvec[0] = UTMP_UPDATE;
/*
* No SIGCHLD, please, and let no one else reap our child.
*/
if (error) {
goto out;
}
if (error) {
(void) posix_spawnattr_destroy(&attr);
goto out;
}
(void) posix_spawnattr_destroy(&attr);
if (error) {
goto out;
}
do {
/*
* We can get ECHILD if the process is ignoring SIGCLD.
*/
/*
* The child encountered an error,
*/
goto out;
}
/*
* Normal termination. Return a pointer to the entry we just made.
*/
setutxent(); /* Reset file pointer */
break;
}
out:
return (curx);
}
/*
* "pututxline" writes the structure sent into the utmpx 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
* utmpx file.
*/
struct utmpx *
{
int lock = 0;
/*
* Copy the user supplied entry into our temporary buffer to
* avoid the possibility that the user is actually passing us
* the address of "ubuf".
*/
return (NULL);
if (fd < 0) {
(void) getutxent_frec();
if (fd < 0)
}
/*
* so invoke update_utmp(8) to write the entry for us.
*/
if (changed_name == 0 && geteuid() != 0)
return (invoke_utmp_update(entry));
/*
* Find the proper entry in the utmpx 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.)
*/
setutxent();
/*
* Lock the the entire file from here onwards.
*/
lock++;
return (NULL);
} else
SEEK_CUR);
} else
/*
* Write out the user supplied structure. If the write fails,
* then the user probably doesn't have permission to write the
* utmpx file.
*/
} else {
/*
* Save the new user structure into ubuf and fubuf so that
* it will be up to date in the future.
*/
}
if (lock)
return (answer);
}
/*
* "pututline" is a wrapper that calls pututxline after converting
* the utmp record to a utmpx record.
*/
struct utmp *
{
if (compat_utmpflag)
return (_compat_pututline(entry));
return (NULL);
return (&utmpcompat);
}
/*
* "setutxent" just resets the utmpx file back to the beginning.
*/
void
setutxent(void)
{
if (fd != -1)
/*
* Zero the stored copy of the last entry read, since we are
* resetting to the beginning of the file.
*/
}
/*
* "setutent" is a wrapper that calls setutxent
*/
void
setutent(void)
{
if (compat_utmpflag) {
return;
}
setutxent();
}
/*
* "endutxent" closes the utmpx file.
*/
void
endutxent(void)
{
if (fd != -1)
fd = -1;
}
/*
* "endutent" is a wrapper that calls endutxent
* and clears the utmp compatibility buffer.
*/
void
endutent(void)
{
if (compat_utmpflag) {
return;
}
endutxent();
}
/*
* "utmpxname" allows the user to read a file other than the
* normal "utmpx" file.
*/
int
{
/*
* Determine if the new filename will fit. If not, return 0.
*/
return (0);
/*
* The name of the utmpx file has to end with 'x'
*/
return (0);
/*
* Otherwise copy in the new file name.
*/
else
/*
* Make sure everything is reset to the beginning state.
*/
endutxent();
/*
* utmp_update. Otherwise we set the flag indicating that they
* changed to another name.
*/
changed_name = 0;
else
changed_name = 1;
return (1);
}
/*
* "utmpname" allows the user to read a file other than the
* format name, and all "utmp" operations become wrapped calls
* to the equivalent "utmpx" routines, with data conversions
* as appropriate. In the event the application wishes to read
* calling this function with that name enables backward compatibility
* mode, where we actually call the old utmp routines to operate on
* the old file.
*/
int
{
return (0);
compat_utmpflag = 0; /* turn off old compat mode */
} else {
compat_utmpflag = 1;
return (1);
}
}
/*
* Add the record to wtmpx.
*/
void
{
int wfdx;
return;
done:
}
/*
* use the old utmp compatibility routine to write a utmp-format
* record to the file specified.
*/
void
{
} else
}
/*
* modutx - modify a utmpx entry. Also notify init about new pids or
* old pids that it no longer needs to care about
*
* args: utp- point to utmpx structure to be created
*/
struct utmpx *
{
int i;
for (i = 0; i < _UTMP_ID_LEN; ++i) {
return (NULL);
}
/*
* copy the supplied utmpx structure someplace safe
*/
setutxent();
while (fup = getutxent_frec()) {
continue;
/*
* only get here if ids are the same, i.e. found right entry
*/
}
break;
}
if (up)
endutxent();
return (up);
}
/*
* modut - modify a utmp entry. Also notify init about new pids or
* old pids that it no longer needs to care about
*
* args: utmp - point to utmp structure to be created
*/
struct utmp *
{
return (NULL);
return (utp);
}
/*
* idcmp - compare two id strings, return 0 if same, non-zero if not *
* args: s1 - first id string
* s2 - second id string
*/
static int
{
int i;
for (i = 0; i < _UTMP_ID_LEN; ++i)
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
{
int i; /* scratch variable */
/* been generated */
changed = 0;
for (i = 0; i < _UTMP_ID_LEN; ++i) {
/*
* if this character isn't wild, it'll be part of the
* generated id
*/
if (copyid[i] != _UTMP_ID_WILDCARD)
continue;
/*
* it's a wild character, retrieve the character from the
* saved id
*/
/*
* if we haven't changed anything yet, try to find a new char
* to use
*/
/*
* 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.
*/
/*
* make sure new char is alphanumeric
*/
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;
saveid[i]++;
}
}
}
/*
* changed is true if we were successful in allocating an id
*/
if (changed) {
return (0);
} else {
return (-1);
}
}
/*
* lockutx - lock utmpx file
*/
static int
lockutx(void)
{
int lockfd;
/* We need to use the same file when we lock. */
return (-1);
return (-1);
}
return (0);
}
/*
* unlockutx - unlock utmpx file
*/
static void
unlockutx(void)
{
}
/*
* sendpid - send message to init to add or remove a pid from the
* "godchild" list
*
* args: cmd - ADDPID or REMPID
* pid - pid of "godchild"
*/
static void
{
/*
* here to avoid sending SIGPIPE to the calling process
*/
if (pfd < 0)
return;
}
/*
* makeutx - create a utmpx entry, recycling an id if a wild card is
* specified. Also notify init about the new pid
*
* args: utmpx - point to utmpx structure to be created
*/
struct utmpx *
{
/* was NOT a dead proc */
/*
* Are any wild card char's present in the idlen string?
*/
/*
* try to lock the utmpx file, only needed if
* we're doing wildcard matching
*/
if (lockutx())
return (NULL);
/*
* used in allocid
*/
continue;
} else {
/*
* Found a match. We are done if this is
* a free slot. Else record this id. We
* will need it to generate the next new id.
*/
break;
else
}
if (ut) {
/*
* Unused entry, reuse it. We know the offset. So
* just go to that offset utmpx and write it out.
*/
unlockutx();
} else {
/*
* nothing available, allocate an id and
* write it out at the end.
*/
unlockutx();
return (NULL);
} else {
/*
* Seek to end and write out the entry
* and also update the utmpx file.
*/
unlockutx();
}
}
} else {
if (utp)
endutxent();
return (utp);
}
}
/*
* makeut - create a utmp entry, recycling an id if a wild card is
* specified. Also notify init about the new pid
*
* args: utmp - point to utmp structure to be created
*/
struct utmp *
{
if (compat_utmpflag)
return (_compat_makeut(utmp));
return (NULL);
return (utmp);
}
/*
* Buffered read routine to get one entry from utmpx file
*/
static struct futmpx *
{
perror("malloc");
return (NULL);
}
/*
* We have read all entries in the utmpbuf. Read
* the buffer from the disk.
*/
/*
* Partial read only. keep count of the
* number of valid entries in the buffer
*/
} else {
/*
* We read in the full UTMPNBUF entries
* Great !
*/
}
nbuf++; /* Number of buf we have read in. */
idx = 0; /* reset index within utmpbuf */
}
/*
* Current offset of this buffer in the file
*/
/*
* We still have at least one valid buffer in
* utmpbuf to be passed to the caller.
*/
}
/*
* Reached EOF. Return NULL. Offset is set correctly
* to append at the end of the file
*/
return (NULL);
}
static void
{
}
/*
* sendupid - send message to utmpd to add or remove a pid from the
* list of procs to watch
*
* args: cmd - ADDPID or REMPID
* pid - process ID of process to watch
*/
static void
{
/*
* here to avoid sending SIGPIPE to the calling process
*/
if (pfd < 0)
return;
}
/*
* getutmpx - convert a utmp record into a utmpx record
*/
void
{
utx->ut_session = 0;
}
/*
* getutmp - convert a utmpx record into a utmp record
*/
void
{
}