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