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 * Compatibility routines to read and write alternate
32 * utmp-like files. These routines are only used in
33 * the case where utmpname() is used to change to a file
34 * other than /var/adm/utmp or /var/adm/wtmp. In this case,
35 * we assume that someone really wants to read old utmp-format
36 * files. Otherwise, the getutent, setutent, getutid, setutline,
37 * and pututline functions are actually wrappers around the
38 * equivalent function operating on utmpx-like files.
43 #include <sys/param.h>
44 #include <sys/types.h>
57 #define IDLEN 4 /* length of id field in utmp */
58 #define SC_WILDC 0xff /* wild char for utmp ids */
59 #define MAXVAL 255 /* max value for an id 'character' */
65 static void utmp_frec2api(const struct futmp
*, struct utmp
*);
66 static void utmp_api2frec(const struct utmp
*, struct futmp
*);
67 struct utmp
*_compat_getutent(void);
68 struct utmp
*_compat_getutid(const struct utmp
*);
69 struct utmp
*_compat_getutline(const struct utmp
*);
70 struct utmp
*_compat_pututline(const struct utmp
*);
71 void _compat_setutent(void);
72 void _compat_endutent(void);
73 void _compat_updwtmp(const char *, struct utmp
*);
74 struct utmp
*_compat_makeut(struct utmp
*);
75 struct utmp
*_compat_modut(struct utmp
*);
77 static void unlockut(void);
78 static int idcmp(const char *, const char *);
79 static int allocid(char *, unsigned char *);
80 static int lockut(void);
83 static int fd
= -1; /* File descriptor for the utmp file. */
85 * name of the current utmp-like file - set by utmpname (getutx.c)
86 * only if running in backward compatibility mode
87 * We don't modify this, but we can't declare it const or lint will freak.
89 extern char _compat_utmpfile
[];
92 static long loc_utmp
; /* Where in "utmp" the current "ubuf" was found. */
95 static struct futmp fubuf
; /* Copy of last entry read in. */
96 static struct utmp ubuf
; /* Last entry returned to client */
99 * In the 64-bit world, the utmp data structure grows because of
100 * the ut_time field (a time_t) at the end of it.
103 utmp_frec2api(const struct futmp
*src
, struct utmp
*dst
)
108 (void) strncpy(dst
->ut_user
, src
->ut_user
, sizeof (dst
->ut_user
));
109 (void) strncpy(dst
->ut_line
, src
->ut_line
, sizeof (dst
->ut_line
));
110 (void) memcpy(dst
->ut_id
, src
->ut_id
, sizeof (dst
->ut_id
));
111 dst
->ut_pid
= src
->ut_pid
;
112 dst
->ut_type
= src
->ut_type
;
113 dst
->ut_exit
.e_termination
= src
->ut_exit
.e_termination
;
114 dst
->ut_exit
.e_exit
= src
->ut_exit
.e_exit
;
115 dst
->ut_time
= (time_t)src
->ut_time
;
119 utmp_api2frec(const struct utmp
*src
, struct futmp
*dst
)
124 (void) strncpy(dst
->ut_user
, src
->ut_user
, sizeof (dst
->ut_user
));
125 (void) strncpy(dst
->ut_line
, src
->ut_line
, sizeof (dst
->ut_line
));
126 (void) memcpy(dst
->ut_id
, src
->ut_id
, sizeof (dst
->ut_id
));
127 dst
->ut_pid
= src
->ut_pid
;
128 dst
->ut_type
= src
->ut_type
;
129 dst
->ut_exit
.e_termination
= src
->ut_exit
.e_termination
;
130 dst
->ut_exit
.e_exit
= src
->ut_exit
.e_exit
;
131 dst
->ut_time
= (time32_t
)src
->ut_time
;
135 * "getutent_frec" gets the raw version of the next entry in the utmp file.
137 static struct futmp
*
141 * If the "utmp" file is not open, attempt to open it for
142 * reading. If there is no file, attempt to create one. If
143 * both attempts fail, return NULL. If the file exists, but
144 * isn't readable and writeable, do not attempt to create.
147 if ((fd
= open(_compat_utmpfile
, O_RDWR
|O_CREAT
, 0644)) < 0) {
150 * If the open failed for permissions, try opening
151 * it only for reading. All "pututline()" later
152 * will fail the writes.
154 if ((fd
= open(_compat_utmpfile
, O_RDONLY
)) < 0)
159 /* Try to read in the next entry from the utmp file. */
161 if (read(fd
, &fubuf
, sizeof (fubuf
)) != sizeof (fubuf
)) {
162 bzero(&fubuf
, sizeof (fubuf
));
166 /* Save the location in the file where this entry was found. */
168 (void) lseek(fd
, 0L, 1);
173 * "_compat_getutent" gets the next entry in the utmp file.
176 _compat_getutent(void)
180 futp
= getutent_frec();
181 utmp_frec2api(&fubuf
, &ubuf
);
188 * "_compat_getutid" finds the specified entry in the utmp file. If
189 * it can't find it, it returns NULL.
192 _compat_getutid(const struct utmp
*entry
)
196 utmp_api2frec(&ubuf
, &fubuf
);
199 * Start looking for entry. Look in our current buffer before
200 * reading in new entries.
204 * If there is no entry in "ubuf", skip to the read.
206 if (fubuf
.ut_type
!= EMPTY
) {
207 switch (entry
->ut_type
) {
210 * Do not look for an entry if the user sent
217 * For RUN_LVL, BOOT_TIME, DOWN_TIME,
218 * OLD_TIME, and NEW_TIME entries, only the
219 * types have to match. If they do, return
220 * the address of internal buffer.
227 if (entry
->ut_type
== fubuf
.ut_type
) {
228 utmp_frec2api(&fubuf
, &ubuf
);
234 * For INIT_PROCESS, LOGIN_PROCESS, USER_PROCESS,
235 * and DEAD_PROCESS the type of the entry in "fubuf",
236 * must be one of the above and id's must match.
242 if (((type
= fubuf
.ut_type
) == INIT_PROCESS
||
243 type
== LOGIN_PROCESS
||
244 type
== USER_PROCESS
||
245 type
== DEAD_PROCESS
) &&
246 fubuf
.ut_id
[0] == entry
->ut_id
[0] &&
247 fubuf
.ut_id
[1] == entry
->ut_id
[1] &&
248 fubuf
.ut_id
[2] == entry
->ut_id
[2] &&
249 fubuf
.ut_id
[3] == entry
->ut_id
[3]) {
250 utmp_frec2api(&fubuf
, &ubuf
);
255 /* Do not search for illegal types of entry. */
260 } while (getutent_frec() != NULL
);
262 /* the proper entry wasn't found. */
264 utmp_frec2api(&fubuf
, &ubuf
);
269 * "_compat_getutline" searches the "utmp" file for a LOGIN_PROCESS or
270 * USER_PROCESS with the same "line" as the specified "entry".
273 _compat_getutline(const struct utmp
*entry
)
275 utmp_api2frec(&ubuf
, &fubuf
);
279 * If the current entry is the one we are interested in,
280 * return a pointer to it.
282 if (fubuf
.ut_type
!= EMPTY
&&
283 (fubuf
.ut_type
== LOGIN_PROCESS
||
284 fubuf
.ut_type
== USER_PROCESS
) &&
285 strncmp(&entry
->ut_line
[0], &fubuf
.ut_line
[0],
286 sizeof (fubuf
.ut_line
)) == 0) {
287 utmp_frec2api(&fubuf
, &ubuf
);
290 } while (getutent_frec() != NULL
);
292 utmp_frec2api(&fubuf
, &ubuf
);
297 * "_compat_pututline" writes the structure sent into the utmp file
298 * If there is already an entry with the same id, then it is
299 * overwritten, otherwise a new entry is made at the end of the
303 _compat_pututline(const struct utmp
*entry
)
308 struct futmp ftmpbuf
;
311 * Copy the user supplied entry into our temporary buffer to
312 * avoid the possibility that the user is actually passing us
313 * the address of "ubuf".
316 utmp_api2frec(entry
, &ftmpbuf
);
318 (void) getutent_frec();
321 gdebug("pututline: Unable to create utmp file.\n");
326 /* Make sure file is writable */
328 if ((fc
= fcntl(fd
, F_GETFL
, NULL
)) == -1 || (fc
& O_RDWR
) != O_RDWR
)
332 * Find the proper entry in the utmp file. Start at the current
333 * location. If it isn't found from here to the end of the
334 * file, then reset to the beginning of the file and try again.
335 * If it still isn't found, then write a new entry at the end of
336 * the file. (Making sure the location is an integral number of
337 * utmp structures into the file incase the file is scribbled.)
340 if (_compat_getutid(&tmpbuf
) == NULL
) {
342 gdebug("1st getutid() failed. fd: %d", fd
);
345 if (_compat_getutid(&tmpbuf
) == NULL
) {
347 loc_utmp
= lseek(fd
, 0L, 1);
348 gdebug("2nd getutid() failed. fd: %d loc_utmp: %ld\n",
351 (void) fcntl(fd
, F_SETFL
, fc
| O_APPEND
);
353 (void) lseek(fd
, -(long)sizeof (struct futmp
), 1);
355 (void) lseek(fd
, -(long)sizeof (struct futmp
), 1);
358 * Write out the user supplied structure. If the write fails,
359 * then the user probably doesn't have permission to write the
362 if (write(fd
, &ftmpbuf
, sizeof (ftmpbuf
)) != sizeof (ftmpbuf
)) {
364 gdebug("pututline failed: write-%d\n", errno
);
369 * Copy the new user structure into ubuf so that it will
370 * be up to date in the future.
373 utmp_frec2api(&fubuf
, &ubuf
);
377 gdebug("id: %c%c loc: %ld\n", fubuf
.ut_id
[0],
378 fubuf
.ut_id
[1], fubuf
.ut_id
[2], fubuf
.ut_id
[3],
383 (void) fcntl(fd
, F_SETFL
, fc
);
389 * "_compat_setutent" just resets the utmp file back to the beginning.
392 _compat_setutent(void)
395 (void) lseek(fd
, 0L, 0);
398 * Zero the stored copy of the last entry read, since we are
399 * resetting to the beginning of the file.
401 bzero(&ubuf
, sizeof (ubuf
));
402 bzero(&fubuf
, sizeof (fubuf
));
406 * "_compat_endutent" closes the utmp file.
409 _compat_endutent(void)
414 bzero(&ubuf
, sizeof (ubuf
));
415 bzero(&fubuf
, sizeof (fubuf
));
420 * If one of wtmp and wtmpx files exist, create the other, and the record.
421 * If they both exist add the record.
424 _compat_updwtmp(const char *file
, struct utmp
*ut
)
430 fd
= open(file
, O_WRONLY
| O_APPEND
);
433 if ((fd
= open(file
, O_WRONLY
|O_CREAT
, 0644)) < 0)
437 (void) lseek(fd
, 0, 2);
439 utmp_api2frec(ut
, &fut
);
440 (void) write(fd
, &fut
, sizeof (fut
));
448 * makeut - create a utmp entry, recycling an id if a wild card is
451 * args: utmp - point to utmp structure to be created
454 _compat_makeut(struct utmp
*utmp
)
457 struct utmp
*utp
; /* "current" utmp entry being examined */
458 int wild
; /* flag, true iff wild card char seen */
460 /* the last id we matched that was NOT a dead proc */
461 unsigned char saveid
[IDLEN
];
464 for (i
= 0; i
< IDLEN
; i
++)
465 if ((unsigned char)utmp
->ut_id
[i
] == SC_WILDC
) {
473 * try to lock the utmp file, only needed if we're
474 * doing wildcard matching
481 /* find the first alphanumeric character */
482 for (i
= 0; i
< MAXVAL
; ++i
)
486 (void) memset(saveid
, i
, IDLEN
);
488 while ((utp
= _compat_getutent()) != 0) {
489 if (idcmp(utmp
->ut_id
, utp
->ut_id
))
491 if (utp
->ut_type
== DEAD_PROCESS
)
493 (void) memcpy(saveid
, utp
->ut_id
, IDLEN
);
498 * found an unused entry, reuse it
500 (void) memcpy(utmp
->ut_id
, utp
->ut_id
, IDLEN
);
501 utp
= _compat_pututline(utmp
);
503 _compat_updwtmp(WTMP_FILE
, utp
);
510 * nothing available, try to allocate an id
512 if (allocid(utmp
->ut_id
, saveid
)) {
517 utp
= _compat_pututline(utmp
);
519 _compat_updwtmp(WTMP_FILE
, utp
);
526 utp
= _compat_pututline(utmp
);
528 _compat_updwtmp(WTMP_FILE
, utp
);
536 * _compat_modut - modify a utmp entry.
538 * args: utmp - point to utmp structure to be created
541 _compat_modut(struct utmp
*utp
)
543 int i
; /* scratch variable */
544 struct utmp utmp
; /* holding area */
545 struct utmp
*ucp
= &utmp
; /* and a pointer to it */
546 struct utmp
*up
; /* "current" utmp entry being examined */
549 for (i
= 0; i
< IDLEN
; ++i
)
550 if ((unsigned char)utp
->ut_id
[i
] == SC_WILDC
)
553 /* copy the supplied utmp structure someplace safe */
556 while (fup
= getutent_frec()) {
557 if (idcmp(ucp
->ut_id
, fup
->ut_id
))
561 up
= _compat_pututline(ucp
);
563 _compat_updwtmp(WTMP_FILE
, up
);
571 * idcmp - compare two id strings, return 0 if same, non-zero if not *
572 * args: s1 - first id string
573 * s2 - second id string
576 idcmp(const char *s1
, const char *s2
)
580 for (i
= 0; i
< IDLEN
; ++i
)
581 if ((unsigned char)*s1
!= SC_WILDC
&& (*s1
++ != *s2
++))
588 * allocid - allocate an unused id for utmp, either by recycling a
589 * DEAD_PROCESS entry or creating a new one. This routine only
590 * gets called if a wild card character was specified.
592 * args: srcid - pattern for new id
593 * saveid - last id matching pattern for a non-dead process
596 allocid(char *srcid
, unsigned char *saveid
)
598 int i
; /* scratch variable */
599 int changed
; /* flag to indicate that a new id has been generated */
600 char copyid
[IDLEN
]; /* work area */
602 (void) memcpy(copyid
, srcid
, IDLEN
);
604 for (i
= 0; i
< IDLEN
; ++i
) {
606 * if this character isn't wild, it'll
607 * be part of the generated id
609 if ((unsigned char) copyid
[i
] != SC_WILDC
)
612 * it's a wild character, retrieve the
613 * character from the saved id
615 copyid
[i
] = saveid
[i
];
617 * if we haven't changed anything yet,
618 * try to find a new char to use
620 if (!changed
&& (saveid
[i
] < MAXVAL
)) {
623 * Note: this algorithm is taking the "last matched" id and trying to make
624 * a 1 character change to it to create a new one. Rather than special-case
625 * the first time (when no perturbation is really necessary), just don't
626 * allocate the first valid id.
629 while (++saveid
[i
] < MAXVAL
) {
630 /* make sure new char is alphanumeric */
631 if (isalnum(saveid
[i
])) {
632 copyid
[i
] = saveid
[i
];
640 * Then 'reset' the current count at
641 * this position to it's lowest valid
642 * value, and propagate the carry to
643 * the next wild-card slot
648 while (!isalnum(saveid
[i
]))
650 copyid
[i
] = ++saveid
[i
];
654 /* changed is true if we were successful in allocating an id */
656 (void) memcpy(srcid
, copyid
, IDLEN
);
664 * lockut - lock utmp file
669 if ((fd
= open(_compat_utmpfile
, O_RDWR
|O_CREAT
, 0644)) < 0)
672 if (lockf(fd
, F_LOCK
, 0) < 0) {
682 * unlockut - unlock utmp file
687 (void) lockf(fd
, F_ULOCK
, 0);
700 gdebug(const char *fmt
, ...)
706 if ((fp
= fopen("/etc/dbg.getut", "a+F")) == NULL
)
709 (void) vfprintf(fp
, fmt
, ap
);