xref: /illumos-gate/usr/src/lib/libc/port/gen/getutx.c (revision dd72704bd9e794056c558153663c739e2012d721)
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) < 0)
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 	(void) close(wfdx);
881 }
882 
883 /*
884  * Add record to wtmp (actually wtmpx). If not updating /var/adm/wtmp,
885  * use the old utmp compatibility routine to write a utmp-format
886  * record to the file specified.
887  */
888 void
889 updwtmp(const char *file, struct utmp *ut)
890 {
891 	struct utmpx utmpx;
892 	char xfile[MAXFILE + 1];
893 
894 	if (strcmp(file, "/var/adm/wtmp") == 0) {
895 		(void) strlcpy(xfile, file, sizeof (xfile) - 1);
896 		(void) strcat(xfile, "x");
897 		getutmpx(ut, &utmpx);
898 		updwtmpx((const char *)&xfile, &utmpx);
899 	} else
900 		_compat_updwtmp(file, ut);
901 }
902 
903 /*
904  * modutx - modify a utmpx entry.  Also notify init about new pids or
905  *	old pids that it no longer needs to care about
906  *
907  *	args:	utp- point to utmpx structure to be created
908  */
909 struct utmpx *
910 modutx(const struct utmpx *utp)
911 {
912 	int i;
913 	struct utmpx utmp;		/* holding area */
914 	struct utmpx *ucp = &utmp;	/* and a pointer to it */
915 	struct utmpx *up;		/* "current" utmpx entry */
916 	struct futmpx *fup;		/* being examined */
917 
918 	for (i = 0; i < IDLEN; ++i) {
919 		if ((unsigned char)utp->ut_id[i] == SC_WILDC)
920 			return (NULL);
921 	}
922 
923 	/*
924 	 * copy the supplied utmpx structure someplace safe
925 	 */
926 	(void) memcpy(&utmp, utp, sizeof (utmp));
927 	setutxent();
928 	while ((fup = getutxent_frec()) != NULL) {
929 		if (idcmp(ucp->ut_id, fup->ut_id))
930 			continue;
931 
932 		/*
933 		 * only get here if ids are the same, i.e. found right entry
934 		 */
935 		if (ucp->ut_pid != fup->ut_pid) {
936 			sendpid(REMPID, (pid_t)fup->ut_pid);
937 			sendpid(ADDPID, (pid_t)ucp->ut_pid);
938 		}
939 		break;
940 	}
941 	up = pututxline(ucp);
942 	if (ucp->ut_type == DEAD_PROCESS)
943 		sendpid(REMPID, (pid_t)ucp->ut_pid);
944 	if (up)
945 		updwtmpx(WTMPX_FILE, up);
946 	endutxent();
947 	return (up);
948 }
949 
950 /*
951  * modut - modify a utmp entry.	 Also notify init about new pids or
952  *	old pids that it no longer needs to care about
953  *
954  *	args:	utmp - point to utmp structure to be created
955  */
956 struct utmp *
957 modut(struct utmp *utp)
958 {
959 	struct utmpx utmpx;
960 	struct utmpx *utmpx2;
961 
962 	getutmpx(utp, &utmpx);
963 	if ((utmpx2 = modutx(&utmpx)) == NULL)
964 		return (NULL);
965 
966 	getutmp(utmpx2, utp);
967 	return (utp);
968 }
969 
970 /*
971  * idcmp - compare two id strings, return  0 if same, non-zero if not *
972  *	args:	s1 - first id string
973  *		s2 - second id string
974  */
975 static int
976 idcmp(const char *s1, const char *s2)
977 {
978 	int i;
979 
980 	for (i = 0; i < IDLEN; ++i)
981 		if ((unsigned char) *s1 != SC_WILDC && (*s1++ != *s2++))
982 			return (-1);
983 	return (0);
984 }
985 
986 
987 /*
988  * allocid - allocate an unused id for utmp, either by recycling a
989  *	DEAD_PROCESS entry or creating a new one.  This routine only
990  *	gets called if a wild card character was specified.
991  *
992  *	args:	srcid - pattern for new id
993  *		saveid - last id matching pattern for a non-dead process
994  */
995 static int
996 allocid(char *srcid, unsigned char *saveid)
997 {
998 	int i;		/* scratch variable */
999 	int changed;		/* flag to indicate that a new id has */
1000 				/* been generated */
1001 	char copyid[IDLEN];	/* work area */
1002 
1003 	(void) memcpy(copyid, srcid, IDLEN);
1004 	changed = 0;
1005 	for (i = 0; i < IDLEN; ++i) {
1006 
1007 		/*
1008 		 * if this character isn't wild, it'll be part of the
1009 		 * generated id
1010 		 */
1011 		if ((unsigned char) copyid[i] != SC_WILDC)
1012 			continue;
1013 
1014 		/*
1015 		 * it's a wild character, retrieve the character from the
1016 		 * saved id
1017 		 */
1018 		copyid[i] = saveid[i];
1019 
1020 		/*
1021 		 * if we haven't changed anything yet, try to find a new char
1022 		 * to use
1023 		 */
1024 		if (!changed && (saveid[i] < MAXVAL)) {
1025 
1026 		/*
1027 		 * Note: this algorithm is taking the "last matched" id
1028 		 * and trying to make a 1 character change to it to create
1029 		 * a new one.  Rather than special-case the first time
1030 		 * (when no perturbation is really necessary), just don't
1031 		 * allocate the first valid id.
1032 		 */
1033 
1034 			while (++saveid[i] < MAXVAL) {
1035 				/*
1036 				 * make sure new char is alphanumeric
1037 				 */
1038 				if (isalnum(saveid[i])) {
1039 					copyid[i] = saveid[i];
1040 					changed = 1;
1041 					break;
1042 				}
1043 			}
1044 
1045 			if (!changed) {
1046 				/*
1047 				 * Then 'reset' the current count at
1048 				 * this position to it's lowest valid
1049 				 * value, and propagate the carry to
1050 				 * the next wild-card slot
1051 				 *
1052 				 * See 1113208.
1053 				 */
1054 				saveid[i] = 0;
1055 				while (!isalnum(saveid[i]))
1056 					saveid[i]++;
1057 				copyid[i] = ++saveid[i];
1058 			}
1059 		}
1060 	}
1061 	/*
1062 	 * changed is true if we were successful in allocating an id
1063 	 */
1064 	if (changed) {
1065 		(void) memcpy(srcid, copyid, IDLEN);
1066 		return (0);
1067 	} else {
1068 		return (-1);
1069 	}
1070 }
1071 
1072 
1073 /*
1074  * lockutx - lock utmpx file
1075  */
1076 static int
1077 lockutx(void)
1078 {
1079 	int lockfd;
1080 
1081 	if ((lockfd = open(UTMPX_FILE, O_RDWR|O_CREAT, 0644)) < 0)
1082 		return (-1);
1083 
1084 	if (lockf(lockfd, F_LOCK, 0) < 0) {
1085 		(void) close(lockfd);
1086 		return (-1);
1087 	}
1088 
1089 	tempfd = fd;
1090 	fd = lockfd;
1091 
1092 	return (0);
1093 
1094 }
1095 
1096 
1097 
1098 /*
1099  * unlockutx - unlock utmpx file
1100  */
1101 static void
1102 unlockutx(void)
1103 {
1104 	(void) lockf(fd, F_ULOCK, 0);
1105 	(void) close(fd);
1106 	fd = tempfd;
1107 }
1108 
1109 
1110 /*
1111  * sendpid - send message to init to add or remove a pid from the
1112  *	"godchild" list
1113  *
1114  *	args:	cmd - ADDPID or REMPID
1115  *		pid - pid of "godchild"
1116  */
1117 static void
1118 sendpid(int cmd, pid_t pid)
1119 {
1120 	int pfd;		/* file desc. for init pipe */
1121 	pidrec_t prec;		/* place for message to be built */
1122 
1123 	/*
1124 	 * if for some reason init didn't open initpipe, open it read/write
1125 	 * here to avoid sending SIGPIPE to the calling process
1126 	 */
1127 	pfd = open(IPIPE, O_RDWR);
1128 	if (pfd < 0)
1129 		return;
1130 	prec.pd_pid = pid;
1131 	prec.pd_type = cmd;
1132 	(void) write(pfd, &prec, sizeof (pidrec_t));
1133 	(void) close(pfd);
1134 }
1135 
1136 /*
1137  * makeutx - create a utmpx entry, recycling an id if a wild card is
1138  *	specified.  Also notify init about the new pid
1139  *
1140  *	args:	utmpx - point to utmpx structure to be created
1141  */
1142 
1143 struct utmpx *
1144 makeutx(const struct utmpx *utmp)
1145 {
1146 	struct utmpx *utp;
1147 	struct futmpx *ut;		/* "current" utmpx being examined */
1148 	unsigned char saveid[IDLEN];	/* the last id we matched that was */
1149 					/* NOT a dead proc */
1150 	int falphanum = 0x30;		/* first alpha num char */
1151 	off_t offset;
1152 
1153 	/*
1154 	 * Are any wild card char's present in the idlen string?
1155 	 */
1156 	if (memchr(utmp->ut_id, SC_WILDC, IDLEN) != NULL) {
1157 		/*
1158 		 * try to lock the utmpx file, only needed if
1159 		 * we're doing wildcard matching
1160 		 */
1161 		if (lockutx())
1162 			return (NULL);
1163 
1164 		/*
1165 		 * used in allocid
1166 		 */
1167 		(void) memset(saveid, falphanum, IDLEN);
1168 
1169 		while ((ut = getoneutx(&offset)) != NULL)
1170 			if (idcmp(utmp->ut_id, ut->ut_id)) {
1171 				continue;
1172 			} else {
1173 				/*
1174 				 * Found a match. We are done if this is
1175 				 * a free slot. Else record this id. We
1176 				 * will need it to generate the next new id.
1177 				 */
1178 				if (ut->ut_type == DEAD_PROCESS)
1179 					break;
1180 				else
1181 					(void) memcpy(saveid, ut->ut_id,
1182 					    IDLEN);
1183 			}
1184 
1185 		if (ut) {
1186 
1187 			/*
1188 			 * Unused entry, reuse it. We know the offset. So
1189 			 * just go to that offset  utmpx and write it out.
1190 			 */
1191 			(void) memcpy((caddr_t)utmp->ut_id, ut->ut_id, IDLEN);
1192 
1193 			putoneutx(utmp, offset);
1194 			updwtmpx(WTMPX_FILE, (struct utmpx *)utmp);
1195 			unlockutx();
1196 			sendpid(ADDPID, (pid_t)utmp->ut_pid);
1197 			return ((struct utmpx *)utmp);
1198 		} else {
1199 			/*
1200 			 * nothing available, allocate an id and
1201 			 * write it out at the end.
1202 			 */
1203 
1204 			if (allocid((char *)utmp->ut_id, saveid)) {
1205 				unlockutx();
1206 				return (NULL);
1207 			} else {
1208 				/*
1209 				 * Seek to end and write out the entry
1210 				 * and also update the utmpx file.
1211 				 */
1212 				(void) lseek(fd, 0L, SEEK_END);
1213 				offset = lseek(fd, 0L, SEEK_CUR);
1214 
1215 				putoneutx(utmp, offset);
1216 				updwtmpx(WTMPX_FILE, (struct utmpx *)utmp);
1217 				unlockutx();
1218 				sendpid(ADDPID, (pid_t)utmp->ut_pid);
1219 				return ((struct utmpx *)utmp);
1220 			}
1221 		}
1222 	} else {
1223 		utp = pututxline(utmp);
1224 		if (utp)
1225 			updwtmpx(WTMPX_FILE, utp);
1226 		endutxent();
1227 		sendpid(ADDPID, (pid_t)utmp->ut_pid);
1228 		return (utp);
1229 	}
1230 }
1231 
1232 /*
1233  * makeut - create a utmp entry, recycling an id if a wild card is
1234  *	specified.  Also notify init about the new pid
1235  *
1236  *	args:	utmp - point to utmp structure to be created
1237  */
1238 struct utmp *
1239 makeut(struct utmp *utmp)
1240 {
1241 	struct utmpx utmpx;
1242 	struct utmpx *utmpx2;
1243 
1244 	if (compat_utmpflag)
1245 		return (_compat_makeut(utmp));
1246 
1247 	getutmpx(utmp, &utmpx);
1248 	if ((utmpx2 = makeutx(&utmpx)) == NULL)
1249 		return (NULL);
1250 
1251 	getutmp(utmpx2, utmp);
1252 	return (utmp);
1253 }
1254 
1255 
1256 #define	UTMPNBUF	200	/* Approx 8k (FS Block) size */
1257 static struct futmpx	*utmpbuf = NULL;
1258 
1259 /*
1260  * Buffered read routine to get one entry from utmpx file
1261  */
1262 static struct futmpx *
1263 getoneutx(off_t *off)
1264 {
1265 	static	size_t idx = 0;	/* Current index in the utmpbuf */
1266 	static	size_t nidx = 0;	/* Max entries in this utmpbuf */
1267 	static	int nbuf = 0;	/* number of utmpbufs read from disk */
1268 	ssize_t	nbytes, bufsz = sizeof (struct futmpx) * UTMPNBUF;
1269 
1270 	if (utmpbuf == NULL)
1271 		if ((utmpbuf = malloc(bufsz)) == NULL) {
1272 			perror("malloc");
1273 			return (NULL);
1274 		}
1275 
1276 	if (idx == nidx) {
1277 		/*
1278 		 *	We have read all entries in the utmpbuf. Read
1279 		 *	the buffer from the disk.
1280 		 */
1281 		if ((nbytes = read(fd, utmpbuf, bufsz)) < bufsz) {
1282 			/*
1283 			 *	Partial read only. keep count of the
1284 			 *	number of valid entries in the buffer
1285 			 */
1286 			nidx = nbytes / sizeof (struct futmpx);
1287 		} else {
1288 			/*
1289 			 *	We read in the full UTMPNBUF entries
1290 			 *	Great !
1291 			 */
1292 			nidx = UTMPNBUF;
1293 		}
1294 		nbuf++;		/* Number of buf we have read in. */
1295 		idx = 0;	/* reset index within utmpbuf */
1296 	}
1297 
1298 	/*
1299 	 *	Current offset of this buffer in the file
1300 	 */
1301 	*off = (((nbuf - 1) * UTMPNBUF) + idx) * sizeof (struct futmpx);
1302 
1303 	if (idx < nidx) {
1304 		/*
1305 		 *	We still have at least one valid buffer in
1306 		 *	utmpbuf to be passed to the caller.
1307 		 */
1308 		return (&utmpbuf[idx++]);
1309 	}
1310 
1311 	/*
1312 	 *	Reached EOF. Return NULL. Offset is set correctly
1313 	 *	to append at the end of the file
1314 	 */
1315 
1316 	return (NULL);
1317 }
1318 
1319 static void
1320 putoneutx(const struct utmpx *utpx, off_t off)
1321 {
1322 	struct	futmpx futx;
1323 
1324 	utmpx_api2frec(utpx, &futx);
1325 	(void) lseek(fd, off, SEEK_SET);	/* seek in the utmpx file */
1326 	(void) write(fd, &futx, sizeof (futx));
1327 }
1328 
1329 /*
1330  * sendupid - send message to utmpd to add or remove a pid from the
1331  *	list of procs to watch
1332  *
1333  *	args:	cmd - ADDPID or REMPID
1334  *		pid - process ID of process to watch
1335  */
1336 static void
1337 sendupid(int cmd, pid_t pid)
1338 {
1339 	int pfd;		/* file desc. for utmp pipe */
1340 	pidrec_t prec;		/* place for message to be built */
1341 
1342 	/*
1343 	 * if for some reason utmp didn't open utmppipe, open it read/write
1344 	 * here to avoid sending SIGPIPE to the calling process
1345 	 */
1346 
1347 	pfd = open(UPIPE, O_RDWR | O_NONBLOCK | O_NDELAY);
1348 	if (pfd < 0)
1349 		return;
1350 	prec.pd_pid = pid;
1351 	prec.pd_type = cmd;
1352 	(void) write(pfd, &prec, sizeof (pidrec_t));
1353 	(void) close(pfd);
1354 }
1355 
1356 /*
1357  * getutmpx - convert a utmp record into a utmpx record
1358  */
1359 void
1360 getutmpx(const struct utmp *ut, struct utmpx *utx)
1361 {
1362 	(void) memcpy(utx->ut_user, ut->ut_user, sizeof (ut->ut_user));
1363 	(void) bzero(&utx->ut_user[sizeof (ut->ut_user)],
1364 	    sizeof (utx->ut_user) - sizeof (ut->ut_user));
1365 	(void) memcpy(utx->ut_line, ut->ut_line, sizeof (ut->ut_line));
1366 	(void) bzero(&utx->ut_line[sizeof (ut->ut_line)],
1367 	    sizeof (utx->ut_line) - sizeof (ut->ut_line));
1368 	(void) memcpy(utx->ut_id, ut->ut_id, sizeof (ut->ut_id));
1369 	utx->ut_pid = ut->ut_pid;
1370 	utx->ut_type = ut->ut_type;
1371 	utx->ut_exit = ut->ut_exit;
1372 	utx->ut_tv.tv_sec = ut->ut_time;
1373 	utx->ut_tv.tv_usec = 0;
1374 	utx->ut_session = 0;
1375 	bzero(utx->pad, sizeof (utx->pad));
1376 	bzero(utx->ut_host, sizeof (utx->ut_host));
1377 	utx->ut_syslen = 0;
1378 }
1379 
1380 /*
1381  * getutmp - convert a utmpx record into a utmp record
1382  */
1383 void
1384 getutmp(const struct utmpx *utx, struct utmp *ut)
1385 {
1386 	(void) memcpy(ut->ut_user, utx->ut_user, sizeof (ut->ut_user));
1387 	(void) memcpy(ut->ut_line, utx->ut_line, sizeof (ut->ut_line));
1388 	(void) memcpy(ut->ut_id, utx->ut_id, sizeof (utx->ut_id));
1389 	ut->ut_pid = utx->ut_pid;
1390 	ut->ut_type = utx->ut_type;
1391 	ut->ut_exit = utx->ut_exit;
1392 	ut->ut_time = utx->ut_tv.tv_sec;
1393 }
1394