diff -r 000000000000 -r 68f95e015346 usr/src/lib/libc/port/gen/getutx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/lib/libc/port/gen/getutx.c Tue Jun 14 00:00:00 2005 -0700 @@ -0,0 +1,1407 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Routines to read and write the /etc/utmpx file. Also contains + * binary compatibility routines to support the old utmp interfaces + * on systems with MAXPID <= SHRT_MAX. + */ + +#pragma weak getutxent = _getutxent +#pragma weak getutent = _getutent +#pragma weak getutxid = _getutxid +#pragma weak getutid = _getutid +#pragma weak getutxline = _getutxline +#pragma weak getutline = _getutline +#pragma weak getutmpx = _getutmpx +#pragma weak getutmp = _getutmp +#pragma weak makeutx = _makeutx +#pragma weak makeut = _makeut +#pragma weak modutx = _modutx +#pragma weak modut = _modut +#pragma weak pututxline = _pututxline +#pragma weak pututline = _pututline +#pragma weak setutxent = _setutxent +#pragma weak setutent = _setutent +#pragma weak endutxent = _endutxent +#pragma weak endutent = _endutent +#pragma weak utmpxname = _utmpxname +#pragma weak utmpname = _utmpname +#pragma weak updwtmpx = _updwtmpx +#pragma weak updwtmp = _updwtmp + +#include "synonyms.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IDLEN 4 /* length of id field in utmp */ +#define SC_WILDC 0xff /* wild char for utmp ids */ +#define MAXFILE 79 /* Maximum pathname length for "utmpx" file */ + +#define MAXVAL 255 /* max value for an id `character' */ +#define IPIPE "/etc/initpipe" /* FIFO to send pids to init */ +#define UPIPE "/etc/utmppipe" /* FIFO to send pids to utmpd */ + +#define VAR_UTMPX_FILE "/var/adm/utmpx" /* for sanity check only */ + + +/* + * format of message sent to init + */ + +typedef struct pidrec { + int pd_type; /* command type */ + pid_t pd_pid; /* pid */ +} pidrec_t; + +/* + * pd_type's + */ +#define ADDPID 1 /* add a pid to "godchild" list */ +#define REMPID 2 /* remove a pid to "godchild" list */ + +static void utmpx_frec2api(const struct futmpx *, struct utmpx *); +static void utmpx_api2frec(const struct utmpx *, struct futmpx *); + +static void unlockutx(void); +static void sendpid(int, pid_t); +static void sendupid(int, pid_t); +static int idcmp(const char *, const char *); +static int allocid(char *, unsigned char *); +static int lockutx(void); + +static struct utmpx *invoke_utmp_update(const struct utmpx *); +static struct futmpx *getoneutx(off_t *); +static void putoneutx(const struct utmpx *, off_t); +static int big_pids_in_use(void); + +/* + * prototypes for utmp compatibility routines (in getut.c) + */ +extern struct utmp *_compat_getutent(void); +extern struct utmp *_compat_getutid(const struct utmp *); +extern struct utmp *_compat_getutline(const struct utmp *); +extern struct utmp *_compat_pututline(const struct utmp *); +extern void _compat_setutent(void); +extern void _compat_endutent(void); +extern void _compat_updwtmp(const char *, struct utmp *); +extern struct utmp *_compat_makeut(struct utmp *); + +static int fd = -1; /* File descriptor for the utmpx file. */ +static int ut_got_maxpid = 0; /* Flag set when sysconf(_SC_MAXPID) called */ +static pid_t ut_maxpid = 0; /* Value of MAXPID from sysconf */ +static int tempfd = -1; /* To store fd between lockutx() and unlockutx() */ + +static FILE *fp = NULL; /* Buffered file descriptior for utmpx file */ +static int changed_name = 0; /* Flag set when not using utmpx file */ +static char utmpxfile[MAXFILE+1] = UTMPX_FILE; /* Name of the current */ +char _compat_utmpfile[MAXFILE+1]; +static int compat_utmpflag = 0; /* old compat mode flag */ + +static struct futmpx fubuf; /* Copy of last entry read in. */ +static struct utmpx ubuf; /* Last entry returned to client */ + +static struct utmp utmpcompat; /* Buffer for returning utmp-format data */ +/* + * 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 +utmpx_frec2api(const struct futmpx *src, struct utmpx *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_tv.tv_sec = (time_t)src->ut_tv.tv_sec; + dst->ut_tv.tv_usec = (suseconds_t)src->ut_tv.tv_usec; + dst->ut_session = src->ut_session; + bzero(dst->pad, sizeof (dst->pad)); + dst->ut_syslen = src->ut_syslen; + (void) memcpy(dst->ut_host, src->ut_host, sizeof (dst->ut_host)); +} + +static void +utmpx_api2frec(const struct utmpx *src, struct futmpx *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_tv.tv_sec = (time32_t)src->ut_tv.tv_sec; + dst->ut_tv.tv_usec = (int32_t)src->ut_tv.tv_usec; + dst->ut_session = src->ut_session; + bzero(dst->pad, sizeof (dst->pad)); + dst->ut_syslen = src->ut_syslen; + (void) memcpy(dst->ut_host, src->ut_host, sizeof (dst->ut_host)); +} + +/* + * "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 ((fd = open(utmpxfile, O_RDWR|O_CREAT, 0644)) < 0) { + + /* + * If the open failed for permissions, try opening + * it only for reading. All "pututxline()" later + * will fail the writes. + */ + + if ((fd = open(utmpxfile, O_RDONLY)) < 0) + return (NULL); + + if ((fp = fopen(utmpxfile, "r")) == NULL) { + (void) close(fd); + fd = -1; + return (NULL); + } + + } else { + /* + * Get the stream pointer + */ + if ((fp = fopen(utmpxfile, "r+")) == NULL) { + (void) close(fd); + fd = -1; + return (NULL); + } + } + } + + /* + * Try to read in the next entry from the utmpx file. + */ + if (fread(&fubuf, sizeof (fubuf), 1, fp) != 1) { + /* + * Make sure fubuf is zeroed. + */ + bzero(&fubuf, sizeof (fubuf)); + 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) { + ut_got_maxpid++; + ut_maxpid = sysconf(_SC_MAXPID); + } + return (ut_maxpid > SHRT_MAX ? 1 : 0); +} + +/* + * "getutxent" gets the next entry in the utmpx file. + */ +struct utmpx * +getutxent(void) +{ + struct futmpx *futxp; + + futxp = getutxent_frec(); + utmpx_frec2api(&fubuf, &ubuf); + if (futxp == NULL) + return (NULL); + return (&ubuf); +} +/* + * "getutent" gets the next entry in the utmp file. + */ +struct utmp * +getutent(void) +{ + struct utmpx *utmpx; + + if (compat_utmpflag) + return (_compat_getutent()); + + /* fail if we can't represent maxpid properly */ + if (big_pids_in_use()) { + errno = EOVERFLOW; + return (NULL); + } + + if ((utmpx = getutxent()) == NULL) + return (NULL); + + getutmp(utmpx, &utmpcompat); + return (&utmpcompat); +} + +/* + * "getutxid" finds the specified entry in the utmpx file. If + * it can't find it, it returns NULL. + */ +struct utmpx * +getutxid(const struct utmpx *entry) +{ + 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." + */ + utmpx_api2frec(&ubuf, &fubuf); + + /* + * 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. + */ + 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, 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) { + utmpx_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])) { + utmpx_frec2api(&fubuf, &ubuf); + 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. + */ + utmpx_frec2api(&fubuf, &ubuf); + return (NULL); +} + +/* + * "getutid" finds the specified entry in the utmp file. If + * it can't find it, it returns NULL. + */ +struct utmp * +getutid(const struct utmp *entry) +{ + struct utmpx utmpx; + struct utmpx *utmpx2; + + if (compat_utmpflag) + return (_compat_getutid(entry)); + + /* fail if we can't represent maxpid properly */ + if (big_pids_in_use()) { + errno = EOVERFLOW; + return (NULL); + } + getutmpx(entry, &utmpx); + if ((utmpx2 = getutxid(&utmpx)) == NULL) + return (NULL); + getutmp(utmpx2, &utmpcompat); + return (&utmpcompat); +} + +/* + * "getutxline" searches the "utmpx" file for a LOGIN_PROCESS or + * USER_PROCESS with the same "line" as the specified "entry". + */ +struct utmpx * +getutxline(const struct utmpx *entry) +{ + /* + * 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." + */ + utmpx_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) { + utmpx_frec2api(&fubuf, &ubuf); + return (&ubuf); + } + } while (getutxent_frec() != NULL); + + /* + * Since entry wasn't found, return NULL. + */ + utmpx_frec2api(&fubuf, &ubuf); + return (NULL); +} + +/* + * "getutline" searches the "utmp" file for a LOGIN_PROCESS or + * USER_PROCESS with the same "line" as the specified "entry". + */ +struct utmp * +getutline(const struct utmp *entry) +{ + struct utmpx utmpx; + struct utmpx *utmpx2; + + if (compat_utmpflag) + return (_compat_getutline(entry)); + + /* fail if we can't represent maxpid properly */ + if (big_pids_in_use()) { + errno = EOVERFLOW; + return (NULL); + } + /* call getutxline */ + getutmpx(entry, &utmpx); + if ((utmpx2 = getutxline(&utmpx)) == NULL) + return (NULL); + getutmp(utmpx2, &utmpcompat); + return (&utmpcompat); +} + +/* + * invoke_utmp_update + * + * Invokes the utmp_update program which has the privilege to write + * to the /etc/utmp file. + */ + +#define UTMP_UPDATE "/usr/lib/utmp_update" +#define STRSZ 64 /* Size of char buffer for argument strings */ + +static struct utmpx * +invoke_utmp_update(const struct utmpx *entryx) +{ + extern char **environ; + + int status; + pid_t child; + pid_t w; + int i; + char user[STRSZ], id[STRSZ], line[STRSZ], pid[STRSZ], type[STRSZ], + term[STRSZ], exit[STRSZ], time[STRSZ], time_usec[STRSZ], + session_id[STRSZ], syslen[32]; + char pad[sizeof (entryx->pad) * 2 + 1]; + char host[sizeof (entryx->ut_host) + 1]; + struct utmpx *curx = NULL; + char bin2hex[] = "0123456789ABCDEF"; + sigset_t mask, omask; + unsigned char *cp; + char *argvec[15]; + int error; + + /* + * Convert the utmp struct to strings for command line arguments. + */ + (void) strncpy(user, entryx->ut_user, sizeof (entryx->ut_user)); + user[sizeof (entryx->ut_user)] = '\0'; + (void) strncpy(id, entryx->ut_id, sizeof (entryx->ut_id)); + id[sizeof (entryx->ut_id)] = '\0'; + (void) strncpy(line, entryx->ut_line, sizeof (entryx->ut_line)); + line[sizeof (entryx->ut_line)] = '\0'; + (void) sprintf(pid, "%d", entryx->ut_pid); + (void) sprintf(type, "%d", entryx->ut_type); + (void) sprintf(term, "%d", entryx->ut_exit.e_termination); + (void) sprintf(exit, "%d", entryx->ut_exit.e_exit); + (void) sprintf(time, "%ld", entryx->ut_tv.tv_sec); + (void) sprintf(time_usec, "%ld", entryx->ut_tv.tv_usec); + (void) sprintf(session_id, "%d", entryx->ut_session); + + cp = (unsigned char *)entryx->pad; + for (i = 0; i < sizeof (entryx->pad); ++i) { + pad[i << 1] = bin2hex[(cp[i] >> 4) & 0xF]; + pad[(i << 1) + 1] = bin2hex[cp[i] & 0xF]; + } + pad[sizeof (pad) - 1] = '\0'; + + (void) sprintf(syslen, "%d", entryx->ut_syslen); + (void) strlcpy(host, entryx->ut_host, sizeof (host)); + + argvec[0] = UTMP_UPDATE; + argvec[1] = user; + argvec[2] = id; + argvec[3] = line; + argvec[4] = pid; + argvec[5] = type; + argvec[6] = term; + argvec[7] = exit; + argvec[8] = time; + argvec[9] = time_usec; + argvec[10] = session_id; + argvec[11] = pad; + argvec[12] = syslen; + argvec[13] = host; + argvec[14] = NULL; + + /* + * Block SIGCLD while we do this. + * This avoids having an unexpected SIGCLD sent to the process. + */ + (void) sigemptyset(&mask); + (void) sigaddset(&mask, SIGCLD); + (void) sigprocmask(SIG_BLOCK, &mask, &omask); + + error = posix_spawn(&child, UTMP_UPDATE, NULL, NULL, argvec, environ); + if (error) { + errno = error; + goto out; + } + + do { + w = waitpid(child, &status, 0); + } while (w == -1 && errno == EINTR); + + /* + * We can get ECHILD if the process is ignoring SIGCLD. + */ + if (!(w == -1 && errno == ECHILD) && + (w == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)) { + /* + * The child encountered an error, + */ + goto out; + } + + /* + * Normal termination. Return a pointer to the entry we just made. + */ + setutxent(); /* Reset file pointer */ + + while ((curx = getutxent()) != NULL) { + if (curx->ut_type != EMPTY && + (curx->ut_type == LOGIN_PROCESS || + curx->ut_type == USER_PROCESS || + curx->ut_type == DEAD_PROCESS) && + strncmp(&entryx->ut_line[0], &curx->ut_line[0], + sizeof (curx->ut_line)) == 0) + break; + } + +out: + (void) sigprocmask(SIG_SETMASK, &omask, NULL); + 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 * +pututxline(const struct utmpx *entry) +{ + struct utmpx *answer; + int lock = 0; + struct utmpx tmpxbuf; + struct futmpx ftmpxbuf; + + /* + * Copy the user supplied entry into our temporary buffer to + * avoid the possibility that the user is actually passing us + * the address of "ubuf". + */ + if (entry == NULL) + return (NULL); + + tmpxbuf = *entry; + utmpx_api2frec(entry, &ftmpxbuf); + + if (fd < 0) { + (void) getutxent_frec(); + if (fd < 0) + return ((struct utmpx *)NULL); + } + + /* + * If we are not the superuser than we can't write to /etc/utmp, + * 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.) + */ + + if (getutxid(&tmpxbuf) == NULL) { + + setutxent(); + + /* + * Lock the the entire file from here onwards. + */ + if (getutxid(&tmpxbuf) == NULL) { + lock++; + if (lockf(fd, F_LOCK, 0) < NULL) + return (NULL); + (void) fseek(fp, 0, SEEK_END); + } else + (void) fseek(fp, -(long)sizeof (struct futmpx), + SEEK_CUR); + } else + (void) fseek(fp, -(long)sizeof (struct futmpx), SEEK_CUR); + + /* + * Write out the user supplied structure. If the write fails, + * then the user probably doesn't have permission to write the + * utmpx file. + */ + if (fwrite(&ftmpxbuf, sizeof (ftmpxbuf), 1, fp) != 1) { + answer = (struct utmpx *)NULL; + } else { + /* + * Save the new user structure into ubuf and fubuf so that + * it will be up to date in the future. + */ + (void) fflush(fp); + fubuf = ftmpxbuf; + utmpx_frec2api(&fubuf, &ubuf); + answer = &ubuf; + } + + if (lock) + (void) lockf(fd, F_ULOCK, 0); + + if (answer != NULL && (tmpxbuf.ut_type == USER_PROCESS || + tmpxbuf.ut_type == DEAD_PROCESS)) + sendupid(tmpxbuf.ut_type == USER_PROCESS ? ADDPID : REMPID, + (pid_t)tmpxbuf.ut_pid); + return (answer); +} +/* + * "pututline" is a wrapper that calls pututxline after converting + * the utmp record to a utmpx record. + */ +struct utmp * +pututline(const struct utmp *entry) +{ + struct utmpx utmpx; + struct utmpx *utmpx2; + + if (compat_utmpflag) + return (_compat_pututline(entry)); + + getutmpx(entry, &utmpx); + if ((utmpx2 = pututxline(&utmpx)) == NULL) + return (NULL); + getutmp(utmpx2, &utmpcompat); + return (&utmpcompat); +} + +/* + * "setutxent" just resets the utmpx file back to the beginning. + */ +void +setutxent(void) +{ + if (fd != -1) + (void) lseek(fd, 0L, SEEK_SET); + + if (fp != NULL) + (void) fseek(fp, 0L, SEEK_SET); + + /* + * 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)); +} + +/* + * "setutent" is a wrapper that calls setutxent + */ +void +setutent(void) +{ + if (compat_utmpflag) { + _compat_setutent(); + return; + } + + setutxent(); +} + +/* + * "endutxent" closes the utmpx file. + */ +void +endutxent(void) +{ + if (fd != -1) + (void) close(fd); + fd = -1; + + if (fp != NULL) + (void) fclose(fp); + fp = NULL; + + bzero(&ubuf, sizeof (ubuf)); + bzero(&fubuf, sizeof (fubuf)); +} + +/* + * "endutent" is a wrapper that calls endutxent + * and clears the utmp compatibility buffer. + */ +void +endutent(void) +{ + if (compat_utmpflag) { + _compat_endutent(); + return; + } + + endutxent(); + bzero(&utmpcompat, sizeof (utmpcompat)); +} + +/* + * "utmpxname" allows the user to read a file other than the + * normal "utmpx" file. + */ +int +utmpxname(const char *newfile) +{ + size_t len; + + /* + * Determine if the new filename will fit. If not, return 0. + */ + if ((len = strlen(newfile)) > MAXFILE-1) + return (0); + + /* + * The name of the utmpx file has to end with 'x' + */ + if (newfile[len-1] != 'x') + return (0); + + /* + * Otherwise copy in the new file name. + */ + else + (void) strcpy(&utmpxfile[0], newfile); + /* + * Make sure everything is reset to the beginning state. + */ + endutxent(); + + /* + * If the file is being changed to /etc/utmpx or /var/adm/utmpx then + * we clear the flag so pututxline invokes utmp_update. Otherwise + * we set the flag indicating that they changed to another name. + */ + if (strcmp(utmpxfile, UTMPX_FILE) == 0 || + strcmp(utmpxfile, VAR_UTMPX_FILE) == 0) + changed_name = 0; + else + changed_name = 1; + + return (1); +} + +/* + * "utmpname" allows the user to read a file other than the + * normal "utmp" file. If the file specified is "/var/adm/utmp" + * or "/var/adm/wtmp", it is translated to the corresponding "utmpx" + * 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 + * an actual "old" utmp file (named something other than /var/adm/utmp), + * 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 +utmpname(const char *newfile) +{ + char name[MAXFILE+1]; + + if (strlen(newfile) > MAXFILE) + return (0); + + if (strcmp(newfile, "/var/adm/utmp") == 0 || + strcmp(newfile, "/var/adm/wtmp") == 0) { + (void) strcpy(name, newfile); + (void) strcat(name, "x"); + compat_utmpflag = 0; /* turn off old compat mode */ + return (utmpxname(name)); + } else { + (void) strcpy(_compat_utmpfile, newfile); + compat_utmpflag = 1; + return (1); + } +} + +/* + * Add the record to wtmpx. + */ +void +updwtmpx(const char *filex, struct utmpx *utx) +{ + struct futmpx futx; + int wfdx; + + if ((wfdx = open(filex, O_WRONLY | O_APPEND)) < 0) + return; + + (void) lseek(wfdx, 0, SEEK_END); + + utmpx_api2frec(utx, &futx); + (void) write(wfdx, &futx, sizeof (futx)); + +done: + (void) close(wfdx); +} + +/* + * Add record to wtmp (actually wtmpx). If not updating /var/adm/wtmp, + * use the old utmp compatibility routine to write a utmp-format + * record to the file specified. + */ +void +updwtmp(const char *file, struct utmp *ut) +{ + struct utmpx utmpx; + char xfile[MAXFILE + 1]; + + if (strcmp(file, "/var/adm/wtmp") == 0) { + (void) strlcpy(xfile, file, sizeof (xfile) - 1); + (void) strcat(xfile, "x"); + getutmpx(ut, &utmpx); + updwtmpx((const char *)&xfile, &utmpx); + } else + _compat_updwtmp(file, ut); +} + +/* + * 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 * +modutx(const struct utmpx *utp) +{ + int i; + struct utmpx utmp; /* holding area */ + struct utmpx *ucp = &utmp; /* and a pointer to it */ + struct utmpx *up; /* "current" utmpx entry */ + struct futmpx *fup; /* being examined */ + + for (i = 0; i < IDLEN; ++i) { + if ((unsigned char)utp->ut_id[i] == SC_WILDC) + return (NULL); + } + + /* + * copy the supplied utmpx structure someplace safe + */ + utmp = *utp; + setutxent(); + while (fup = getutxent_frec()) { + if (idcmp(ucp->ut_id, fup->ut_id)) + continue; + + /* + * only get here if ids are the same, i.e. found right entry + */ + if (ucp->ut_pid != fup->ut_pid) { + sendpid(REMPID, (pid_t)fup->ut_pid); + sendpid(ADDPID, (pid_t)ucp->ut_pid); + } + break; + } + up = pututxline(ucp); + if (ucp->ut_type == DEAD_PROCESS) + sendpid(REMPID, (pid_t)ucp->ut_pid); + if (up) + updwtmpx(WTMPX_FILE, 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 * +modut(struct utmp *utp) +{ + struct utmpx utmpx; + struct utmpx *utmpx2; + + getutmpx(utp, &utmpx); + if ((utmpx2 = modutx(&utmpx)) == NULL) + return (NULL); + + getutmp(utmpx2, utp); + 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 +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); + } +} + + +/* + * lockutx - lock utmpx file + */ +static int +lockutx(void) +{ + int lockfd; + + if ((lockfd = open(UTMPX_FILE, O_RDWR|O_CREAT, 0644)) < 0) + return (-1); + + if (lockf(lockfd, F_LOCK, 0) < 0) { + (void) close(lockfd); + return (-1); + } + + tempfd = fd; + fd = lockfd; + + return (0); + +} + + + +/* + * unlockutx - unlock utmpx file + */ +static void +unlockutx(void) +{ + (void) lockf(fd, F_ULOCK, 0); + (void) close(fd); + fd = tempfd; +} + + +/* + * 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 +sendpid(int cmd, pid_t pid) +{ + int pfd; /* file desc. for init pipe */ + pidrec_t prec; /* place for message to be built */ + + /* + * if for some reason init didn't open initpipe, open it read/write + * here to avoid sending SIGPIPE to the calling process + */ + pfd = open(IPIPE, O_RDWR); + if (pfd < 0) + return; + prec.pd_pid = pid; + prec.pd_type = cmd; + (void) write(pfd, &prec, sizeof (pidrec_t)); + (void) close(pfd); +} + +/* + * 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 * +makeutx(const struct utmpx *utmp) +{ + struct utmpx *utp; + struct futmpx *ut; /* "current" utmpx being examined */ + unsigned char saveid[IDLEN]; /* the last id we matched that was */ + /* NOT a dead proc */ + int falphanum = 0x30; /* first alpha num char */ + off_t offset; + + /* + * Are any wild card char's present in the idlen string? + */ + if (memchr(utmp->ut_id, SC_WILDC, IDLEN) != NULL) { + /* + * try to lock the utmpx file, only needed if + * we're doing wildcard matching + */ + if (lockutx()) + return (NULL); + + /* + * used in allocid + */ + (void) memset(saveid, falphanum, IDLEN); + + while (ut = getoneutx(&offset)) + if (idcmp(utmp->ut_id, ut->ut_id)) { + 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. + */ + if (ut->ut_type == DEAD_PROCESS) + break; + else + (void) memcpy(saveid, ut->ut_id, + IDLEN); + } + + if (ut) { + + /* + * Unused entry, reuse it. We know the offset. So + * just go to that offset utmpx and write it out. + */ + (void) memcpy((caddr_t)utmp->ut_id, ut->ut_id, IDLEN); + + putoneutx(utmp, offset); + updwtmpx(WTMPX_FILE, (struct utmpx *)utmp); + unlockutx(); + sendpid(ADDPID, (pid_t)utmp->ut_pid); + return ((struct utmpx *)utmp); + } else { + /* + * nothing available, allocate an id and + * write it out at the end. + */ + + if (allocid((char *)utmp->ut_id, saveid)) { + unlockutx(); + return (NULL); + } else { + /* + * Seek to end and write out the entry + * and also update the utmpx file. + */ + (void) lseek(fd, 0L, SEEK_END); + offset = lseek(fd, 0L, SEEK_CUR); + + putoneutx(utmp, offset); + updwtmpx(WTMPX_FILE, (struct utmpx *)utmp); + unlockutx(); + sendpid(ADDPID, (pid_t)utmp->ut_pid); + return ((struct utmpx *)utmp); + } + } + } else { + utp = pututxline(utmp); + if (utp) + updwtmpx(WTMPX_FILE, utp); + endutxent(); + sendpid(ADDPID, (pid_t)utmp->ut_pid); + 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 * +makeut(struct utmp *utmp) +{ + struct utmpx utmpx; + struct utmpx *utmpx2; + + if (compat_utmpflag) + return (_compat_makeut(utmp)); + + getutmpx(utmp, &utmpx); + if ((utmpx2 = makeutx(&utmpx)) == NULL) + return (NULL); + + getutmp(utmpx2, utmp); + return (utmp); +} + + +#define UTMPNBUF 200 /* Approx 8k (FS Block) size */ +static struct futmpx *utmpbuf = NULL; + +/* + * Buffered read routine to get one entry from utmpx file + */ +static struct futmpx * +getoneutx(off_t *off) +{ + static size_t idx = 0; /* Current index in the utmpbuf */ + static size_t nidx = 0; /* Max entries in this utmpbuf */ + static int nbuf = 0; /* number of utmpbufs read from disk */ + ssize_t nbytes, bufsz = sizeof (struct futmpx) * UTMPNBUF; + + if (utmpbuf == NULL) + if ((utmpbuf = malloc(bufsz)) == NULL) { + perror("malloc"); + return (NULL); + } + + if (idx == nidx) { + /* + * We have read all entries in the utmpbuf. Read + * the buffer from the disk. + */ + if ((nbytes = read(fd, utmpbuf, bufsz)) < bufsz) { + /* + * Partial read only. keep count of the + * number of valid entries in the buffer + */ + nidx = nbytes / sizeof (struct futmpx); + } else { + /* + * We read in the full UTMPNBUF entries + * Great ! + */ + nidx = UTMPNBUF; + } + nbuf++; /* Number of buf we have read in. */ + idx = 0; /* reset index within utmpbuf */ + } + + /* + * Current offset of this buffer in the file + */ + *off = (((nbuf - 1) * UTMPNBUF) + idx) * sizeof (struct futmpx); + + if (idx < nidx) { + /* + * We still have at least one valid buffer in + * utmpbuf to be passed to the caller. + */ + return (&utmpbuf[idx++]); + } + + /* + * Reached EOF. Return NULL. Offset is set correctly + * to append at the end of the file + */ + + return (NULL); +} + +static void +putoneutx(const struct utmpx *utpx, off_t off) +{ + struct futmpx futx; + + utmpx_api2frec(utpx, &futx); + (void) lseek(fd, off, SEEK_SET); /* seek in the utmpx file */ + (void) write(fd, &futx, sizeof (futx)); +} + +/* + * 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 +sendupid(int cmd, pid_t pid) +{ + int pfd; /* file desc. for utmp pipe */ + pidrec_t prec; /* place for message to be built */ + + /* + * if for some reason utmp didn't open utmppipe, open it read/write + * here to avoid sending SIGPIPE to the calling process + */ + + pfd = open(UPIPE, O_RDWR | O_NONBLOCK | O_NDELAY); + if (pfd < 0) + return; + prec.pd_pid = pid; + prec.pd_type = cmd; + (void) write(pfd, &prec, sizeof (pidrec_t)); + (void) close(pfd); +} + +/* + * getutmpx - convert a utmp record into a utmpx record + */ +void +getutmpx(const struct utmp *ut, struct utmpx *utx) +{ + (void) memcpy(utx->ut_user, ut->ut_user, sizeof (ut->ut_user)); + (void) bzero(&utx->ut_user[sizeof (ut->ut_user)], + sizeof (utx->ut_user) - sizeof (ut->ut_user)); + (void) memcpy(utx->ut_line, ut->ut_line, sizeof (ut->ut_line)); + (void) bzero(&utx->ut_line[sizeof (ut->ut_line)], + sizeof (utx->ut_line) - sizeof (ut->ut_line)); + (void) memcpy(utx->ut_id, ut->ut_id, sizeof (ut->ut_id)); + utx->ut_pid = ut->ut_pid; + utx->ut_type = ut->ut_type; + utx->ut_exit = ut->ut_exit; + utx->ut_tv.tv_sec = ut->ut_time; + utx->ut_tv.tv_usec = 0; + utx->ut_session = 0; + bzero(utx->pad, sizeof (utx->pad)); + bzero(utx->ut_host, sizeof (utx->ut_host)); + utx->ut_syslen = 0; +} + +/* + * getutmp - convert a utmpx record into a utmp record + */ +void +getutmp(const struct utmpx *utx, struct utmp *ut) +{ + (void) memcpy(ut->ut_user, utx->ut_user, sizeof (ut->ut_user)); + (void) memcpy(ut->ut_line, utx->ut_line, sizeof (ut->ut_line)); + (void) memcpy(ut->ut_id, utx->ut_id, sizeof (utx->ut_id)); + ut->ut_pid = utx->ut_pid; + ut->ut_type = utx->ut_type; + ut->ut_exit = utx->ut_exit; + ut->ut_time = utx->ut_tv.tv_sec; +}