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