xref: /illumos-gate/usr/src/lib/libc/port/gen/getutx.c (revision 15d9d0b5)
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 #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 <pthread.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		"/var/run/initpipe"	/* FIFO to send pids to init */
96 #define	UPIPE		"/var/run/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, "rF")) == 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+F")) == 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 	posix_spawnattr_t attr;
511 	int status;
512 	int cancel_state;
513 	pid_t child;
514 	pid_t w;
515 	int i;
516 	char user[STRSZ], id[STRSZ], line[STRSZ], pid[STRSZ], type[STRSZ],
517 	    term[STRSZ], exit[STRSZ], time[STRSZ], time_usec[STRSZ],
518 	    session_id[STRSZ], syslen[32];
519 	char pad[sizeof (entryx->pad) * 2 + 1];
520 	char host[sizeof (entryx->ut_host) + 1];
521 	struct utmpx *curx = NULL;
522 	char bin2hex[] = "0123456789ABCDEF";
523 	unsigned char *cp;
524 	char *argvec[15];
525 	int error;
526 
527 	/*
528 	 * Convert the utmp struct to strings for command line arguments.
529 	 */
530 	(void) strncpy(user, entryx->ut_user, sizeof (entryx->ut_user));
531 	user[sizeof (entryx->ut_user)] = '\0';
532 	(void) strncpy(id, entryx->ut_id, sizeof (entryx->ut_id));
533 	id[sizeof (entryx->ut_id)] = '\0';
534 	(void) strncpy(line, entryx->ut_line, sizeof (entryx->ut_line));
535 	line[sizeof (entryx->ut_line)] = '\0';
536 	(void) sprintf(pid, "%d", entryx->ut_pid);
537 	(void) sprintf(type, "%d", entryx->ut_type);
538 	(void) sprintf(term, "%d", entryx->ut_exit.e_termination);
539 	(void) sprintf(exit, "%d", entryx->ut_exit.e_exit);
540 	(void) sprintf(time, "%ld", entryx->ut_tv.tv_sec);
541 	(void) sprintf(time_usec, "%ld", entryx->ut_tv.tv_usec);
542 	(void) sprintf(session_id, "%d", entryx->ut_session);
543 
544 	cp = (unsigned char *)entryx->pad;
545 	for (i = 0; i < sizeof (entryx->pad); ++i) {
546 		pad[i << 1] = bin2hex[(cp[i] >> 4) & 0xF];
547 		pad[(i << 1) + 1] = bin2hex[cp[i] & 0xF];
548 	}
549 	pad[sizeof (pad) - 1] = '\0';
550 
551 	(void) sprintf(syslen, "%d", entryx->ut_syslen);
552 	(void) strlcpy(host, entryx->ut_host, sizeof (host));
553 
554 	argvec[0] = UTMP_UPDATE;
555 	argvec[1] = user;
556 	argvec[2] = id;
557 	argvec[3] = line;
558 	argvec[4] = pid;
559 	argvec[5] = type;
560 	argvec[6] = term;
561 	argvec[7] = exit;
562 	argvec[8] = time;
563 	argvec[9] = time_usec;
564 	argvec[10] = session_id;
565 	argvec[11] = pad;
566 	argvec[12] = syslen;
567 	argvec[13] = host;
568 	argvec[14] = NULL;
569 
570 	/*
571 	 * No SIGCHLD, please, and let no one else reap our child.
572 	 */
573 	error = posix_spawnattr_init(&attr);
574 	if (error) {
575 		errno = error;
576 		goto out;
577 	}
578 	error = posix_spawnattr_setflags(&attr,
579 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
580 	if (error) {
581 		(void) posix_spawnattr_destroy(&attr);
582 		errno = error;
583 		goto out;
584 	}
585 	error = posix_spawn(&child, UTMP_UPDATE, NULL, &attr, argvec, environ);
586 	(void) posix_spawnattr_destroy(&attr);
587 	if (error) {
588 		errno = error;
589 		goto out;
590 	}
591 
592 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
593 	do {
594 		w = waitpid(child, &status, 0);
595 	} while (w == -1 && errno == EINTR);
596 	(void) pthread_setcancelstate(cancel_state, NULL);
597 
598 	/*
599 	 * We can get ECHILD if the process is ignoring SIGCLD.
600 	 */
601 	if (!(w == -1 && errno == ECHILD) &&
602 	    (w == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
603 		/*
604 		 * The child encountered an error,
605 		 */
606 		goto out;
607 	}
608 
609 	/*
610 	 * Normal termination.  Return a pointer to the entry we just made.
611 	 */
612 	setutxent();	/* Reset file pointer */
613 
614 	while ((curx = getutxent()) != NULL) {
615 		if (curx->ut_type != EMPTY &&
616 		    (curx->ut_type == LOGIN_PROCESS ||
617 		    curx->ut_type == USER_PROCESS ||
618 		    curx->ut_type == DEAD_PROCESS) &&
619 		    strncmp(&entryx->ut_line[0], &curx->ut_line[0],
620 		    sizeof (curx->ut_line)) == 0)
621 			break;
622 	}
623 
624 out:
625 	return (curx);
626 }
627 
628 /*
629  * "pututxline" writes the structure sent into the utmpx file.
630  * If there is already an entry with the same id, then it is
631  * overwritten, otherwise a new entry is made at the end of the
632  * utmpx file.
633  */
634 
635 struct utmpx *
636 pututxline(const struct utmpx *entry)
637 {
638 	struct utmpx *answer;
639 	int lock = 0;
640 	struct utmpx tmpxbuf;
641 	struct futmpx ftmpxbuf;
642 
643 	/*
644 	 * Copy the user supplied entry into our temporary buffer to
645 	 * avoid the possibility that the user is actually passing us
646 	 * the address of "ubuf".
647 	 */
648 	if (entry == NULL)
649 		return (NULL);
650 
651 	(void) memcpy(&tmpxbuf, entry, sizeof (tmpxbuf));
652 	utmpx_api2frec(entry, &ftmpxbuf);
653 
654 	if (fd < 0) {
655 		(void) getutxent_frec();
656 		if (fd < 0)
657 			return ((struct utmpx *)NULL);
658 	}
659 
660 	/*
661 	 * If we are not the superuser than we can't write to /etc/utmp,
662 	 * so invoke update_utmp(8) to write the entry for us.
663 	 */
664 	if (changed_name == 0 && geteuid() != 0)
665 		return (invoke_utmp_update(entry));
666 
667 	/*
668 	 * Find the proper entry in the utmpx file.  Start at the current
669 	 * location.  If it isn't found from here to the end of the
670 	 * file, then reset to the beginning of the file and try again.
671 	 * If it still isn't found, then write a new entry at the end of
672 	 * the file.  (Making sure the location is an integral number of
673 	 * utmp structures into the file incase the file is scribbled.)
674 	 */
675 
676 	if (getutxid(&tmpxbuf) == NULL) {
677 
678 		setutxent();
679 
680 		/*
681 		 * Lock the the entire file from here onwards.
682 		 */
683 		if (getutxid(&tmpxbuf) == NULL) {
684 			lock++;
685 			if (lockf(fd, F_LOCK, 0) < NULL)
686 				return (NULL);
687 			(void) fseek(fp, 0, SEEK_END);
688 		} else
689 			(void) fseek(fp, -(long)sizeof (struct futmpx),
690 			    SEEK_CUR);
691 	} else
692 		(void) fseek(fp, -(long)sizeof (struct futmpx), SEEK_CUR);
693 
694 	/*
695 	 * Write out the user supplied structure.  If the write fails,
696 	 * then the user probably doesn't have permission to write the
697 	 * utmpx file.
698 	 */
699 	if (fwrite(&ftmpxbuf, sizeof (ftmpxbuf), 1, fp) != 1) {
700 		answer = (struct utmpx *)NULL;
701 	} else {
702 		/*
703 		 * Save the new user structure into ubuf and fubuf so that
704 		 * it will be up to date in the future.
705 		 */
706 		(void) fflush(fp);
707 		(void) memcpy(&fubuf, &ftmpxbuf, sizeof (fubuf));
708 		utmpx_frec2api(&fubuf, &ubuf);
709 		answer = &ubuf;
710 	}
711 
712 	if (lock)
713 		(void) lockf(fd, F_ULOCK, 0);
714 
715 	if (answer != NULL && (tmpxbuf.ut_type == USER_PROCESS ||
716 	    tmpxbuf.ut_type == DEAD_PROCESS))
717 		sendupid(tmpxbuf.ut_type == USER_PROCESS ? ADDPID : REMPID,
718 		    (pid_t)tmpxbuf.ut_pid);
719 	return (answer);
720 }
721 /*
722  * "pututline" is a wrapper that calls pututxline after converting
723  * the utmp record to a utmpx record.
724  */
725 struct utmp *
726 pututline(const struct utmp *entry)
727 {
728 	struct utmpx utmpx;
729 	struct utmpx *utmpx2;
730 
731 	if (compat_utmpflag)
732 		return (_compat_pututline(entry));
733 
734 	getutmpx(entry, &utmpx);
735 	if ((utmpx2 = pututxline(&utmpx)) == NULL)
736 		return (NULL);
737 	getutmp(utmpx2, &utmpcompat);
738 	return (&utmpcompat);
739 }
740 
741 /*
742  * "setutxent" just resets the utmpx file back to the beginning.
743  */
744 void
745 setutxent(void)
746 {
747 	if (fd != -1)
748 		(void) lseek(fd, 0L, SEEK_SET);
749 
750 	if (fp != NULL)
751 		(void) fseek(fp, 0L, SEEK_SET);
752 
753 	/*
754 	 * Zero the stored copy of the last entry read, since we are
755 	 * resetting to the beginning of the file.
756 	 */
757 	bzero(&ubuf, sizeof (ubuf));
758 	bzero(&fubuf, sizeof (fubuf));
759 }
760 
761 /*
762  * "setutent" is a wrapper that calls setutxent
763  */
764 void
765 setutent(void)
766 {
767 	if (compat_utmpflag) {
768 		_compat_setutent();
769 		return;
770 	}
771 
772 	setutxent();
773 }
774 
775 /*
776  * "endutxent" closes the utmpx file.
777  */
778 void
779 endutxent(void)
780 {
781 	if (fd != -1)
782 		(void) close(fd);
783 	fd = -1;
784 
785 	if (fp != NULL)
786 		(void) fclose(fp);
787 	fp = NULL;
788 
789 	bzero(&ubuf, sizeof (ubuf));
790 	bzero(&fubuf, sizeof (fubuf));
791 }
792 
793 /*
794  * "endutent" is a wrapper that calls endutxent
795  * and clears the utmp compatibility buffer.
796  */
797 void
798 endutent(void)
799 {
800 	if (compat_utmpflag) {
801 		_compat_endutent();
802 		return;
803 	}
804 
805 	endutxent();
806 	bzero(&utmpcompat, sizeof (utmpcompat));
807 }
808 
809 /*
810  * "utmpxname" allows the user to read a file other than the
811  * normal "utmpx" file.
812  */
813 int
814 utmpxname(const char *newfile)
815 {
816 	size_t len;
817 
818 	/*
819 	 * Determine if the new filename will fit.  If not, return 0.
820 	 */
821 	if ((len = strlen(newfile)) > MAXFILE-1)
822 		return (0);
823 
824 	/*
825 	 * The name of the utmpx file has to end with 'x'
826 	 */
827 	if (newfile[len-1] != 'x')
828 		return (0);
829 
830 	/*
831 	 * Otherwise copy in the new file name.
832 	 */
833 	else
834 		(void) strcpy(&utmpxfile[0], newfile);
835 	/*
836 	 * Make sure everything is reset to the beginning state.
837 	 */
838 	endutxent();
839 
840 	/*
841 	 * If the file is being changed to /etc/utmpx or /var/adm/utmpx then
842 	 * we clear the flag so pututxline invokes utmp_update.  Otherwise
843 	 * we set the flag indicating that they changed to another name.
844 	 */
845 	if (strcmp(utmpxfile, UTMPX_FILE) == 0 ||
846 	    strcmp(utmpxfile, VAR_UTMPX_FILE) == 0)
847 		changed_name = 0;
848 	else
849 		changed_name = 1;
850 
851 	return (1);
852 }
853 
854 /*
855  * "utmpname" allows the user to read a file other than the
856  * normal "utmp" file. If the file specified is "/var/adm/utmp"
857  * or "/var/adm/wtmp", it is translated to the corresponding "utmpx"
858  * format name, and all "utmp" operations become wrapped calls
859  * to the equivalent "utmpx" routines, with data conversions
860  * as appropriate.  In the event the application wishes to read
861  * an actual "old" utmp file (named something other than /var/adm/utmp),
862  * calling this function with that name enables backward compatibility
863  * mode, where we actually call the old utmp routines to operate on
864  * the old file.
865  */
866 int
867 utmpname(const char *newfile)
868 {
869 	char name[MAXFILE+1];
870 
871 	if (strlen(newfile) > MAXFILE)
872 		return (0);
873 
874 	if (strcmp(newfile, "/var/adm/utmp") == 0 ||
875 	    strcmp(newfile, "/var/adm/wtmp") == 0) {
876 		(void) strcpy(name, newfile);
877 		(void) strcat(name, "x");
878 		compat_utmpflag = 0;	/* turn off old compat mode */
879 		return (utmpxname(name));
880 	} else {
881 		(void) strcpy(_compat_utmpfile, newfile);
882 		compat_utmpflag = 1;
883 		return (1);
884 	}
885 }
886 
887 /*
888  * Add the record to wtmpx.
889  */
890 void
891 updwtmpx(const char *filex, struct utmpx *utx)
892 {
893 	struct futmpx futx;
894 	int wfdx;
895 
896 	if ((wfdx = open(filex, O_WRONLY | O_APPEND)) < 0)
897 		return;
898 
899 	(void) lseek(wfdx, 0, SEEK_END);
900 
901 	utmpx_api2frec(utx, &futx);
902 	(void) write(wfdx, &futx, sizeof (futx));
903 
904 done:
905 	(void) close(wfdx);
906 }
907 
908 /*
909  * Add record to wtmp (actually wtmpx). If not updating /var/adm/wtmp,
910  * use the old utmp compatibility routine to write a utmp-format
911  * record to the file specified.
912  */
913 void
914 updwtmp(const char *file, struct utmp *ut)
915 {
916 	struct utmpx utmpx;
917 	char xfile[MAXFILE + 1];
918 
919 	if (strcmp(file, "/var/adm/wtmp") == 0) {
920 		(void) strlcpy(xfile, file, sizeof (xfile) - 1);
921 		(void) strcat(xfile, "x");
922 		getutmpx(ut, &utmpx);
923 		updwtmpx((const char *)&xfile, &utmpx);
924 	} else
925 		_compat_updwtmp(file, ut);
926 }
927 
928 /*
929  * modutx - modify a utmpx entry.  Also notify init about new pids or
930  *	old pids that it no longer needs to care about
931  *
932  *	args:	utp- point to utmpx structure to be created
933  */
934 struct utmpx *
935 modutx(const struct utmpx *utp)
936 {
937 	int i;
938 	struct utmpx utmp;		/* holding area */
939 	struct utmpx *ucp = &utmp;	/* and a pointer to it */
940 	struct utmpx *up;		/* "current" utmpx entry */
941 	struct futmpx *fup;		/* being examined */
942 
943 	for (i = 0; i < IDLEN; ++i) {
944 		if ((unsigned char)utp->ut_id[i] == SC_WILDC)
945 			return (NULL);
946 	}
947 
948 	/*
949 	 * copy the supplied utmpx structure someplace safe
950 	 */
951 	(void) memcpy(&utmp, utp, sizeof (utmp));
952 	setutxent();
953 	while (fup = getutxent_frec()) {
954 		if (idcmp(ucp->ut_id, fup->ut_id))
955 			continue;
956 
957 		/*
958 		 * only get here if ids are the same, i.e. found right entry
959 		 */
960 		if (ucp->ut_pid != fup->ut_pid) {
961 			sendpid(REMPID, (pid_t)fup->ut_pid);
962 			sendpid(ADDPID, (pid_t)ucp->ut_pid);
963 		}
964 		break;
965 	}
966 	up = pututxline(ucp);
967 	if (ucp->ut_type == DEAD_PROCESS)
968 		sendpid(REMPID, (pid_t)ucp->ut_pid);
969 	if (up)
970 		updwtmpx(WTMPX_FILE, up);
971 	endutxent();
972 	return (up);
973 }
974 
975 /*
976  * modut - modify a utmp entry.	 Also notify init about new pids or
977  *	old pids that it no longer needs to care about
978  *
979  *	args:	utmp - point to utmp structure to be created
980  */
981 struct utmp *
982 modut(struct utmp *utp)
983 {
984 	struct utmpx utmpx;
985 	struct utmpx *utmpx2;
986 
987 	getutmpx(utp, &utmpx);
988 	if ((utmpx2 = modutx(&utmpx)) == NULL)
989 		return (NULL);
990 
991 	getutmp(utmpx2, utp);
992 	return (utp);
993 }
994 
995 /*
996  * idcmp - compare two id strings, return  0 if same, non-zero if not *
997  *	args:	s1 - first id string
998  *		s2 - second id string
999  */
1000 static int
1001 idcmp(const char *s1, const char *s2)
1002 {
1003 	int i;
1004 
1005 	for (i = 0; i < IDLEN; ++i)
1006 		if ((unsigned char) *s1 != SC_WILDC && (*s1++ != *s2++))
1007 			return (-1);
1008 	return (0);
1009 }
1010 
1011 
1012 /*
1013  * allocid - allocate an unused id for utmp, either by recycling a
1014  *	DEAD_PROCESS entry or creating a new one.  This routine only
1015  *	gets called if a wild card character was specified.
1016  *
1017  *	args:	srcid - pattern for new id
1018  *		saveid - last id matching pattern for a non-dead process
1019  */
1020 static int
1021 allocid(char *srcid, unsigned char *saveid)
1022 {
1023 	int i;		/* scratch variable */
1024 	int changed;		/* flag to indicate that a new id has */
1025 				/* been generated */
1026 	char copyid[IDLEN];	/* work area */
1027 
1028 	(void) memcpy(copyid, srcid, IDLEN);
1029 	changed = 0;
1030 	for (i = 0; i < IDLEN; ++i) {
1031 
1032 		/*
1033 		 * if this character isn't wild, it'll be part of the
1034 		 * generated id
1035 		 */
1036 		if ((unsigned char) copyid[i] != SC_WILDC)
1037 			continue;
1038 
1039 		/*
1040 		 * it's a wild character, retrieve the character from the
1041 		 * saved id
1042 		 */
1043 		copyid[i] = saveid[i];
1044 
1045 		/*
1046 		 * if we haven't changed anything yet, try to find a new char
1047 		 * to use
1048 		 */
1049 		if (!changed && (saveid[i] < MAXVAL)) {
1050 
1051 		/*
1052 		 * Note: this algorithm is taking the "last matched" id
1053 		 * and trying to make a 1 character change to it to create
1054 		 * a new one.  Rather than special-case the first time
1055 		 * (when no perturbation is really necessary), just don't
1056 		 * allocate the first valid id.
1057 		 */
1058 
1059 			while (++saveid[i] < MAXVAL) {
1060 				/*
1061 				 * make sure new char is alphanumeric
1062 				 */
1063 				if (isalnum(saveid[i])) {
1064 					copyid[i] = saveid[i];
1065 					changed = 1;
1066 					break;
1067 				}
1068 			}
1069 
1070 			if (!changed) {
1071 				/*
1072 				 * Then 'reset' the current count at
1073 				 * this position to it's lowest valid
1074 				 * value, and propagate the carry to
1075 				 * the next wild-card slot
1076 				 *
1077 				 * See 1113208.
1078 				 */
1079 				saveid[i] = 0;
1080 				while (!isalnum(saveid[i]))
1081 				saveid[i]++;
1082 				copyid[i] = ++saveid[i];
1083 			}
1084 		}
1085 	}
1086 	/*
1087 	 * changed is true if we were successful in allocating an id
1088 	 */
1089 	if (changed) {
1090 		(void) memcpy(srcid, copyid, IDLEN);
1091 		return (0);
1092 	} else {
1093 		return (-1);
1094 	}
1095 }
1096 
1097 
1098 /*
1099  * lockutx - lock utmpx file
1100  */
1101 static int
1102 lockutx(void)
1103 {
1104 	int lockfd;
1105 
1106 	if ((lockfd = open(UTMPX_FILE, O_RDWR|O_CREAT, 0644)) < 0)
1107 		return (-1);
1108 
1109 	if (lockf(lockfd, F_LOCK, 0) < 0) {
1110 		(void) close(lockfd);
1111 		return (-1);
1112 	}
1113 
1114 	tempfd = fd;
1115 	fd = lockfd;
1116 
1117 	return (0);
1118 
1119 }
1120 
1121 
1122 
1123 /*
1124  * unlockutx - unlock utmpx file
1125  */
1126 static void
1127 unlockutx(void)
1128 {
1129 	(void) lockf(fd, F_ULOCK, 0);
1130 	(void) close(fd);
1131 	fd = tempfd;
1132 }
1133 
1134 
1135 /*
1136  * sendpid - send message to init to add or remove a pid from the
1137  *	"godchild" list
1138  *
1139  *	args:	cmd - ADDPID or REMPID
1140  *		pid - pid of "godchild"
1141  */
1142 static void
1143 sendpid(int cmd, pid_t pid)
1144 {
1145 	int pfd;		/* file desc. for init pipe */
1146 	pidrec_t prec;		/* place for message to be built */
1147 
1148 	/*
1149 	 * if for some reason init didn't open initpipe, open it read/write
1150 	 * here to avoid sending SIGPIPE to the calling process
1151 	 */
1152 	pfd = open(IPIPE, O_RDWR);
1153 	if (pfd < 0)
1154 		return;
1155 	prec.pd_pid = pid;
1156 	prec.pd_type = cmd;
1157 	(void) write(pfd, &prec, sizeof (pidrec_t));
1158 	(void) close(pfd);
1159 }
1160 
1161 /*
1162  * makeutx - create a utmpx entry, recycling an id if a wild card is
1163  *	specified.  Also notify init about the new pid
1164  *
1165  *	args:	utmpx - point to utmpx structure to be created
1166  */
1167 
1168 struct utmpx *
1169 makeutx(const struct utmpx *utmp)
1170 {
1171 	struct utmpx *utp;
1172 	struct futmpx *ut;		/* "current" utmpx being examined */
1173 	unsigned char saveid[IDLEN];	/* the last id we matched that was */
1174 					/* NOT a dead proc */
1175 	int falphanum = 0x30;		/* first alpha num char */
1176 	off_t offset;
1177 
1178 	/*
1179 	 * Are any wild card char's present in the idlen string?
1180 	 */
1181 	if (memchr(utmp->ut_id, SC_WILDC, IDLEN) != NULL) {
1182 		/*
1183 		 * try to lock the utmpx file, only needed if
1184 		 * we're doing wildcard matching
1185 		 */
1186 		if (lockutx())
1187 			return (NULL);
1188 
1189 		/*
1190 		 * used in allocid
1191 		 */
1192 		(void) memset(saveid, falphanum, IDLEN);
1193 
1194 		while (ut = getoneutx(&offset))
1195 			if (idcmp(utmp->ut_id, ut->ut_id)) {
1196 				continue;
1197 			} else {
1198 				/*
1199 				 * Found a match. We are done if this is
1200 				 * a free slot. Else record this id. We
1201 				 * will need it to generate the next new id.
1202 				 */
1203 				if (ut->ut_type == DEAD_PROCESS)
1204 					break;
1205 				else
1206 					(void) memcpy(saveid, ut->ut_id,
1207 					    IDLEN);
1208 			}
1209 
1210 		if (ut) {
1211 
1212 			/*
1213 			 * Unused entry, reuse it. We know the offset. So
1214 			 * just go to that offset  utmpx and write it out.
1215 			 */
1216 			(void) memcpy((caddr_t)utmp->ut_id, ut->ut_id, IDLEN);
1217 
1218 			putoneutx(utmp, offset);
1219 			updwtmpx(WTMPX_FILE, (struct utmpx *)utmp);
1220 			unlockutx();
1221 			sendpid(ADDPID, (pid_t)utmp->ut_pid);
1222 			return ((struct utmpx *)utmp);
1223 		} else {
1224 			/*
1225 			 * nothing available, allocate an id and
1226 			 * write it out at the end.
1227 			 */
1228 
1229 			if (allocid((char *)utmp->ut_id, saveid)) {
1230 				unlockutx();
1231 				return (NULL);
1232 			} else {
1233 				/*
1234 				 * Seek to end and write out the entry
1235 				 * and also update the utmpx file.
1236 				 */
1237 				(void) lseek(fd, 0L, SEEK_END);
1238 				offset = lseek(fd, 0L, SEEK_CUR);
1239 
1240 				putoneutx(utmp, offset);
1241 				updwtmpx(WTMPX_FILE, (struct utmpx *)utmp);
1242 				unlockutx();
1243 				sendpid(ADDPID, (pid_t)utmp->ut_pid);
1244 				return ((struct utmpx *)utmp);
1245 			}
1246 		}
1247 	} else {
1248 		utp = pututxline(utmp);
1249 		if (utp)
1250 			updwtmpx(WTMPX_FILE, utp);
1251 		endutxent();
1252 		sendpid(ADDPID, (pid_t)utmp->ut_pid);
1253 		return (utp);
1254 	}
1255 }
1256 
1257 /*
1258  * makeut - create a utmp entry, recycling an id if a wild card is
1259  *	specified.  Also notify init about the new pid
1260  *
1261  *	args:	utmp - point to utmp structure to be created
1262  */
1263 struct utmp *
1264 makeut(struct utmp *utmp)
1265 {
1266 	struct utmpx utmpx;
1267 	struct utmpx *utmpx2;
1268 
1269 	if (compat_utmpflag)
1270 		return (_compat_makeut(utmp));
1271 
1272 	getutmpx(utmp, &utmpx);
1273 	if ((utmpx2 = makeutx(&utmpx)) == NULL)
1274 		return (NULL);
1275 
1276 	getutmp(utmpx2, utmp);
1277 	return (utmp);
1278 }
1279 
1280 
1281 #define	UTMPNBUF	200	/* Approx 8k (FS Block) size */
1282 static struct futmpx	*utmpbuf = NULL;
1283 
1284 /*
1285  * Buffered read routine to get one entry from utmpx file
1286  */
1287 static struct futmpx *
1288 getoneutx(off_t *off)
1289 {
1290 	static	size_t idx = 0;	/* Current index in the utmpbuf */
1291 	static	size_t nidx = 0;	/* Max entries in this utmpbuf */
1292 	static	int nbuf = 0;	/* number of utmpbufs read from disk */
1293 	ssize_t	nbytes, bufsz = sizeof (struct futmpx) * UTMPNBUF;
1294 
1295 	if (utmpbuf == NULL)
1296 		if ((utmpbuf = malloc(bufsz)) == NULL) {
1297 			perror("malloc");
1298 			return (NULL);
1299 		}
1300 
1301 	if (idx == nidx) {
1302 		/*
1303 		 *	We have read all entries in the utmpbuf. Read
1304 		 *	the buffer from the disk.
1305 		 */
1306 		if ((nbytes = read(fd, utmpbuf, bufsz)) < bufsz) {
1307 			/*
1308 			 *	Partial read only. keep count of the
1309 			 *	number of valid entries in the buffer
1310 			 */
1311 			nidx = nbytes / sizeof (struct futmpx);
1312 		} else {
1313 			/*
1314 			 *	We read in the full UTMPNBUF entries
1315 			 *	Great !
1316 			 */
1317 			nidx = UTMPNBUF;
1318 		}
1319 		nbuf++;		/* Number of buf we have read in. */
1320 		idx = 0;	/* reset index within utmpbuf */
1321 	}
1322 
1323 	/*
1324 	 *	Current offset of this buffer in the file
1325 	 */
1326 	*off = (((nbuf - 1) * UTMPNBUF) + idx) * sizeof (struct futmpx);
1327 
1328 	if (idx < nidx) {
1329 		/*
1330 		 *	We still have at least one valid buffer in
1331 		 *	utmpbuf to be passed to the caller.
1332 		 */
1333 		return (&utmpbuf[idx++]);
1334 	}
1335 
1336 	/*
1337 	 *	Reached EOF. Return NULL. Offset is set correctly
1338 	 *	to append at the end of the file
1339 	 */
1340 
1341 	return (NULL);
1342 }
1343 
1344 static void
1345 putoneutx(const struct utmpx *utpx, off_t off)
1346 {
1347 	struct	futmpx futx;
1348 
1349 	utmpx_api2frec(utpx, &futx);
1350 	(void) lseek(fd, off, SEEK_SET);	/* seek in the utmpx file */
1351 	(void) write(fd, &futx, sizeof (futx));
1352 }
1353 
1354 /*
1355  * sendupid - send message to utmpd to add or remove a pid from the
1356  *	list of procs to watch
1357  *
1358  *	args:	cmd - ADDPID or REMPID
1359  *		pid - process ID of process to watch
1360  */
1361 static void
1362 sendupid(int cmd, pid_t pid)
1363 {
1364 	int pfd;		/* file desc. for utmp pipe */
1365 	pidrec_t prec;		/* place for message to be built */
1366 
1367 	/*
1368 	 * if for some reason utmp didn't open utmppipe, open it read/write
1369 	 * here to avoid sending SIGPIPE to the calling process
1370 	 */
1371 
1372 	pfd = open(UPIPE, O_RDWR | O_NONBLOCK | O_NDELAY);
1373 	if (pfd < 0)
1374 		return;
1375 	prec.pd_pid = pid;
1376 	prec.pd_type = cmd;
1377 	(void) write(pfd, &prec, sizeof (pidrec_t));
1378 	(void) close(pfd);
1379 }
1380 
1381 /*
1382  * getutmpx - convert a utmp record into a utmpx record
1383  */
1384 void
1385 getutmpx(const struct utmp *ut, struct utmpx *utx)
1386 {
1387 	(void) memcpy(utx->ut_user, ut->ut_user, sizeof (ut->ut_user));
1388 	(void) bzero(&utx->ut_user[sizeof (ut->ut_user)],
1389 	    sizeof (utx->ut_user) - sizeof (ut->ut_user));
1390 	(void) memcpy(utx->ut_line, ut->ut_line, sizeof (ut->ut_line));
1391 	(void) bzero(&utx->ut_line[sizeof (ut->ut_line)],
1392 	    sizeof (utx->ut_line) - sizeof (ut->ut_line));
1393 	(void) memcpy(utx->ut_id, ut->ut_id, sizeof (ut->ut_id));
1394 	utx->ut_pid = ut->ut_pid;
1395 	utx->ut_type = ut->ut_type;
1396 	utx->ut_exit = ut->ut_exit;
1397 	utx->ut_tv.tv_sec = ut->ut_time;
1398 	utx->ut_tv.tv_usec = 0;
1399 	utx->ut_session = 0;
1400 	bzero(utx->pad, sizeof (utx->pad));
1401 	bzero(utx->ut_host, sizeof (utx->ut_host));
1402 	utx->ut_syslen = 0;
1403 }
1404 
1405 /*
1406  * getutmp - convert a utmpx record into a utmp record
1407  */
1408 void
1409 getutmp(const struct utmpx *utx, struct utmp *ut)
1410 {
1411 	(void) memcpy(ut->ut_user, utx->ut_user, sizeof (ut->ut_user));
1412 	(void) memcpy(ut->ut_line, utx->ut_line, sizeof (ut->ut_line));
1413 	(void) memcpy(ut->ut_id, utx->ut_id, sizeof (utx->ut_id));
1414 	ut->ut_pid = utx->ut_pid;
1415 	ut->ut_type = utx->ut_type;
1416 	ut->ut_exit = utx->ut_exit;
1417 	ut->ut_time = utx->ut_tv.tv_sec;
1418 }
1419