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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1988 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
42 * Routines to read and write the /etc/utmpx file. Also contains
43 * binary compatibility routines to support the old utmp interfaces
44 * on systems with MAXPID <= SHRT_MAX.
48 #include <sys/types.h>
50 #include <sys/param.h>
66 #define IDLEN 4 /* length of id field in utmp */
67 #define SC_WILDC 0xff /* wild char for utmp ids */
68 #define MAXFILE 79 /* Maximum pathname length for "utmpx" file */
70 #define MAXVAL 255 /* max value for an id `character' */
71 #define IPIPE "/var/run/initpipe" /* FIFO to send pids to init */
72 #define UPIPE "/var/run/utmppipe" /* FIFO to send pids to utmpd */
74 #define VAR_UTMPX_FILE "/var/adm/utmpx" /* for sanity check only */
78 * format of message sent to init
81 typedef struct pidrec
{
82 int pd_type
; /* command type */
83 pid_t pd_pid
; /* pid */
89 #define ADDPID 1 /* add a pid to "godchild" list */
90 #define REMPID 2 /* remove a pid to "godchild" list */
92 static void utmpx_frec2api(const struct futmpx
*, struct utmpx
*);
93 static void utmpx_api2frec(const struct utmpx
*, struct futmpx
*);
95 static void unlockutx(void);
96 static void sendpid(int, pid_t
);
97 static void sendupid(int, pid_t
);
98 static int idcmp(const char *, const char *);
99 static int allocid(char *, unsigned char *);
100 static int lockutx(void);
102 static struct utmpx
*invoke_utmp_update(const struct utmpx
*);
103 static struct futmpx
*getoneutx(off_t
*);
104 static void putoneutx(const struct utmpx
*, off_t
);
105 static int big_pids_in_use(void);
108 * prototypes for utmp compatibility routines (in getut.c)
110 extern struct utmp
*_compat_getutent(void);
111 extern struct utmp
*_compat_getutid(const struct utmp
*);
112 extern struct utmp
*_compat_getutline(const struct utmp
*);
113 extern struct utmp
*_compat_pututline(const struct utmp
*);
114 extern void _compat_setutent(void);
115 extern void _compat_endutent(void);
116 extern void _compat_updwtmp(const char *, struct utmp
*);
117 extern struct utmp
*_compat_makeut(struct utmp
*);
119 static int fd
= -1; /* File descriptor for the utmpx file. */
120 static int ut_got_maxpid
= 0; /* Flag set when sysconf(_SC_MAXPID) called */
121 static pid_t ut_maxpid
= 0; /* Value of MAXPID from sysconf */
122 static int tempfd
= -1; /* To store fd between lockutx() and unlockutx() */
124 static FILE *fp
= NULL
; /* Buffered file descriptior for utmpx file */
125 static int changed_name
= 0; /* Flag set when not using utmpx file */
126 static char utmpxfile
[MAXFILE
+1] = UTMPX_FILE
; /* Name of the current */
127 char _compat_utmpfile
[MAXFILE
+1];
128 static int compat_utmpflag
= 0; /* old compat mode flag */
130 static struct futmpx fubuf
; /* Copy of last entry read in. */
131 static struct utmpx ubuf
; /* Last entry returned to client */
133 static struct utmp utmpcompat
; /* Buffer for returning utmp-format data */
135 * In the 64-bit world, the utmpx data structure grows because of
136 * the ut_time field (a struct timeval) grows in the middle of it.
139 utmpx_frec2api(const struct futmpx
*src
, struct utmpx
*dst
)
144 (void) strncpy(dst
->ut_user
, src
->ut_user
, sizeof (dst
->ut_user
));
145 (void) strncpy(dst
->ut_line
, src
->ut_line
, sizeof (dst
->ut_line
));
146 (void) memcpy(dst
->ut_id
, src
->ut_id
, sizeof (dst
->ut_id
));
147 dst
->ut_pid
= src
->ut_pid
;
148 dst
->ut_type
= src
->ut_type
;
149 dst
->ut_exit
.e_termination
= src
->ut_exit
.e_termination
;
150 dst
->ut_exit
.e_exit
= src
->ut_exit
.e_exit
;
151 dst
->ut_tv
.tv_sec
= (time_t)src
->ut_tv
.tv_sec
;
152 dst
->ut_tv
.tv_usec
= (suseconds_t
)src
->ut_tv
.tv_usec
;
153 dst
->ut_session
= src
->ut_session
;
154 bzero(dst
->pad
, sizeof (dst
->pad
));
155 dst
->ut_syslen
= src
->ut_syslen
;
156 (void) memcpy(dst
->ut_host
, src
->ut_host
, sizeof (dst
->ut_host
));
160 utmpx_api2frec(const struct utmpx
*src
, struct futmpx
*dst
)
165 (void) strncpy(dst
->ut_user
, src
->ut_user
, sizeof (dst
->ut_user
));
166 (void) strncpy(dst
->ut_line
, src
->ut_line
, sizeof (dst
->ut_line
));
167 (void) memcpy(dst
->ut_id
, src
->ut_id
, sizeof (dst
->ut_id
));
168 dst
->ut_pid
= src
->ut_pid
;
169 dst
->ut_type
= src
->ut_type
;
170 dst
->ut_exit
.e_termination
= src
->ut_exit
.e_termination
;
171 dst
->ut_exit
.e_exit
= src
->ut_exit
.e_exit
;
172 dst
->ut_tv
.tv_sec
= (time32_t
)src
->ut_tv
.tv_sec
;
173 dst
->ut_tv
.tv_usec
= (int32_t)src
->ut_tv
.tv_usec
;
174 dst
->ut_session
= src
->ut_session
;
175 bzero(dst
->pad
, sizeof (dst
->pad
));
176 dst
->ut_syslen
= src
->ut_syslen
;
177 (void) memcpy(dst
->ut_host
, src
->ut_host
, sizeof (dst
->ut_host
));
181 * "getutxent_frec" gets the raw version of the next entry in the utmpx file.
183 static struct futmpx
*
187 * If the "utmpx" file is not open, attempt to open it for
188 * reading. If there is no file, attempt to create one. If
189 * both attempts fail, return NULL. If the file exists, but
190 * isn't readable and writeable, do not attempt to create.
194 if ((fd
= open(utmpxfile
, O_RDWR
|O_CREAT
, 0644)) < 0) {
197 * If the open failed for permissions, try opening
198 * it only for reading. All "pututxline()" later
199 * will fail the writes.
202 if ((fd
= open(utmpxfile
, O_RDONLY
)) < 0)
205 if ((fp
= fopen(utmpxfile
, "rF")) == NULL
) {
213 * Get the stream pointer
215 if ((fp
= fopen(utmpxfile
, "r+F")) == NULL
) {
224 * Try to read in the next entry from the utmpx file.
226 if (fread(&fubuf
, sizeof (fubuf
), 1, fp
) != 1) {
228 * Make sure fubuf is zeroed.
230 bzero(&fubuf
, sizeof (fubuf
));
238 * "big_pids_in_use" determines whether large pid numbers are in use
239 * or not. If MAXPID won't fit in a signed short, the utmp.ut_pid
240 * field will overflow.
242 * Returns 0 if small pids are in use, 1 otherwise
245 big_pids_in_use(void)
247 if (!ut_got_maxpid
) {
249 ut_maxpid
= sysconf(_SC_MAXPID
);
251 return (ut_maxpid
> SHRT_MAX
? 1 : 0);
255 * "getutxent" gets the next entry in the utmpx file.
260 struct futmpx
*futxp
;
262 futxp
= getutxent_frec();
263 utmpx_frec2api(&fubuf
, &ubuf
);
269 * "getutent" gets the next entry in the utmp file.
277 return (_compat_getutent());
279 /* fail if we can't represent maxpid properly */
280 if (big_pids_in_use()) {
285 if ((utmpx
= getutxent()) == NULL
)
288 getutmp(utmpx
, &utmpcompat
);
289 return (&utmpcompat
);
293 * "getutxid" finds the specified entry in the utmpx file. If
294 * it can't find it, it returns NULL.
297 getutxid(const struct utmpx
*entry
)
302 * From XPG5: "The getutxid() or getutxline() may cache data.
303 * For this reason, to use getutxline() to search for multiple
304 * occurrences, it is necessary to zero out the static data after
305 * each success, or getutxline() could just return a pointer to
306 * the same utmpx structure over and over again."
308 utmpx_api2frec(&ubuf
, &fubuf
);
311 * Start looking for entry. Look in our current buffer before
312 * reading in new entries.
316 * If there is no entry in "fubuf", skip to the read.
318 if (fubuf
.ut_type
!= EMPTY
) {
319 switch (entry
->ut_type
) {
322 * Do not look for an entry if the user sent
329 * For RUN_LVL, BOOT_TIME, OLD_TIME, and NEW_TIME
330 * entries, only the types have to match. If they do,
331 * return the address of internal buffer.
338 if (entry
->ut_type
== fubuf
.ut_type
) {
339 utmpx_frec2api(&fubuf
, &ubuf
);
345 * For INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS,
346 * and DEAD_PROCESS the type of the entry in "fubuf",
347 * must be one of the above and id's must match.
353 if (((type
= fubuf
.ut_type
) == INIT_PROCESS
||
354 type
== LOGIN_PROCESS
||
355 type
== USER_PROCESS
||
356 type
== DEAD_PROCESS
) &&
357 (fubuf
.ut_id
[0] == entry
->ut_id
[0]) &&
358 (fubuf
.ut_id
[1] == entry
->ut_id
[1]) &&
359 (fubuf
.ut_id
[2] == entry
->ut_id
[2]) &&
360 (fubuf
.ut_id
[3] == entry
->ut_id
[3])) {
361 utmpx_frec2api(&fubuf
, &ubuf
);
367 * Do not search for illegal types of entry.
373 } while (getutxent_frec() != NULL
);
376 * Return NULL since the proper entry wasn't found.
378 utmpx_frec2api(&fubuf
, &ubuf
);
383 * "getutid" finds the specified entry in the utmp file. If
384 * it can't find it, it returns NULL.
387 getutid(const struct utmp
*entry
)
390 struct utmpx
*utmpx2
;
393 return (_compat_getutid(entry
));
395 /* fail if we can't represent maxpid properly */
396 if (big_pids_in_use()) {
400 getutmpx(entry
, &utmpx
);
401 if ((utmpx2
= getutxid(&utmpx
)) == NULL
)
403 getutmp(utmpx2
, &utmpcompat
);
404 return (&utmpcompat
);
408 * "getutxline" searches the "utmpx" file for a LOGIN_PROCESS or
409 * USER_PROCESS with the same "line" as the specified "entry".
412 getutxline(const struct utmpx
*entry
)
415 * From XPG5: "The getutxid() or getutxline() may cache data.
416 * For this reason, to use getutxline() to search for multiple
417 * occurrences, it is necessary to zero out the static data after
418 * each success, or getutxline() could just return a pointer to
419 * the same utmpx structure over and over again."
421 utmpx_api2frec(&ubuf
, &fubuf
);
425 * If the current entry is the one we are interested in,
426 * return a pointer to it.
428 if (fubuf
.ut_type
!= EMPTY
&&
429 (fubuf
.ut_type
== LOGIN_PROCESS
||
430 fubuf
.ut_type
== USER_PROCESS
) &&
431 strncmp(&entry
->ut_line
[0], &fubuf
.ut_line
[0],
432 sizeof (fubuf
.ut_line
)) == 0) {
433 utmpx_frec2api(&fubuf
, &ubuf
);
436 } while (getutxent_frec() != NULL
);
439 * Since entry wasn't found, return NULL.
441 utmpx_frec2api(&fubuf
, &ubuf
);
446 * "getutline" searches the "utmp" file for a LOGIN_PROCESS or
447 * USER_PROCESS with the same "line" as the specified "entry".
450 getutline(const struct utmp
*entry
)
453 struct utmpx
*utmpx2
;
456 return (_compat_getutline(entry
));
458 /* fail if we can't represent maxpid properly */
459 if (big_pids_in_use()) {
463 /* call getutxline */
464 getutmpx(entry
, &utmpx
);
465 if ((utmpx2
= getutxline(&utmpx
)) == NULL
)
467 getutmp(utmpx2
, &utmpcompat
);
468 return (&utmpcompat
);
474 * Invokes the utmp_update program which has the privilege to write
475 * to the /etc/utmp file.
478 #define UTMP_UPDATE "/usr/lib/utmp_update"
479 #define STRSZ 64 /* Size of char buffer for argument strings */
481 static struct utmpx
*
482 invoke_utmp_update(const struct utmpx
*entryx
)
484 extern char **_environ
;
486 posix_spawnattr_t attr
;
492 char user
[STRSZ
], id
[STRSZ
], line
[STRSZ
], pid
[STRSZ
], type
[STRSZ
],
493 term
[STRSZ
], exit
[STRSZ
], time
[STRSZ
], time_usec
[STRSZ
],
494 session_id
[STRSZ
], syslen
[32];
495 char pad
[sizeof (entryx
->pad
) * 2 + 1];
496 char host
[sizeof (entryx
->ut_host
) + 1];
497 struct utmpx
*curx
= NULL
;
498 char bin2hex
[] = "0123456789ABCDEF";
504 * Convert the utmp struct to strings for command line arguments.
506 (void) strncpy(user
, entryx
->ut_user
, sizeof (entryx
->ut_user
));
507 user
[sizeof (entryx
->ut_user
)] = '\0';
508 (void) strncpy(id
, entryx
->ut_id
, sizeof (entryx
->ut_id
));
509 id
[sizeof (entryx
->ut_id
)] = '\0';
510 (void) strncpy(line
, entryx
->ut_line
, sizeof (entryx
->ut_line
));
511 line
[sizeof (entryx
->ut_line
)] = '\0';
512 (void) sprintf(pid
, "%d", (int)entryx
->ut_pid
);
513 (void) sprintf(type
, "%d", entryx
->ut_type
);
514 (void) sprintf(term
, "%d", entryx
->ut_exit
.e_termination
);
515 (void) sprintf(exit
, "%d", entryx
->ut_exit
.e_exit
);
516 (void) sprintf(time
, "%ld", entryx
->ut_tv
.tv_sec
);
517 (void) sprintf(time_usec
, "%ld", entryx
->ut_tv
.tv_usec
);
518 (void) sprintf(session_id
, "%d", entryx
->ut_session
);
520 cp
= (unsigned char *)entryx
->pad
;
521 for (i
= 0; i
< sizeof (entryx
->pad
); ++i
) {
522 pad
[i
<< 1] = bin2hex
[(cp
[i
] >> 4) & 0xF];
523 pad
[(i
<< 1) + 1] = bin2hex
[cp
[i
] & 0xF];
525 pad
[sizeof (pad
) - 1] = '\0';
527 (void) sprintf(syslen
, "%d", entryx
->ut_syslen
);
528 (void) strlcpy(host
, entryx
->ut_host
, sizeof (host
));
530 argvec
[0] = UTMP_UPDATE
;
539 argvec
[9] = time_usec
;
540 argvec
[10] = session_id
;
547 * No SIGCHLD, please, and let no one else reap our child.
549 error
= posix_spawnattr_init(&attr
);
554 error
= posix_spawnattr_setflags(&attr
,
555 POSIX_SPAWN_NOSIGCHLD_NP
| POSIX_SPAWN_WAITPID_NP
);
557 (void) posix_spawnattr_destroy(&attr
);
561 error
= posix_spawn(&child
, UTMP_UPDATE
, NULL
, &attr
, argvec
, _environ
);
562 (void) posix_spawnattr_destroy(&attr
);
568 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, &cancel_state
);
570 w
= waitpid(child
, &status
, 0);
571 } while (w
== -1 && errno
== EINTR
);
572 (void) pthread_setcancelstate(cancel_state
, NULL
);
575 * We can get ECHILD if the process is ignoring SIGCLD.
577 if (!(w
== -1 && errno
== ECHILD
) &&
578 (w
== -1 || !WIFEXITED(status
) || WEXITSTATUS(status
) != 0)) {
580 * The child encountered an error,
586 * Normal termination. Return a pointer to the entry we just made.
588 setutxent(); /* Reset file pointer */
590 while ((curx
= getutxent()) != NULL
) {
591 if (curx
->ut_type
!= EMPTY
&&
592 (curx
->ut_type
== LOGIN_PROCESS
||
593 curx
->ut_type
== USER_PROCESS
||
594 curx
->ut_type
== DEAD_PROCESS
) &&
595 strncmp(&entryx
->ut_line
[0], &curx
->ut_line
[0],
596 sizeof (curx
->ut_line
)) == 0)
605 * "pututxline" writes the structure sent into the utmpx file.
606 * If there is already an entry with the same id, then it is
607 * overwritten, otherwise a new entry is made at the end of the
612 pututxline(const struct utmpx
*entry
)
614 struct utmpx
*answer
;
616 struct utmpx tmpxbuf
;
617 struct futmpx ftmpxbuf
;
620 * Copy the user supplied entry into our temporary buffer to
621 * avoid the possibility that the user is actually passing us
622 * the address of "ubuf".
627 (void) memcpy(&tmpxbuf
, entry
, sizeof (tmpxbuf
));
628 utmpx_api2frec(entry
, &ftmpxbuf
);
631 (void) getutxent_frec();
637 * If we are not the superuser than we can't write to /etc/utmp,
638 * so invoke update_utmp(8) to write the entry for us.
640 if (changed_name
== 0 && geteuid() != 0)
641 return (invoke_utmp_update(entry
));
644 * Find the proper entry in the utmpx file. Start at the current
645 * location. If it isn't found from here to the end of the
646 * file, then reset to the beginning of the file and try again.
647 * If it still isn't found, then write a new entry at the end of
648 * the file. (Making sure the location is an integral number of
649 * utmp structures into the file incase the file is scribbled.)
652 if (getutxid(&tmpxbuf
) == NULL
) {
657 * Lock the the entire file from here onwards.
659 if (getutxid(&tmpxbuf
) == NULL
) {
661 if (lockf(fd
, F_LOCK
, 0) < 0)
663 (void) fseek(fp
, 0, SEEK_END
);
665 (void) fseek(fp
, -(long)sizeof (struct futmpx
),
668 (void) fseek(fp
, -(long)sizeof (struct futmpx
), SEEK_CUR
);
671 * Write out the user supplied structure. If the write fails,
672 * then the user probably doesn't have permission to write the
675 if (fwrite(&ftmpxbuf
, sizeof (ftmpxbuf
), 1, fp
) != 1) {
679 * Save the new user structure into ubuf and fubuf so that
680 * it will be up to date in the future.
683 (void) memcpy(&fubuf
, &ftmpxbuf
, sizeof (fubuf
));
684 utmpx_frec2api(&fubuf
, &ubuf
);
689 (void) lockf(fd
, F_ULOCK
, 0);
691 if (answer
!= NULL
&& (tmpxbuf
.ut_type
== USER_PROCESS
||
692 tmpxbuf
.ut_type
== DEAD_PROCESS
))
693 sendupid(tmpxbuf
.ut_type
== USER_PROCESS
? ADDPID
: REMPID
,
694 (pid_t
)tmpxbuf
.ut_pid
);
698 * "pututline" is a wrapper that calls pututxline after converting
699 * the utmp record to a utmpx record.
702 pututline(const struct utmp
*entry
)
705 struct utmpx
*utmpx2
;
708 return (_compat_pututline(entry
));
710 getutmpx(entry
, &utmpx
);
711 if ((utmpx2
= pututxline(&utmpx
)) == NULL
)
713 getutmp(utmpx2
, &utmpcompat
);
714 return (&utmpcompat
);
718 * "setutxent" just resets the utmpx file back to the beginning.
724 (void) lseek(fd
, 0L, SEEK_SET
);
727 (void) fseek(fp
, 0L, SEEK_SET
);
730 * Zero the stored copy of the last entry read, since we are
731 * resetting to the beginning of the file.
733 bzero(&ubuf
, sizeof (ubuf
));
734 bzero(&fubuf
, sizeof (fubuf
));
738 * "setutent" is a wrapper that calls setutxent
743 if (compat_utmpflag
) {
752 * "endutxent" closes the utmpx file.
765 bzero(&ubuf
, sizeof (ubuf
));
766 bzero(&fubuf
, sizeof (fubuf
));
770 * "endutent" is a wrapper that calls endutxent
771 * and clears the utmp compatibility buffer.
776 if (compat_utmpflag
) {
782 bzero(&utmpcompat
, sizeof (utmpcompat
));
786 * "utmpxname" allows the user to read a file other than the
787 * normal "utmpx" file.
790 utmpxname(const char *newfile
)
795 * Determine if the new filename will fit. If not, return 0.
797 if ((len
= strlen(newfile
)) > MAXFILE
-1)
801 * The name of the utmpx file has to end with 'x'
803 if (newfile
[len
-1] != 'x')
807 * Otherwise copy in the new file name.
810 (void) strcpy(&utmpxfile
[0], newfile
);
812 * Make sure everything is reset to the beginning state.
817 * If the file is being changed to /etc/utmpx or /var/adm/utmpx then
818 * we clear the flag so pututxline invokes utmp_update. Otherwise
819 * we set the flag indicating that they changed to another name.
821 if (strcmp(utmpxfile
, UTMPX_FILE
) == 0 ||
822 strcmp(utmpxfile
, VAR_UTMPX_FILE
) == 0)
831 * "utmpname" allows the user to read a file other than the
832 * normal "utmp" file. If the file specified is "/var/adm/utmp"
833 * or "/var/adm/wtmp", it is translated to the corresponding "utmpx"
834 * format name, and all "utmp" operations become wrapped calls
835 * to the equivalent "utmpx" routines, with data conversions
836 * as appropriate. In the event the application wishes to read
837 * an actual "old" utmp file (named something other than /var/adm/utmp),
838 * calling this function with that name enables backward compatibility
839 * mode, where we actually call the old utmp routines to operate on
843 utmpname(const char *newfile
)
845 char name
[MAXFILE
+1];
847 if (strlen(newfile
) > MAXFILE
)
850 if (strcmp(newfile
, "/var/adm/utmp") == 0 ||
851 strcmp(newfile
, "/var/adm/wtmp") == 0) {
852 (void) strcpy(name
, newfile
);
853 (void) strcat(name
, "x");
854 compat_utmpflag
= 0; /* turn off old compat mode */
855 return (utmpxname(name
));
857 (void) strcpy(_compat_utmpfile
, newfile
);
864 * Add the record to wtmpx.
867 updwtmpx(const char *filex
, struct utmpx
*utx
)
872 if ((wfdx
= open(filex
, O_WRONLY
| O_APPEND
)) < 0)
875 (void) lseek(wfdx
, 0, SEEK_END
);
877 utmpx_api2frec(utx
, &futx
);
878 (void) write(wfdx
, &futx
, sizeof (futx
));
885 * Add record to wtmp (actually wtmpx). If not updating /var/adm/wtmp,
886 * use the old utmp compatibility routine to write a utmp-format
887 * record to the file specified.
890 updwtmp(const char *file
, struct utmp
*ut
)
893 char xfile
[MAXFILE
+ 1];
895 if (strcmp(file
, "/var/adm/wtmp") == 0) {
896 (void) strlcpy(xfile
, file
, sizeof (xfile
) - 1);
897 (void) strcat(xfile
, "x");
898 getutmpx(ut
, &utmpx
);
899 updwtmpx((const char *)&xfile
, &utmpx
);
901 _compat_updwtmp(file
, ut
);
905 * modutx - modify a utmpx entry. Also notify init about new pids or
906 * old pids that it no longer needs to care about
908 * args: utp- point to utmpx structure to be created
911 modutx(const struct utmpx
*utp
)
914 struct utmpx utmp
; /* holding area */
915 struct utmpx
*ucp
= &utmp
; /* and a pointer to it */
916 struct utmpx
*up
; /* "current" utmpx entry */
917 struct futmpx
*fup
; /* being examined */
919 for (i
= 0; i
< IDLEN
; ++i
) {
920 if ((unsigned char)utp
->ut_id
[i
] == SC_WILDC
)
925 * copy the supplied utmpx structure someplace safe
927 (void) memcpy(&utmp
, utp
, sizeof (utmp
));
929 while (fup
= getutxent_frec()) {
930 if (idcmp(ucp
->ut_id
, fup
->ut_id
))
934 * only get here if ids are the same, i.e. found right entry
936 if (ucp
->ut_pid
!= fup
->ut_pid
) {
937 sendpid(REMPID
, (pid_t
)fup
->ut_pid
);
938 sendpid(ADDPID
, (pid_t
)ucp
->ut_pid
);
942 up
= pututxline(ucp
);
943 if (ucp
->ut_type
== DEAD_PROCESS
)
944 sendpid(REMPID
, (pid_t
)ucp
->ut_pid
);
946 updwtmpx(WTMPX_FILE
, up
);
952 * modut - modify a utmp entry. Also notify init about new pids or
953 * old pids that it no longer needs to care about
955 * args: utmp - point to utmp structure to be created
958 modut(struct utmp
*utp
)
961 struct utmpx
*utmpx2
;
963 getutmpx(utp
, &utmpx
);
964 if ((utmpx2
= modutx(&utmpx
)) == NULL
)
967 getutmp(utmpx2
, utp
);
972 * idcmp - compare two id strings, return 0 if same, non-zero if not *
973 * args: s1 - first id string
974 * s2 - second id string
977 idcmp(const char *s1
, const char *s2
)
981 for (i
= 0; i
< IDLEN
; ++i
)
982 if ((unsigned char) *s1
!= SC_WILDC
&& (*s1
++ != *s2
++))
989 * allocid - allocate an unused id for utmp, either by recycling a
990 * DEAD_PROCESS entry or creating a new one. This routine only
991 * gets called if a wild card character was specified.
993 * args: srcid - pattern for new id
994 * saveid - last id matching pattern for a non-dead process
997 allocid(char *srcid
, unsigned char *saveid
)
999 int i
; /* scratch variable */
1000 int changed
; /* flag to indicate that a new id has */
1001 /* been generated */
1002 char copyid
[IDLEN
]; /* work area */
1004 (void) memcpy(copyid
, srcid
, IDLEN
);
1006 for (i
= 0; i
< IDLEN
; ++i
) {
1009 * if this character isn't wild, it'll be part of the
1012 if ((unsigned char) copyid
[i
] != SC_WILDC
)
1016 * it's a wild character, retrieve the character from the
1019 copyid
[i
] = saveid
[i
];
1022 * if we haven't changed anything yet, try to find a new char
1025 if (!changed
&& (saveid
[i
] < MAXVAL
)) {
1028 * Note: this algorithm is taking the "last matched" id
1029 * and trying to make a 1 character change to it to create
1030 * a new one. Rather than special-case the first time
1031 * (when no perturbation is really necessary), just don't
1032 * allocate the first valid id.
1035 while (++saveid
[i
] < MAXVAL
) {
1037 * make sure new char is alphanumeric
1039 if (isalnum(saveid
[i
])) {
1040 copyid
[i
] = saveid
[i
];
1048 * Then 'reset' the current count at
1049 * this position to it's lowest valid
1050 * value, and propagate the carry to
1051 * the next wild-card slot
1056 while (!isalnum(saveid
[i
]))
1058 copyid
[i
] = ++saveid
[i
];
1063 * changed is true if we were successful in allocating an id
1066 (void) memcpy(srcid
, copyid
, IDLEN
);
1075 * lockutx - lock utmpx file
1082 if ((lockfd
= open(UTMPX_FILE
, O_RDWR
|O_CREAT
, 0644)) < 0)
1085 if (lockf(lockfd
, F_LOCK
, 0) < 0) {
1086 (void) close(lockfd
);
1100 * unlockutx - unlock utmpx file
1105 (void) lockf(fd
, F_ULOCK
, 0);
1112 * sendpid - send message to init to add or remove a pid from the
1115 * args: cmd - ADDPID or REMPID
1116 * pid - pid of "godchild"
1119 sendpid(int cmd
, pid_t pid
)
1121 int pfd
; /* file desc. for init pipe */
1122 pidrec_t prec
; /* place for message to be built */
1125 * if for some reason init didn't open initpipe, open it read/write
1126 * here to avoid sending SIGPIPE to the calling process
1128 pfd
= open(IPIPE
, O_RDWR
);
1133 (void) write(pfd
, &prec
, sizeof (pidrec_t
));
1138 * makeutx - create a utmpx entry, recycling an id if a wild card is
1139 * specified. Also notify init about the new pid
1141 * args: utmpx - point to utmpx structure to be created
1145 makeutx(const struct utmpx
*utmp
)
1148 struct futmpx
*ut
; /* "current" utmpx being examined */
1149 unsigned char saveid
[IDLEN
]; /* the last id we matched that was */
1150 /* NOT a dead proc */
1151 int falphanum
= 0x30; /* first alpha num char */
1155 * Are any wild card char's present in the idlen string?
1157 if (memchr(utmp
->ut_id
, SC_WILDC
, IDLEN
) != NULL
) {
1159 * try to lock the utmpx file, only needed if
1160 * we're doing wildcard matching
1168 (void) memset(saveid
, falphanum
, IDLEN
);
1170 while (ut
= getoneutx(&offset
))
1171 if (idcmp(utmp
->ut_id
, ut
->ut_id
)) {
1175 * Found a match. We are done if this is
1176 * a free slot. Else record this id. We
1177 * will need it to generate the next new id.
1179 if (ut
->ut_type
== DEAD_PROCESS
)
1182 (void) memcpy(saveid
, ut
->ut_id
,
1189 * Unused entry, reuse it. We know the offset. So
1190 * just go to that offset utmpx and write it out.
1192 (void) memcpy((caddr_t
)utmp
->ut_id
, ut
->ut_id
, IDLEN
);
1194 putoneutx(utmp
, offset
);
1195 updwtmpx(WTMPX_FILE
, (struct utmpx
*)utmp
);
1197 sendpid(ADDPID
, (pid_t
)utmp
->ut_pid
);
1198 return ((struct utmpx
*)utmp
);
1201 * nothing available, allocate an id and
1202 * write it out at the end.
1205 if (allocid((char *)utmp
->ut_id
, saveid
)) {
1210 * Seek to end and write out the entry
1211 * and also update the utmpx file.
1213 (void) lseek(fd
, 0L, SEEK_END
);
1214 offset
= lseek(fd
, 0L, SEEK_CUR
);
1216 putoneutx(utmp
, offset
);
1217 updwtmpx(WTMPX_FILE
, (struct utmpx
*)utmp
);
1219 sendpid(ADDPID
, (pid_t
)utmp
->ut_pid
);
1220 return ((struct utmpx
*)utmp
);
1224 utp
= pututxline(utmp
);
1226 updwtmpx(WTMPX_FILE
, utp
);
1228 sendpid(ADDPID
, (pid_t
)utmp
->ut_pid
);
1234 * makeut - create a utmp entry, recycling an id if a wild card is
1235 * specified. Also notify init about the new pid
1237 * args: utmp - point to utmp structure to be created
1240 makeut(struct utmp
*utmp
)
1243 struct utmpx
*utmpx2
;
1245 if (compat_utmpflag
)
1246 return (_compat_makeut(utmp
));
1248 getutmpx(utmp
, &utmpx
);
1249 if ((utmpx2
= makeutx(&utmpx
)) == NULL
)
1252 getutmp(utmpx2
, utmp
);
1257 #define UTMPNBUF 200 /* Approx 8k (FS Block) size */
1258 static struct futmpx
*utmpbuf
= NULL
;
1261 * Buffered read routine to get one entry from utmpx file
1263 static struct futmpx
*
1264 getoneutx(off_t
*off
)
1266 static size_t idx
= 0; /* Current index in the utmpbuf */
1267 static size_t nidx
= 0; /* Max entries in this utmpbuf */
1268 static int nbuf
= 0; /* number of utmpbufs read from disk */
1269 ssize_t nbytes
, bufsz
= sizeof (struct futmpx
) * UTMPNBUF
;
1271 if (utmpbuf
== NULL
)
1272 if ((utmpbuf
= malloc(bufsz
)) == NULL
) {
1279 * We have read all entries in the utmpbuf. Read
1280 * the buffer from the disk.
1282 if ((nbytes
= read(fd
, utmpbuf
, bufsz
)) < bufsz
) {
1284 * Partial read only. keep count of the
1285 * number of valid entries in the buffer
1287 nidx
= nbytes
/ sizeof (struct futmpx
);
1290 * We read in the full UTMPNBUF entries
1295 nbuf
++; /* Number of buf we have read in. */
1296 idx
= 0; /* reset index within utmpbuf */
1300 * Current offset of this buffer in the file
1302 *off
= (((nbuf
- 1) * UTMPNBUF
) + idx
) * sizeof (struct futmpx
);
1306 * We still have at least one valid buffer in
1307 * utmpbuf to be passed to the caller.
1309 return (&utmpbuf
[idx
++]);
1313 * Reached EOF. Return NULL. Offset is set correctly
1314 * to append at the end of the file
1321 putoneutx(const struct utmpx
*utpx
, off_t off
)
1325 utmpx_api2frec(utpx
, &futx
);
1326 (void) lseek(fd
, off
, SEEK_SET
); /* seek in the utmpx file */
1327 (void) write(fd
, &futx
, sizeof (futx
));
1331 * sendupid - send message to utmpd to add or remove a pid from the
1332 * list of procs to watch
1334 * args: cmd - ADDPID or REMPID
1335 * pid - process ID of process to watch
1338 sendupid(int cmd
, pid_t pid
)
1340 int pfd
; /* file desc. for utmp pipe */
1341 pidrec_t prec
; /* place for message to be built */
1344 * if for some reason utmp didn't open utmppipe, open it read/write
1345 * here to avoid sending SIGPIPE to the calling process
1348 pfd
= open(UPIPE
, O_RDWR
| O_NONBLOCK
| O_NDELAY
);
1353 (void) write(pfd
, &prec
, sizeof (pidrec_t
));
1358 * getutmpx - convert a utmp record into a utmpx record
1361 getutmpx(const struct utmp
*ut
, struct utmpx
*utx
)
1363 (void) memcpy(utx
->ut_user
, ut
->ut_user
, sizeof (ut
->ut_user
));
1364 (void) bzero(&utx
->ut_user
[sizeof (ut
->ut_user
)],
1365 sizeof (utx
->ut_user
) - sizeof (ut
->ut_user
));
1366 (void) memcpy(utx
->ut_line
, ut
->ut_line
, sizeof (ut
->ut_line
));
1367 (void) bzero(&utx
->ut_line
[sizeof (ut
->ut_line
)],
1368 sizeof (utx
->ut_line
) - sizeof (ut
->ut_line
));
1369 (void) memcpy(utx
->ut_id
, ut
->ut_id
, sizeof (ut
->ut_id
));
1370 utx
->ut_pid
= ut
->ut_pid
;
1371 utx
->ut_type
= ut
->ut_type
;
1372 utx
->ut_exit
= ut
->ut_exit
;
1373 utx
->ut_tv
.tv_sec
= ut
->ut_time
;
1374 utx
->ut_tv
.tv_usec
= 0;
1375 utx
->ut_session
= 0;
1376 bzero(utx
->pad
, sizeof (utx
->pad
));
1377 bzero(utx
->ut_host
, sizeof (utx
->ut_host
));
1382 * getutmp - convert a utmpx record into a utmp record
1385 getutmp(const struct utmpx
*utx
, struct utmp
*ut
)
1387 (void) memcpy(ut
->ut_user
, utx
->ut_user
, sizeof (ut
->ut_user
));
1388 (void) memcpy(ut
->ut_line
, utx
->ut_line
, sizeof (ut
->ut_line
));
1389 (void) memcpy(ut
->ut_id
, utx
->ut_id
, sizeof (utx
->ut_id
));
1390 ut
->ut_pid
= utx
->ut_pid
;
1391 ut
->ut_type
= utx
->ut_type
;
1392 ut
->ut_exit
= utx
->ut_exit
;
1393 ut
->ut_time
= utx
->ut_tv
.tv_sec
;