Home | History | Annotate | Download | only in gen
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1988 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 /*
     31  * University Copyright- Copyright (c) 1982, 1986, 1988
     32  * The Regents of the University of California
     33  * All Rights Reserved
     34  *
     35  * University Acknowledgment- Portions of this document are derived from
     36  * software developed by the University of California, Berkeley, and its
     37  * contributors.
     38  */
     39 
     40 
     41 /*
     42  * Routines to read and write the /etc/utmpx file. Also contains
     43  * binary compatibility routines to support the old utmp interfaces
     44  * on systems with MAXPID <= SHRT_MAX.
     45  */
     46 
     47 #include "lint.h"
     48 #include <sys/types.h>
     49 #include <stdio.h>
     50 #include <sys/param.h>
     51 #include <sys/stat.h>
     52 #include <utmpx.h>
     53 #include <errno.h>
     54 #include <fcntl.h>
     55 #include <string.h>
     56 #include <strings.h>
     57 #include <unistd.h>
     58 #include <ctype.h>
     59 #include <stdlib.h>
     60 #include <sys/wait.h>
     61 #include <pthread.h>
     62 #include <limits.h>
     63 #include <signal.h>
     64 #include <spawn.h>
     65 
     66 #define	IDLEN		4	/* length of id field in utmp */
     67 #define	SC_WILDC	0xff	/* wild char for utmp ids */
     68 #define	MAXFILE		79	/* Maximum pathname length for "utmpx" file */
     69 
     70 #define	MAXVAL		255		/* max value for an id `character' */
     71 #define	IPIPE		"/var/run/initpipe"	/* FIFO to send pids to init */
     72 #define	UPIPE		"/var/run/utmppipe"	/* FIFO to send pids to utmpd */
     73 
     74 #define	VAR_UTMPX_FILE	"/var/adm/utmpx" /* for sanity check only */
     75 
     76 
     77 /*
     78  * format of message sent to init
     79  */
     80 
     81 typedef struct	pidrec {
     82 	int	pd_type;	/* command type */
     83 	pid_t	pd_pid;		/* pid */
     84 } pidrec_t;
     85 
     86 /*
     87  * pd_type's
     88  */
     89 #define	ADDPID 1	/* add a pid to "godchild" list */
     90 #define	REMPID 2	/* remove a pid to "godchild" list */
     91 
     92 static void	utmpx_frec2api(const struct futmpx *, struct utmpx *);
     93 static void	utmpx_api2frec(const struct utmpx *, struct futmpx *);
     94 
     95 static void	unlockutx(void);
     96 static void	sendpid(int, pid_t);
     97 static void	sendupid(int, pid_t);
     98 static int	idcmp(const char *, const char *);
     99 static int	allocid(char *, unsigned char *);
    100 static int	lockutx(void);
    101 
    102 static struct utmpx *invoke_utmp_update(const struct utmpx *);
    103 static struct futmpx *getoneutx(off_t *);
    104 static void	putoneutx(const struct utmpx *, off_t);
    105 static int	big_pids_in_use(void);
    106 
    107 /*
    108  * prototypes for utmp compatibility routines (in getut.c)
    109  */
    110 extern struct utmp *_compat_getutent(void);
    111 extern struct utmp *_compat_getutid(const struct utmp *);
    112 extern struct utmp *_compat_getutline(const struct utmp *);
    113 extern struct utmp *_compat_pututline(const struct utmp *);
    114 extern void _compat_setutent(void);
    115 extern void _compat_endutent(void);
    116 extern void _compat_updwtmp(const char *, struct utmp *);
    117 extern struct utmp *_compat_makeut(struct utmp *);
    118 
    119 static int fd = -1;	/* File descriptor for the utmpx file. */
    120 static int ut_got_maxpid = 0;	/* Flag set when sysconf(_SC_MAXPID) called */
    121 static pid_t ut_maxpid = 0;	/* Value of MAXPID from sysconf */
    122 static int tempfd = -1;  /* To store fd between lockutx() and unlockutx() */
    123 
    124 static	FILE	*fp = NULL;	/* Buffered file descriptior for utmpx file */
    125 static int changed_name = 0;	/* Flag set when not using utmpx file */
    126 static char utmpxfile[MAXFILE+1] = UTMPX_FILE;	/* Name of the current */
    127 char _compat_utmpfile[MAXFILE+1];
    128 static int compat_utmpflag = 0;	/* old compat mode flag */
    129 
    130 static struct futmpx fubuf;	/* Copy of last entry read in. */
    131 static struct utmpx ubuf;	/* Last entry returned to client */
    132 
    133 static struct utmp utmpcompat;	/* Buffer for returning utmp-format data */
    134 /*
    135  * In the 64-bit world, the utmpx data structure grows because of
    136  * the ut_time field (a struct timeval) grows in the middle of it.
    137  */
    138 static void
    139 utmpx_frec2api(const struct futmpx *src, struct utmpx *dst)
    140 {
    141 	if (src == NULL)
    142 		return;
    143 
    144 	(void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
    145 	(void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
    146 	(void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
    147 	dst->ut_pid = src->ut_pid;
    148 	dst->ut_type = src->ut_type;
    149 	dst->ut_exit.e_termination = src->ut_exit.e_termination;
    150 	dst->ut_exit.e_exit = src->ut_exit.e_exit;
    151 	dst->ut_tv.tv_sec = (time_t)src->ut_tv.tv_sec;
    152 	dst->ut_tv.tv_usec = (suseconds_t)src->ut_tv.tv_usec;
    153 	dst->ut_session = src->ut_session;
    154 	bzero(dst->pad, sizeof (dst->pad));
    155 	dst->ut_syslen = src->ut_syslen;
    156 	(void) memcpy(dst->ut_host, src->ut_host, sizeof (dst->ut_host));
    157 }
    158 
    159 static void
    160 utmpx_api2frec(const struct utmpx *src, struct futmpx *dst)
    161 {
    162 	if (src == NULL)
    163 		return;
    164 
    165 	(void) strncpy(dst->ut_user, src->ut_user, sizeof (dst->ut_user));
    166 	(void) strncpy(dst->ut_line, src->ut_line, sizeof (dst->ut_line));
    167 	(void) memcpy(dst->ut_id, src->ut_id, sizeof (dst->ut_id));
    168 	dst->ut_pid = src->ut_pid;
    169 	dst->ut_type = src->ut_type;
    170 	dst->ut_exit.e_termination = src->ut_exit.e_termination;
    171 	dst->ut_exit.e_exit = src->ut_exit.e_exit;
    172 	dst->ut_tv.tv_sec = (time32_t)src->ut_tv.tv_sec;
    173 	dst->ut_tv.tv_usec = (int32_t)src->ut_tv.tv_usec;
    174 	dst->ut_session = src->ut_session;
    175 	bzero(dst->pad, sizeof (dst->pad));
    176 	dst->ut_syslen = src->ut_syslen;
    177 	(void) memcpy(dst->ut_host, src->ut_host, sizeof (dst->ut_host));
    178 }
    179 
    180 /*
    181  * "getutxent_frec" gets the raw version of the next entry in the utmpx file.
    182  */
    183 static struct futmpx *
    184 getutxent_frec(void)
    185 {
    186 	/*
    187 	 * If the "utmpx" file is not open, attempt to open it for
    188 	 * reading.  If there is no file, attempt to create one.  If
    189 	 * both attempts fail, return NULL.  If the file exists, but
    190 	 * isn't readable and writeable, do not attempt to create.
    191 	 */
    192 	if (fd < 0) {
    193 
    194 		if ((fd = open(utmpxfile, O_RDWR|O_CREAT, 0644)) < 0) {
    195 
    196 			/*
    197 			 * If the open failed for permissions, try opening
    198 			 * it only for reading.  All "pututxline()" later
    199 			 * will fail the writes.
    200 			 */
    201 
    202 			if ((fd = open(utmpxfile, O_RDONLY)) < 0)
    203 				return (NULL);
    204 
    205 			if ((fp = fopen(utmpxfile, "rF")) == NULL) {
    206 				(void) close(fd);
    207 				fd = -1;
    208 				return (NULL);
    209 			}
    210 
    211 		} else {
    212 			/*
    213 			 * Get the stream pointer
    214 			 */
    215 			if ((fp = fopen(utmpxfile, "r+F")) == NULL) {
    216 				(void) close(fd);
    217 				fd = -1;
    218 				return (NULL);
    219 			}
    220 		}
    221 	}
    222 
    223 	/*
    224 	 * Try to read in the next entry from the utmpx file.
    225 	 */
    226 	if (fread(&fubuf, sizeof (fubuf), 1, fp) != 1) {
    227 		/*
    228 		 * Make sure fubuf is zeroed.
    229 		 */
    230 		bzero(&fubuf, sizeof (fubuf));
    231 		return (NULL);
    232 	}
    233 
    234 	return (&fubuf);
    235 }
    236 
    237 /*
    238  * "big_pids_in_use" determines whether large pid numbers are in use
    239  * or not.  If MAXPID won't fit in a signed short, the utmp.ut_pid
    240  * field will overflow.
    241  *
    242  * Returns 0 if small pids are in use, 1 otherwise
    243  */
    244 static int
    245 big_pids_in_use(void)
    246 {
    247 	if (!ut_got_maxpid) {
    248 		ut_got_maxpid++;
    249 		ut_maxpid = sysconf(_SC_MAXPID);
    250 	}
    251 	return (ut_maxpid > SHRT_MAX ? 1 : 0);
    252 }
    253 
    254 /*
    255  * "getutxent" gets the next entry in the utmpx file.
    256  */
    257 struct utmpx *
    258 getutxent(void)
    259 {
    260 	struct futmpx *futxp;
    261 
    262 	futxp = getutxent_frec();
    263 	utmpx_frec2api(&fubuf, &ubuf);
    264 	if (futxp == NULL)
    265 		return (NULL);
    266 	return (&ubuf);
    267 }
    268 /*
    269  * "getutent" gets the next entry in the utmp file.
    270  */
    271 struct utmp *
    272 getutent(void)
    273 {
    274 	struct utmpx *utmpx;
    275 
    276 	if (compat_utmpflag)
    277 		return (_compat_getutent());
    278 
    279 	/* fail if we can't represent maxpid properly */
    280 	if (big_pids_in_use()) {
    281 		errno = EOVERFLOW;
    282 		return (NULL);
    283 	}
    284 
    285 	if ((utmpx = getutxent()) == NULL)
    286 		return (NULL);
    287 
    288 	getutmp(utmpx, &utmpcompat);
    289 	return (&utmpcompat);
    290 }
    291 
    292 /*
    293  * "getutxid" finds the specified entry in the utmpx file.  If
    294  * it can't find it, it returns NULL.
    295  */
    296 struct utmpx *
    297 getutxid(const struct utmpx *entry)
    298 {
    299 	short type;
    300 
    301 	/*
    302 	 * From XPG5: "The getutxid() or getutxline() may cache data.
    303 	 * For this reason, to use getutxline() to search for multiple
    304 	 * occurrences, it is necessary to zero out the static data after
    305 	 * each success, or getutxline() could just return a pointer to
    306 	 * the same utmpx structure over and over again."
    307 	 */
    308 	utmpx_api2frec(&ubuf, &fubuf);
    309 
    310 	/*
    311 	 * Start looking for entry. Look in our current buffer before
    312 	 * reading in new entries.
    313 	 */
    314 	do {
    315 		/*
    316 		 * If there is no entry in "fubuf", skip to the read.
    317 		 */
    318 		if (fubuf.ut_type != EMPTY) {
    319 			switch (entry->ut_type) {
    320 
    321 			/*
    322 			 * Do not look for an entry if the user sent
    323 			 * us an EMPTY entry.
    324 			 */
    325 			case EMPTY:
    326 				return (NULL);
    327 
    328 			/*
    329 			 * For RUN_LVL, BOOT_TIME, OLD_TIME, and NEW_TIME
    330 			 * entries, only the types have to match.  If they do,
    331 			 * return the address of internal buffer.
    332 			 */
    333 			case RUN_LVL:
    334 			case BOOT_TIME:
    335 			case DOWN_TIME:
    336 			case OLD_TIME:
    337 			case NEW_TIME:
    338 				if (entry->ut_type == fubuf.ut_type) {
    339 					utmpx_frec2api(&fubuf, &ubuf);
    340 					return (&ubuf);
    341 				}
    342 				break;
    343 
    344 			/*
    345 			 * For INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS,
    346 			 * and DEAD_PROCESS the type of the entry in "fubuf",
    347 			 * must be one of the above and id's must match.
    348 			 */
    349 			case INIT_PROCESS:
    350 			case LOGIN_PROCESS:
    351 			case USER_PROCESS:
    352 			case DEAD_PROCESS:
    353 				if (((type = fubuf.ut_type) == INIT_PROCESS ||
    354 				    type == LOGIN_PROCESS ||
    355 				    type == USER_PROCESS ||
    356 				    type == DEAD_PROCESS) &&
    357 				    (fubuf.ut_id[0] == entry->ut_id[0]) &&
    358 				    (fubuf.ut_id[1] == entry->ut_id[1]) &&
    359 				    (fubuf.ut_id[2] == entry->ut_id[2]) &&
    360 				    (fubuf.ut_id[3] == entry->ut_id[3])) {
    361 					utmpx_frec2api(&fubuf, &ubuf);
    362 					return (&ubuf);
    363 				}
    364 				break;
    365 
    366 			/*
    367 			 * Do not search for illegal types of entry.
    368 			 */
    369 			default:
    370 				return (NULL);
    371 			}
    372 		}
    373 	} while (getutxent_frec() != NULL);
    374 
    375 	/*
    376 	 * Return NULL since the proper entry wasn't found.
    377 	 */
    378 	utmpx_frec2api(&fubuf, &ubuf);
    379 	return (NULL);
    380 }
    381 
    382 /*
    383  * "getutid" finds the specified entry in the utmp file.  If
    384  * it can't find it, it returns NULL.
    385  */
    386 struct utmp *
    387 getutid(const struct utmp *entry)
    388 {
    389 	struct utmpx utmpx;
    390 	struct utmpx *utmpx2;
    391 
    392 	if (compat_utmpflag)
    393 		return (_compat_getutid(entry));
    394 
    395 	/* fail if we can't represent maxpid properly */
    396 	if (big_pids_in_use()) {
    397 		errno = EOVERFLOW;
    398 		return (NULL);
    399 	}
    400 	getutmpx(entry, &utmpx);
    401 	if ((utmpx2 = getutxid(&utmpx)) == NULL)
    402 		return (NULL);
    403 	getutmp(utmpx2, &utmpcompat);
    404 	return (&utmpcompat);
    405 }
    406 
    407 /*
    408  * "getutxline" searches the "utmpx" file for a LOGIN_PROCESS or
    409  * USER_PROCESS with the same "line" as the specified "entry".
    410  */
    411 struct utmpx *
    412 getutxline(const struct utmpx *entry)
    413 {
    414 	/*
    415 	 * From XPG5: "The getutxid() or getutxline() may cache data.
    416 	 * For this reason, to use getutxline() to search for multiple
    417 	 * occurrences, it is necessary to zero out the static data after
    418 	 * each success, or getutxline() could just return a pointer to
    419 	 * the same utmpx structure over and over again."
    420 	 */
    421 	utmpx_api2frec(&ubuf, &fubuf);
    422 
    423 	do {
    424 		/*
    425 		 * If the current entry is the one we are interested in,
    426 		 * return a pointer to it.
    427 		 */
    428 		if (fubuf.ut_type != EMPTY &&
    429 		    (fubuf.ut_type == LOGIN_PROCESS ||
    430 		    fubuf.ut_type == USER_PROCESS) &&
    431 		    strncmp(&entry->ut_line[0], &fubuf.ut_line[0],
    432 		    sizeof (fubuf.ut_line)) == 0) {
    433 			utmpx_frec2api(&fubuf, &ubuf);
    434 			return (&ubuf);
    435 		}
    436 	} while (getutxent_frec() != NULL);
    437 
    438 	/*
    439 	 * Since entry wasn't found, return NULL.
    440 	 */
    441 	utmpx_frec2api(&fubuf, &ubuf);
    442 	return (NULL);
    443 }
    444 
    445 /*
    446  * "getutline" searches the "utmp" file for a LOGIN_PROCESS or
    447  * USER_PROCESS with the same "line" as the specified "entry".
    448  */
    449 struct utmp *
    450 getutline(const struct utmp *entry)
    451 {
    452 	struct utmpx utmpx;
    453 	struct utmpx *utmpx2;
    454 
    455 	if (compat_utmpflag)
    456 		return (_compat_getutline(entry));
    457 
    458 	/* fail if we can't represent maxpid properly */
    459 	if (big_pids_in_use()) {
    460 		errno = EOVERFLOW;
    461 		return (NULL);
    462 	}
    463 	/* call getutxline */
    464 	getutmpx(entry, &utmpx);
    465 	if ((utmpx2 = getutxline(&utmpx)) == NULL)
    466 		return (NULL);
    467 	getutmp(utmpx2, &utmpcompat);
    468 	return (&utmpcompat);
    469 }
    470 
    471 /*
    472  * invoke_utmp_update
    473  *
    474  * Invokes the utmp_update program which has the privilege to write
    475  * to the /etc/utmp file.
    476  */
    477 
    478 #define	UTMP_UPDATE 	"/usr/lib/utmp_update"
    479 #define	STRSZ	64	/* Size of char buffer for argument strings */
    480 
    481 static struct utmpx *
    482 invoke_utmp_update(const struct utmpx *entryx)
    483 {
    484 	extern char **_environ;
    485 
    486 	posix_spawnattr_t attr;
    487 	int status;
    488 	int cancel_state;
    489 	pid_t child;
    490 	pid_t w;
    491 	int i;
    492 	char user[STRSZ], id[STRSZ], line[STRSZ], pid[STRSZ], type[STRSZ],
    493 	    term[STRSZ], exit[STRSZ], time[STRSZ], time_usec[STRSZ],
    494 	    session_id[STRSZ], syslen[32];
    495 	char pad[sizeof (entryx->pad) * 2 + 1];
    496 	char host[sizeof (entryx->ut_host) + 1];
    497 	struct utmpx *curx = NULL;
    498 	char bin2hex[] = "0123456789ABCDEF";
    499 	unsigned char *cp;
    500 	char *argvec[15];
    501 	int error;
    502 
    503 	/*
    504 	 * Convert the utmp struct to strings for command line arguments.
    505 	 */
    506 	(void) strncpy(user, entryx->ut_user, sizeof (entryx->ut_user));
    507 	user[sizeof (entryx->ut_user)] = '\0';
    508 	(void) strncpy(id, entryx->ut_id, sizeof (entryx->ut_id));
    509 	id[sizeof (entryx->ut_id)] = '\0';
    510 	(void) strncpy(line, entryx->ut_line, sizeof (entryx->ut_line));
    511 	line[sizeof (entryx->ut_line)] = '\0';
    512 	(void) sprintf(pid, "%d", (int)entryx->ut_pid);
    513 	(void) sprintf(type, "%d", entryx->ut_type);
    514 	(void) sprintf(term, "%d", entryx->ut_exit.e_termination);
    515 	(void) sprintf(exit, "%d", entryx->ut_exit.e_exit);
    516 	(void) sprintf(time, "%ld", entryx->ut_tv.tv_sec);
    517 	(void) sprintf(time_usec, "%ld", entryx->ut_tv.tv_usec);
    518 	(void) sprintf(session_id, "%d", entryx->ut_session);
    519 
    520 	cp = (unsigned char *)entryx->pad;
    521 	for (i = 0; i < sizeof (entryx->pad); ++i) {
    522 		pad[i << 1] = bin2hex[(cp[i] >> 4) & 0xF];
    523 		pad[(i << 1) + 1] = bin2hex[cp[i] & 0xF];
    524 	}
    525 	pad[sizeof (pad) - 1] = '\0';
    526 
    527 	(void) sprintf(syslen, "%d", entryx->ut_syslen);
    528 	(void) strlcpy(host, entryx->ut_host, sizeof (host));
    529 
    530 	argvec[0] = UTMP_UPDATE;
    531 	argvec[1] = user;
    532 	argvec[2] = id;
    533 	argvec[3] = line;
    534 	argvec[4] = pid;
    535 	argvec[5] = type;
    536 	argvec[6] = term;
    537 	argvec[7] = exit;
    538 	argvec[8] = time;
    539 	argvec[9] = time_usec;
    540 	argvec[10] = session_id;
    541 	argvec[11] = pad;
    542 	argvec[12] = syslen;
    543 	argvec[13] = host;
    544 	argvec[14] = NULL;
    545 
    546 	/*
    547 	 * No SIGCHLD, please, and let no one else reap our child.
    548 	 */
    549 	error = posix_spawnattr_init(&attr);
    550 	if (error) {
    551 		errno = error;
    552 		goto out;
    553 	}
    554 	error = posix_spawnattr_setflags(&attr,
    555 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
    556 	if (error) {
    557 		(void) posix_spawnattr_destroy(&attr);
    558 		errno = error;
    559 		goto out;
    560 	}
    561 	error = posix_spawn(&child, UTMP_UPDATE, NULL, &attr, argvec, _environ);
    562 	(void) posix_spawnattr_destroy(&attr);
    563 	if (error) {
    564 		errno = error;
    565 		goto out;
    566 	}
    567 
    568 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
    569 	do {
    570 		w = waitpid(child, &status, 0);
    571 	} while (w == -1 && errno == EINTR);
    572 	(void) pthread_setcancelstate(cancel_state, NULL);
    573 
    574 	/*
    575 	 * We can get ECHILD if the process is ignoring SIGCLD.
    576 	 */
    577 	if (!(w == -1 && errno == ECHILD) &&
    578 	    (w == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
    579 		/*
    580 		 * The child encountered an error,
    581 		 */
    582 		goto out;
    583 	}
    584 
    585 	/*
    586 	 * Normal termination.  Return a pointer to the entry we just made.
    587 	 */
    588 	setutxent();	/* Reset file pointer */
    589 
    590 	while ((curx = getutxent()) != NULL) {
    591 		if (curx->ut_type != EMPTY &&
    592 		    (curx->ut_type == LOGIN_PROCESS ||
    593 		    curx->ut_type == USER_PROCESS ||
    594 		    curx->ut_type == DEAD_PROCESS) &&
    595 		    strncmp(&entryx->ut_line[0], &curx->ut_line[0],
    596 		    sizeof (curx->ut_line)) == 0)
    597 			break;
    598 	}
    599 
    600 out:
    601 	return (curx);
    602 }
    603 
    604 /*
    605  * "pututxline" writes the structure sent into the utmpx file.
    606  * If there is already an entry with the same id, then it is
    607  * overwritten, otherwise a new entry is made at the end of the
    608  * utmpx file.
    609  */
    610 
    611 struct utmpx *
    612 pututxline(const struct utmpx *entry)
    613 {
    614 	struct utmpx *answer;
    615 	int lock = 0;
    616 	struct utmpx tmpxbuf;
    617 	struct futmpx ftmpxbuf;
    618 
    619 	/*
    620 	 * Copy the user supplied entry into our temporary buffer to
    621 	 * avoid the possibility that the user is actually passing us
    622 	 * the address of "ubuf".
    623 	 */
    624 	if (entry == NULL)
    625 		return (NULL);
    626 
    627 	(void) memcpy(&tmpxbuf, entry, sizeof (tmpxbuf));
    628 	utmpx_api2frec(entry, &ftmpxbuf);
    629 
    630 	if (fd < 0) {
    631 		(void) getutxent_frec();
    632 		if (fd < 0)
    633 			return ((struct utmpx *)NULL);
    634 	}
    635 
    636 	/*
    637 	 * If we are not the superuser than we can't write to /etc/utmp,
    638 	 * so invoke update_utmp(8) to write the entry for us.
    639 	 */
    640 	if (changed_name == 0 && geteuid() != 0)
    641 		return (invoke_utmp_update(entry));
    642 
    643 	/*
    644 	 * Find the proper entry in the utmpx file.  Start at the current
    645 	 * location.  If it isn't found from here to the end of the
    646 	 * file, then reset to the beginning of the file and try again.
    647 	 * If it still isn't found, then write a new entry at the end of
    648 	 * the file.  (Making sure the location is an integral number of
    649 	 * utmp structures into the file incase the file is scribbled.)
    650 	 */
    651 
    652 	if (getutxid(&tmpxbuf) == NULL) {
    653 
    654 		setutxent();
    655 
    656 		/*
    657 		 * Lock the the entire file from here onwards.
    658 		 */
    659 		if (getutxid(&tmpxbuf) == NULL) {
    660 			lock++;
    661 			if (lockf(fd, F_LOCK, 0) < NULL)
    662 				return (NULL);
    663 			(void) fseek(fp, 0, SEEK_END);
    664 		} else
    665 			(void) fseek(fp, -(long)sizeof (struct futmpx),
    666 			    SEEK_CUR);
    667 	} else
    668 		(void) fseek(fp, -(long)sizeof (struct futmpx), SEEK_CUR);
    669 
    670 	/*
    671 	 * Write out the user supplied structure.  If the write fails,
    672 	 * then the user probably doesn't have permission to write the
    673 	 * utmpx file.
    674 	 */
    675 	if (fwrite(&ftmpxbuf, sizeof (ftmpxbuf), 1, fp) != 1) {
    676 		answer = (struct utmpx *)NULL;
    677 	} else {
    678 		/*
    679 		 * Save the new user structure into ubuf and fubuf so that
    680 		 * it will be up to date in the future.
    681 		 */
    682 		(void) fflush(fp);
    683 		(void) memcpy(&fubuf, &ftmpxbuf, sizeof (fubuf));
    684 		utmpx_frec2api(&fubuf, &ubuf);
    685 		answer = &ubuf;
    686 	}
    687 
    688 	if (lock)
    689 		(void) lockf(fd, F_ULOCK, 0);
    690 
    691 	if (answer != NULL && (tmpxbuf.ut_type == USER_PROCESS ||
    692 	    tmpxbuf.ut_type == DEAD_PROCESS))
    693 		sendupid(tmpxbuf.ut_type == USER_PROCESS ? ADDPID : REMPID,
    694 		    (pid_t)tmpxbuf.ut_pid);
    695 	return (answer);
    696 }
    697 /*
    698  * "pututline" is a wrapper that calls pututxline after converting
    699  * the utmp record to a utmpx record.
    700  */
    701 struct utmp *
    702 pututline(const struct utmp *entry)
    703 {
    704 	struct utmpx utmpx;
    705 	struct utmpx *utmpx2;
    706 
    707 	if (compat_utmpflag)
    708 		return (_compat_pututline(entry));
    709 
    710 	getutmpx(entry, &utmpx);
    711 	if ((utmpx2 = pututxline(&utmpx)) == NULL)
    712 		return (NULL);
    713 	getutmp(utmpx2, &utmpcompat);
    714 	return (&utmpcompat);
    715 }
    716 
    717 /*
    718  * "setutxent" just resets the utmpx file back to the beginning.
    719  */
    720 void
    721 setutxent(void)
    722 {
    723 	if (fd != -1)
    724 		(void) lseek(fd, 0L, SEEK_SET);
    725 
    726 	if (fp != NULL)
    727 		(void) fseek(fp, 0L, SEEK_SET);
    728 
    729 	/*
    730 	 * Zero the stored copy of the last entry read, since we are
    731 	 * resetting to the beginning of the file.
    732 	 */
    733 	bzero(&ubuf, sizeof (ubuf));
    734 	bzero(&fubuf, sizeof (fubuf));
    735 }
    736 
    737 /*
    738  * "setutent" is a wrapper that calls setutxent
    739  */
    740 void
    741 setutent(void)
    742 {
    743 	if (compat_utmpflag) {
    744 		_compat_setutent();
    745 		return;
    746 	}
    747 
    748 	setutxent();
    749 }
    750 
    751 /*
    752  * "endutxent" closes the utmpx file.
    753  */
    754 void
    755 endutxent(void)
    756 {
    757 	if (fd != -1)
    758 		(void) close(fd);
    759 	fd = -1;
    760 
    761 	if (fp != NULL)
    762 		(void) fclose(fp);
    763 	fp = NULL;
    764 
    765 	bzero(&ubuf, sizeof (ubuf));
    766 	bzero(&fubuf, sizeof (fubuf));
    767 }
    768 
    769 /*
    770  * "endutent" is a wrapper that calls endutxent
    771  * and clears the utmp compatibility buffer.
    772  */
    773 void
    774 endutent(void)
    775 {
    776 	if (compat_utmpflag) {
    777 		_compat_endutent();
    778 		return;
    779 	}
    780 
    781 	endutxent();
    782 	bzero(&utmpcompat, sizeof (utmpcompat));
    783 }
    784 
    785 /*
    786  * "utmpxname" allows the user to read a file other than the
    787  * normal "utmpx" file.
    788  */
    789 int
    790 utmpxname(const char *newfile)
    791 {
    792 	size_t len;
    793 
    794 	/*
    795 	 * Determine if the new filename will fit.  If not, return 0.
    796 	 */
    797 	if ((len = strlen(newfile)) > MAXFILE-1)
    798 		return (0);
    799 
    800 	/*
    801 	 * The name of the utmpx file has to end with 'x'
    802 	 */
    803 	if (newfile[len-1] != 'x')
    804 		return (0);
    805 
    806 	/*
    807 	 * Otherwise copy in the new file name.
    808 	 */
    809 	else
    810 		(void) strcpy(&utmpxfile[0], newfile);
    811 	/*
    812 	 * Make sure everything is reset to the beginning state.
    813 	 */
    814 	endutxent();
    815 
    816 	/*
    817 	 * If the file is being changed to /etc/utmpx or /var/adm/utmpx then
    818 	 * we clear the flag so pututxline invokes utmp_update.  Otherwise
    819 	 * we set the flag indicating that they changed to another name.
    820 	 */
    821 	if (strcmp(utmpxfile, UTMPX_FILE) == 0 ||
    822 	    strcmp(utmpxfile, VAR_UTMPX_FILE) == 0)
    823 		changed_name = 0;
    824 	else
    825 		changed_name = 1;
    826 
    827 	return (1);
    828 }
    829 
    830 /*
    831  * "utmpname" allows the user to read a file other than the
    832  * normal "utmp" file. If the file specified is "/var/adm/utmp"
    833  * or "/var/adm/wtmp", it is translated to the corresponding "utmpx"
    834  * format name, and all "utmp" operations become wrapped calls
    835  * to the equivalent "utmpx" routines, with data conversions
    836  * as appropriate.  In the event the application wishes to read
    837  * an actual "old" utmp file (named something other than /var/adm/utmp),
    838  * calling this function with that name enables backward compatibility
    839  * mode, where we actually call the old utmp routines to operate on
    840  * the old file.
    841  */
    842 int
    843 utmpname(const char *newfile)
    844 {
    845 	char name[MAXFILE+1];
    846 
    847 	if (strlen(newfile) > MAXFILE)
    848 		return (0);
    849 
    850 	if (strcmp(newfile, "/var/adm/utmp") == 0 ||
    851 	    strcmp(newfile, "/var/adm/wtmp") == 0) {
    852 		(void) strcpy(name, newfile);
    853 		(void) strcat(name, "x");
    854 		compat_utmpflag = 0;	/* turn off old compat mode */
    855 		return (utmpxname(name));
    856 	} else {
    857 		(void) strcpy(_compat_utmpfile, newfile);
    858 		compat_utmpflag = 1;
    859 		return (1);
    860 	}
    861 }
    862 
    863 /*
    864  * Add the record to wtmpx.
    865  */
    866 void
    867 updwtmpx(const char *filex, struct utmpx *utx)
    868 {
    869 	struct futmpx futx;
    870 	int wfdx;
    871 
    872 	if ((wfdx = open(filex, O_WRONLY | O_APPEND)) < 0)
    873 		return;
    874 
    875 	(void) lseek(wfdx, 0, SEEK_END);
    876 
    877 	utmpx_api2frec(utx, &futx);
    878 	(void) write(wfdx, &futx, sizeof (futx));
    879 
    880 done:
    881 	(void) close(wfdx);
    882 }
    883 
    884 /*
    885  * Add record to wtmp (actually wtmpx). If not updating /var/adm/wtmp,
    886  * use the old utmp compatibility routine to write a utmp-format
    887  * record to the file specified.
    888  */
    889 void
    890 updwtmp(const char *file, struct utmp *ut)
    891 {
    892 	struct utmpx utmpx;
    893 	char xfile[MAXFILE + 1];
    894 
    895 	if (strcmp(file, "/var/adm/wtmp") == 0) {
    896 		(void) strlcpy(xfile, file, sizeof (xfile) - 1);
    897 		(void) strcat(xfile, "x");
    898 		getutmpx(ut, &utmpx);
    899 		updwtmpx((const char *)&xfile, &utmpx);
    900 	} else
    901 		_compat_updwtmp(file, ut);
    902 }
    903 
    904 /*
    905  * modutx - modify a utmpx entry.  Also notify init about new pids or
    906  *	old pids that it no longer needs to care about
    907  *
    908  *	args:	utp- point to utmpx structure to be created
    909  */
    910 struct utmpx *
    911 modutx(const struct utmpx *utp)
    912 {
    913 	int i;
    914 	struct utmpx utmp;		/* holding area */
    915 	struct utmpx *ucp = &utmp;	/* and a pointer to it */
    916 	struct utmpx *up;		/* "current" utmpx entry */
    917 	struct futmpx *fup;		/* being examined */
    918 
    919 	for (i = 0; i < IDLEN; ++i) {
    920 		if ((unsigned char)utp->ut_id[i] == SC_WILDC)
    921 			return (NULL);
    922 	}
    923 
    924 	/*
    925 	 * copy the supplied utmpx structure someplace safe
    926 	 */
    927 	(void) memcpy(&utmp, utp, sizeof (utmp));
    928 	setutxent();
    929 	while (fup = getutxent_frec()) {
    930 		if (idcmp(ucp->ut_id, fup->ut_id))
    931 			continue;
    932 
    933 		/*
    934 		 * only get here if ids are the same, i.e. found right entry
    935 		 */
    936 		if (ucp->ut_pid != fup->ut_pid) {
    937 			sendpid(REMPID, (pid_t)fup->ut_pid);
    938 			sendpid(ADDPID, (pid_t)ucp->ut_pid);
    939 		}
    940 		break;
    941 	}
    942 	up = pututxline(ucp);
    943 	if (ucp->ut_type == DEAD_PROCESS)
    944 		sendpid(REMPID, (pid_t)ucp->ut_pid);
    945 	if (up)
    946 		updwtmpx(WTMPX_FILE, up);
    947 	endutxent();
    948 	return (up);
    949 }
    950 
    951 /*
    952  * modut - modify a utmp entry.	 Also notify init about new pids or
    953  *	old pids that it no longer needs to care about
    954  *
    955  *	args:	utmp - point to utmp structure to be created
    956  */
    957 struct utmp *
    958 modut(struct utmp *utp)
    959 {
    960 	struct utmpx utmpx;
    961 	struct utmpx *utmpx2;
    962 
    963 	getutmpx(utp, &utmpx);
    964 	if ((utmpx2 = modutx(&utmpx)) == NULL)
    965 		return (NULL);
    966 
    967 	getutmp(utmpx2, utp);
    968 	return (utp);
    969 }
    970 
    971 /*
    972  * idcmp - compare two id strings, return  0 if same, non-zero if not *
    973  *	args:	s1 - first id string
    974  *		s2 - second id string
    975  */
    976 static int
    977 idcmp(const char *s1, const char *s2)
    978 {
    979 	int i;
    980 
    981 	for (i = 0; i < IDLEN; ++i)
    982 		if ((unsigned char) *s1 != SC_WILDC && (*s1++ != *s2++))
    983 			return (-1);
    984 	return (0);
    985 }
    986 
    987 
    988 /*
    989  * allocid - allocate an unused id for utmp, either by recycling a
    990  *	DEAD_PROCESS entry or creating a new one.  This routine only
    991  *	gets called if a wild card character was specified.
    992  *
    993  *	args:	srcid - pattern for new id
    994  *		saveid - last id matching pattern for a non-dead process
    995  */
    996 static int
    997 allocid(char *srcid, unsigned char *saveid)
    998 {
    999 	int i;		/* scratch variable */
   1000 	int changed;		/* flag to indicate that a new id has */
   1001 				/* been generated */
   1002 	char copyid[IDLEN];	/* work area */
   1003 
   1004 	(void) memcpy(copyid, srcid, IDLEN);
   1005 	changed = 0;
   1006 	for (i = 0; i < IDLEN; ++i) {
   1007 
   1008 		/*
   1009 		 * if this character isn't wild, it'll be part of the
   1010 		 * generated id
   1011 		 */
   1012 		if ((unsigned char) copyid[i] != SC_WILDC)
   1013 			continue;
   1014 
   1015 		/*
   1016 		 * it's a wild character, retrieve the character from the
   1017 		 * saved id
   1018 		 */
   1019 		copyid[i] = saveid[i];
   1020 
   1021 		/*
   1022 		 * if we haven't changed anything yet, try to find a new char
   1023 		 * to use
   1024 		 */
   1025 		if (!changed && (saveid[i] < MAXVAL)) {
   1026 
   1027 		/*
   1028 		 * Note: this algorithm is taking the "last matched" id
   1029 		 * and trying to make a 1 character change to it to create
   1030 		 * a new one.  Rather than special-case the first time
   1031 		 * (when no perturbation is really necessary), just don't
   1032 		 * allocate the first valid id.
   1033 		 */
   1034 
   1035 			while (++saveid[i] < MAXVAL) {
   1036 				/*
   1037 				 * make sure new char is alphanumeric
   1038 				 */
   1039 				if (isalnum(saveid[i])) {
   1040 					copyid[i] = saveid[i];
   1041 					changed = 1;
   1042 					break;
   1043 				}
   1044 			}
   1045 
   1046 			if (!changed) {
   1047 				/*
   1048 				 * Then 'reset' the current count at
   1049 				 * this position to it's lowest valid
   1050 				 * value, and propagate the carry to
   1051 				 * the next wild-card slot
   1052 				 *
   1053 				 * See 1113208.
   1054 				 */
   1055 				saveid[i] = 0;
   1056 				while (!isalnum(saveid[i]))
   1057 				saveid[i]++;
   1058 				copyid[i] = ++saveid[i];
   1059 			}
   1060 		}
   1061 	}
   1062 	/*
   1063 	 * changed is true if we were successful in allocating an id
   1064 	 */
   1065 	if (changed) {
   1066 		(void) memcpy(srcid, copyid, IDLEN);
   1067 		return (0);
   1068 	} else {
   1069 		return (-1);
   1070 	}
   1071 }
   1072 
   1073 
   1074 /*
   1075  * lockutx - lock utmpx file
   1076  */
   1077 static int
   1078 lockutx(void)
   1079 {
   1080 	int lockfd;
   1081 
   1082 	if ((lockfd = open(UTMPX_FILE, O_RDWR|O_CREAT, 0644)) < 0)
   1083 		return (-1);
   1084 
   1085 	if (lockf(lockfd, F_LOCK, 0) < 0) {
   1086 		(void) close(lockfd);
   1087 		return (-1);
   1088 	}
   1089 
   1090 	tempfd = fd;
   1091 	fd = lockfd;
   1092 
   1093 	return (0);
   1094 
   1095 }
   1096 
   1097 
   1098 
   1099 /*
   1100  * unlockutx - unlock utmpx file
   1101  */
   1102 static void
   1103 unlockutx(void)
   1104 {
   1105 	(void) lockf(fd, F_ULOCK, 0);
   1106 	(void) close(fd);
   1107 	fd = tempfd;
   1108 }
   1109 
   1110 
   1111 /*
   1112  * sendpid - send message to init to add or remove a pid from the
   1113  *	"godchild" list
   1114  *
   1115  *	args:	cmd - ADDPID or REMPID
   1116  *		pid - pid of "godchild"
   1117  */
   1118 static void
   1119 sendpid(int cmd, pid_t pid)
   1120 {
   1121 	int pfd;		/* file desc. for init pipe */
   1122 	pidrec_t prec;		/* place for message to be built */
   1123 
   1124 	/*
   1125 	 * if for some reason init didn't open initpipe, open it read/write
   1126 	 * here to avoid sending SIGPIPE to the calling process
   1127 	 */
   1128 	pfd = open(IPIPE, O_RDWR);
   1129 	if (pfd < 0)
   1130 		return;
   1131 	prec.pd_pid = pid;
   1132 	prec.pd_type = cmd;
   1133 	(void) write(pfd, &prec, sizeof (pidrec_t));
   1134 	(void) close(pfd);
   1135 }
   1136 
   1137 /*
   1138  * makeutx - create a utmpx entry, recycling an id if a wild card is
   1139  *	specified.  Also notify init about the new pid
   1140  *
   1141  *	args:	utmpx - point to utmpx structure to be created
   1142  */
   1143 
   1144 struct utmpx *
   1145 makeutx(const struct utmpx *utmp)
   1146 {
   1147 	struct utmpx *utp;
   1148 	struct futmpx *ut;		/* "current" utmpx being examined */
   1149 	unsigned char saveid[IDLEN];	/* the last id we matched that was */
   1150 					/* NOT a dead proc */
   1151 	int falphanum = 0x30;		/* first alpha num char */
   1152 	off_t offset;
   1153 
   1154 	/*
   1155 	 * Are any wild card char's present in the idlen string?
   1156 	 */
   1157 	if (memchr(utmp->ut_id, SC_WILDC, IDLEN) != NULL) {
   1158 		/*
   1159 		 * try to lock the utmpx file, only needed if
   1160 		 * we're doing wildcard matching
   1161 		 */
   1162 		if (lockutx())
   1163 			return (NULL);
   1164 
   1165 		/*
   1166 		 * used in allocid
   1167 		 */
   1168 		(void) memset(saveid, falphanum, IDLEN);
   1169 
   1170 		while (ut = getoneutx(&offset))
   1171 			if (idcmp(utmp->ut_id, ut->ut_id)) {
   1172 				continue;
   1173 			} else {
   1174 				/*
   1175 				 * Found a match. We are done if this is
   1176 				 * a free slot. Else record this id. We
   1177 				 * will need it to generate the next new id.
   1178 				 */
   1179 				if (ut->ut_type == DEAD_PROCESS)
   1180 					break;
   1181 				else
   1182 					(void) memcpy(saveid, ut->ut_id,
   1183 					    IDLEN);
   1184 			}
   1185 
   1186 		if (ut) {
   1187 
   1188 			/*
   1189 			 * Unused entry, reuse it. We know the offset. So
   1190 			 * just go to that offset  utmpx and write it out.
   1191 			 */
   1192 			(void) memcpy((caddr_t)utmp->ut_id, ut->ut_id, IDLEN);
   1193 
   1194 			putoneutx(utmp, offset);
   1195 			updwtmpx(WTMPX_FILE, (struct utmpx *)utmp);
   1196 			unlockutx();
   1197 			sendpid(ADDPID, (pid_t)utmp->ut_pid);
   1198 			return ((struct utmpx *)utmp);
   1199 		} else {
   1200 			/*
   1201 			 * nothing available, allocate an id and
   1202 			 * write it out at the end.
   1203 			 */
   1204 
   1205 			if (allocid((char *)utmp->ut_id, saveid)) {
   1206 				unlockutx();
   1207 				return (NULL);
   1208 			} else {
   1209 				/*
   1210 				 * Seek to end and write out the entry
   1211 				 * and also update the utmpx file.
   1212 				 */
   1213 				(void) lseek(fd, 0L, SEEK_END);
   1214 				offset = lseek(fd, 0L, SEEK_CUR);
   1215 
   1216 				putoneutx(utmp, offset);
   1217 				updwtmpx(WTMPX_FILE, (struct utmpx *)utmp);
   1218 				unlockutx();
   1219 				sendpid(ADDPID, (pid_t)utmp->ut_pid);
   1220 				return ((struct utmpx *)utmp);
   1221 			}
   1222 		}
   1223 	} else {
   1224 		utp = pututxline(utmp);
   1225 		if (utp)
   1226 			updwtmpx(WTMPX_FILE, utp);
   1227 		endutxent();
   1228 		sendpid(ADDPID, (pid_t)utmp->ut_pid);
   1229 		return (utp);
   1230 	}
   1231 }
   1232 
   1233 /*
   1234  * makeut - create a utmp entry, recycling an id if a wild card is
   1235  *	specified.  Also notify init about the new pid
   1236  *
   1237  *	args:	utmp - point to utmp structure to be created
   1238  */
   1239 struct utmp *
   1240 makeut(struct utmp *utmp)
   1241 {
   1242 	struct utmpx utmpx;
   1243 	struct utmpx *utmpx2;
   1244 
   1245 	if (compat_utmpflag)
   1246 		return (_compat_makeut(utmp));
   1247 
   1248 	getutmpx(utmp, &utmpx);
   1249 	if ((utmpx2 = makeutx(&utmpx)) == NULL)
   1250 		return (NULL);
   1251 
   1252 	getutmp(utmpx2, utmp);
   1253 	return (utmp);
   1254 }
   1255 
   1256 
   1257 #define	UTMPNBUF	200	/* Approx 8k (FS Block) size */
   1258 static struct futmpx	*utmpbuf = NULL;
   1259 
   1260 /*
   1261  * Buffered read routine to get one entry from utmpx file
   1262  */
   1263 static struct futmpx *
   1264 getoneutx(off_t *off)
   1265 {
   1266 	static	size_t idx = 0;	/* Current index in the utmpbuf */
   1267 	static	size_t nidx = 0;	/* Max entries in this utmpbuf */
   1268 	static	int nbuf = 0;	/* number of utmpbufs read from disk */
   1269 	ssize_t	nbytes, bufsz = sizeof (struct futmpx) * UTMPNBUF;
   1270 
   1271 	if (utmpbuf == NULL)
   1272 		if ((utmpbuf = malloc(bufsz)) == NULL) {
   1273 			perror("malloc");
   1274 			return (NULL);
   1275 		}
   1276 
   1277 	if (idx == nidx) {
   1278 		/*
   1279 		 *	We have read all entries in the utmpbuf. Read
   1280 		 *	the buffer from the disk.
   1281 		 */
   1282 		if ((nbytes = read(fd, utmpbuf, bufsz)) < bufsz) {
   1283 			/*
   1284 			 *	Partial read only. keep count of the
   1285 			 *	number of valid entries in the buffer
   1286 			 */
   1287 			nidx = nbytes / sizeof (struct futmpx);
   1288 		} else {
   1289 			/*
   1290 			 *	We read in the full UTMPNBUF entries
   1291 			 *	Great !
   1292 			 */
   1293 			nidx = UTMPNBUF;
   1294 		}
   1295 		nbuf++;		/* Number of buf we have read in. */
   1296 		idx = 0;	/* reset index within utmpbuf */
   1297 	}
   1298 
   1299 	/*
   1300 	 *	Current offset of this buffer in the file
   1301 	 */
   1302 	*off = (((nbuf - 1) * UTMPNBUF) + idx) * sizeof (struct futmpx);
   1303 
   1304 	if (idx < nidx) {
   1305 		/*
   1306 		 *	We still have at least one valid buffer in
   1307 		 *	utmpbuf to be passed to the caller.
   1308 		 */
   1309 		return (&utmpbuf[idx++]);
   1310 	}
   1311 
   1312 	/*
   1313 	 *	Reached EOF. Return NULL. Offset is set correctly
   1314 	 *	to append at the end of the file
   1315 	 */
   1316 
   1317 	return (NULL);
   1318 }
   1319 
   1320 static void
   1321 putoneutx(const struct utmpx *utpx, off_t off)
   1322 {
   1323 	struct	futmpx futx;
   1324 
   1325 	utmpx_api2frec(utpx, &futx);
   1326 	(void) lseek(fd, off, SEEK_SET);	/* seek in the utmpx file */
   1327 	(void) write(fd, &futx, sizeof (futx));
   1328 }
   1329 
   1330 /*
   1331  * sendupid - send message to utmpd to add or remove a pid from the
   1332  *	list of procs to watch
   1333  *
   1334  *	args:	cmd - ADDPID or REMPID
   1335  *		pid - process ID of process to watch
   1336  */
   1337 static void
   1338 sendupid(int cmd, pid_t pid)
   1339 {
   1340 	int pfd;		/* file desc. for utmp pipe */
   1341 	pidrec_t prec;		/* place for message to be built */
   1342 
   1343 	/*
   1344 	 * if for some reason utmp didn't open utmppipe, open it read/write
   1345 	 * here to avoid sending SIGPIPE to the calling process
   1346 	 */
   1347 
   1348 	pfd = open(UPIPE, O_RDWR | O_NONBLOCK | O_NDELAY);
   1349 	if (pfd < 0)
   1350 		return;
   1351 	prec.pd_pid = pid;
   1352 	prec.pd_type = cmd;
   1353 	(void) write(pfd, &prec, sizeof (pidrec_t));
   1354 	(void) close(pfd);
   1355 }
   1356 
   1357 /*
   1358  * getutmpx - convert a utmp record into a utmpx record
   1359  */
   1360 void
   1361 getutmpx(const struct utmp *ut, struct utmpx *utx)
   1362 {
   1363 	(void) memcpy(utx->ut_user, ut->ut_user, sizeof (ut->ut_user));
   1364 	(void) bzero(&utx->ut_user[sizeof (ut->ut_user)],
   1365 	    sizeof (utx->ut_user) - sizeof (ut->ut_user));
   1366 	(void) memcpy(utx->ut_line, ut->ut_line, sizeof (ut->ut_line));
   1367 	(void) bzero(&utx->ut_line[sizeof (ut->ut_line)],
   1368 	    sizeof (utx->ut_line) - sizeof (ut->ut_line));
   1369 	(void) memcpy(utx->ut_id, ut->ut_id, sizeof (ut->ut_id));
   1370 	utx->ut_pid = ut->ut_pid;
   1371 	utx->ut_type = ut->ut_type;
   1372 	utx->ut_exit = ut->ut_exit;
   1373 	utx->ut_tv.tv_sec = ut->ut_time;
   1374 	utx->ut_tv.tv_usec = 0;
   1375 	utx->ut_session = 0;
   1376 	bzero(utx->pad, sizeof (utx->pad));
   1377 	bzero(utx->ut_host, sizeof (utx->ut_host));
   1378 	utx->ut_syslen = 0;
   1379 }
   1380 
   1381 /*
   1382  * getutmp - convert a utmpx record into a utmp record
   1383  */
   1384 void
   1385 getutmp(const struct utmpx *utx, struct utmp *ut)
   1386 {
   1387 	(void) memcpy(ut->ut_user, utx->ut_user, sizeof (ut->ut_user));
   1388 	(void) memcpy(ut->ut_line, utx->ut_line, sizeof (ut->ut_line));
   1389 	(void) memcpy(ut->ut_id, utx->ut_id, sizeof (utx->ut_id));
   1390 	ut->ut_pid = utx->ut_pid;
   1391 	ut->ut_type = utx->ut_type;
   1392 	ut->ut_exit = utx->ut_exit;
   1393 	ut->ut_time = utx->ut_tv.tv_sec;
   1394 }
   1395