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
81typedef 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
92static void	utmpx_frec2api(const struct futmpx *, struct utmpx *);
93static void	utmpx_api2frec(const struct utmpx *, struct futmpx *);
94
95static void	unlockutx(void);
96static void	sendpid(int, pid_t);
97static void	sendupid(int, pid_t);
98static int	idcmp(const char *, const char *);
99static int	allocid(char *, unsigned char *);
100static int	lockutx(void);
101
102static struct utmpx *invoke_utmp_update(const struct utmpx *);
103static struct futmpx *getoneutx(off_t *);
104static void	putoneutx(const struct utmpx *, off_t);
105static int	big_pids_in_use(void);
106
107/*
108 * prototypes for utmp compatibility routines (in getut.c)
109 */
110extern struct utmp *_compat_getutent(void);
111extern struct utmp *_compat_getutid(const struct utmp *);
112extern struct utmp *_compat_getutline(const struct utmp *);
113extern struct utmp *_compat_pututline(const struct utmp *);
114extern void _compat_setutent(void);
115extern void _compat_endutent(void);
116extern void _compat_updwtmp(const char *, struct utmp *);
117extern struct utmp *_compat_makeut(struct utmp *);
118
119static int fd = -1;	/* File descriptor for the utmpx file. */
120static int ut_got_maxpid = 0;	/* Flag set when sysconf(_SC_MAXPID) called */
121static pid_t ut_maxpid = 0;	/* Value of MAXPID from sysconf */
122static int tempfd = -1;  /* To store fd between lockutx() and unlockutx() */
123
124static	FILE	*fp = NULL;	/* Buffered file descriptior for utmpx file */
125static int changed_name = 0;	/* Flag set when not using utmpx file */
126static char utmpxfile[MAXFILE+1] = UTMPX_FILE;	/* Name of the current */
127char _compat_utmpfile[MAXFILE+1];
128static int compat_utmpflag = 0;	/* old compat mode flag */
129
130static struct futmpx fubuf;	/* Copy of last entry read in. */
131static struct utmpx ubuf;	/* Last entry returned to client */
132
133static 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 */
138static void
139utmpx_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
159static void
160utmpx_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 */
183static struct futmpx *
184getutxent_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 */
244static int
245big_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 */
257struct utmpx *
258getutxent(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 */
271struct utmp *
272getutent(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 */
296struct utmpx *
297getutxid(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 */
386struct utmp *
387getutid(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 */
411struct utmpx *
412getutxline(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 */
449struct utmp *
450getutline(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
481static struct utmpx *
482invoke_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
600out:
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
611struct utmpx *
612pututxline(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 */
701struct utmp *
702pututline(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 */
720void
721setutxent(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 */
740void
741setutent(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 */
754void
755endutxent(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 */
773void
774endutent(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 */
789int
790utmpxname(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 */
842int
843utmpname(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 */
866void
867updwtmpx(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
880done:
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 */
889void
890updwtmp(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 */
910struct utmpx *
911modutx(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 */
957struct utmp *
958modut(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 */
976static int
977idcmp(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 */
996static int
997allocid(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 */
1077static int
1078lockutx(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 */
1102static void
1103unlockutx(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 */
1118static void
1119sendpid(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
1144struct utmpx *
1145makeutx(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 */
1239struct utmp *
1240makeut(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 */
1258static struct futmpx	*utmpbuf = NULL;
1259
1260/*
1261 * Buffered read routine to get one entry from utmpx file
1262 */
1263static struct futmpx *
1264getoneutx(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
1320static void
1321putoneutx(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 */
1337static void
1338sendupid(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 */
1360void
1361getutmpx(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 */
1384void
1385getutmp(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