--- utmpx.c.orig 2009-04-01 04:01:12.000000000 -0700 +++ utmpx.c 2009-04-01 04:09:50.000000000 -0700 @@ -49,48 +49,57 @@ __RCSID("$NetBSD: utmpx.c,v 1.21 2003/09 #include <sys/time.h> #include <sys/wait.h> -#include <assert.h> -#include <db.h> -#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#ifdef LEGACY_UTMP_APIS #include <utmp.h> -/* don't define earlier, has side effects in fcntl.h */ -#define __LIBC12_SOURCE__ +#endif /* LEGACY_UTMP_APIS */ #include <utmpx.h> +#include <utmpx-darwin.h> +#include <errno.h> #include <vis.h> - -__warn_references(getlastlogx, - "warning: reference to compatibility getlastlogx(); include <utmpx.h> for correct reference") -__warn_references(lastlogxname, - "warning: reference to deprecated lastlogxname()") +#include <notify.h> static FILE *fp; static int readonly = 0; static struct utmpx ut; static char utfile[MAXPATHLEN] = _PATH_UTMPX; -static char llfile[MAXPATHLEN] = _PATH_LASTLOGX; +__private_extern__ int utfile_system = 1; /* are we using _PATH_UTMPX? */ +__private_extern__ pthread_mutex_t utmpx_mutex = PTHREAD_MUTEX_INITIALIZER; -static struct utmpx *utmp_update(const struct utmpx *); +static struct utmpx *_getutxid(const struct utmpx *); -static const char vers[] = "utmpx-1.00"; +__private_extern__ const char _utmpx_vers[] = "utmpx-1.00"; -void -setutxent() +__private_extern__ void +_setutxent() { (void)memset(&ut, 0, sizeof(ut)); if (fp == NULL) return; +#ifdef __LP64__ + (void)fseeko(fp, (off_t)sizeof(struct utmpx32), SEEK_SET); +#else /* __LP64__ */ (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET); +#endif /* __LP64__ */ } void -endutxent() +setutxent() +{ + UTMPX_LOCK; + _setutxent(); + UTMPX_UNLOCK; +} + + +__private_extern__ void +_endutxent() { (void)memset(&ut, 0, sizeof(ut)); @@ -102,9 +111,21 @@ endutxent() } -struct utmpx * -getutxent() +void +endutxent() { + UTMPX_LOCK; + _endutxent(); + UTMPX_UNLOCK; +} + + +static struct utmpx * +_getutxent() +{ +#ifdef __LP64__ + struct utmpx32 ut32; +#endif /* __LP64__ */ if (fp == NULL) { struct stat st; @@ -116,7 +137,8 @@ getutxent() else readonly = 1; } - + + fcntl(fileno(fp), F_SETFD, 1); /* set close-on-exec flag */ /* get file size in order to check if new file */ if (fstat(fileno(fp), &st) == -1) @@ -124,27 +146,51 @@ getutxent() if (st.st_size == 0) { /* new file, add signature record */ +#ifdef __LP64__ + (void)memset(&ut32, 0, sizeof(ut32)); + ut32.ut_type = SIGNATURE; + (void)memcpy(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers)); + if (fwrite(&ut32, sizeof(ut32), 1, fp) != 1) +#else /* __LP64__ */ (void)memset(&ut, 0, sizeof(ut)); ut.ut_type = SIGNATURE; - (void)memcpy(ut.ut_user, vers, sizeof(vers)); + (void)memcpy(ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers)); if (fwrite(&ut, sizeof(ut), 1, fp) != 1) +#endif /* __LP64__ */ goto failclose; } else { /* old file, read signature record */ +#ifdef __LP64__ + if (fread(&ut32, sizeof(ut32), 1, fp) != 1) +#else /* __LP64__ */ if (fread(&ut, sizeof(ut), 1, fp) != 1) +#endif /* __LP64__ */ goto failclose; - if (memcmp(ut.ut_user, vers, sizeof(vers)) != 0 || +#ifdef __LP64__ + if (memcmp(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 || + ut32.ut_type != SIGNATURE) +#else /* __LP64__ */ + if (memcmp(ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 || ut.ut_type != SIGNATURE) +#endif /* __LP64__ */ goto failclose; } } +#ifdef __LP64__ + if (fread(&ut32, sizeof(ut32), 1, fp) != 1) +#else /* __LP64__ */ if (fread(&ut, sizeof(ut), 1, fp) != 1) +#endif /* __LP64__ */ goto fail; +#ifdef __LP64__ + _utmpx32_64(&ut32, &ut); +#endif /* __LP64__ */ return &ut; failclose: (void)fclose(fp); + fp = NULL; fail: (void)memset(&ut, 0, sizeof(ut)); return NULL; @@ -152,14 +198,45 @@ fail: struct utmpx * +getutxent() +{ + struct utmpx *ret; + UTMPX_LOCK; + ret = _getutxent(); + UTMPX_UNLOCK; + return ret; +} + +struct utmpx * getutxid(const struct utmpx *utx) { + struct utmpx temp; + const struct utmpx *ux; + struct utmpx *ret; _DIAGASSERT(utx != NULL); if (utx->ut_type == EMPTY) return NULL; + UTMPX_LOCK; + /* make a copy as needed, and auto-fill if requested */ + ux = _utmpx_working_copy(utx, &temp, 1); + if (!ux) { + UTMPX_UNLOCK; + return NULL; + } + + ret = _getutxid(ux); + UTMPX_UNLOCK; + return ret; +} + + +static struct utmpx * +_getutxid(const struct utmpx *utx) +{ + do { if (ut.ut_type == EMPTY) continue; @@ -193,7 +270,7 @@ getutxid(const struct utmpx *utx) default: return NULL; } - } while (getutxent() != NULL); + } while (_getutxent() != NULL); return NULL; } @@ -204,6 +281,7 @@ getutxline(const struct utmpx *utx) _DIAGASSERT(utx != NULL); + UTMPX_LOCK; do { switch (ut.ut_type) { case EMPTY: @@ -211,13 +289,16 @@ getutxline(const struct utmpx *utx) case LOGIN_PROCESS: case USER_PROCESS: if (strncmp(ut.ut_line, utx->ut_line, - sizeof(ut.ut_line)) == 0) + sizeof(ut.ut_line)) == 0) { + UTMPX_UNLOCK; return &ut; + } break; default: break; } - } while (getutxent() != NULL); + } while (_getutxent() != NULL); + UTMPX_UNLOCK; return NULL; } @@ -225,156 +306,180 @@ getutxline(const struct utmpx *utx) struct utmpx * pututxline(const struct utmpx *utx) { - struct utmpx temp, *u = NULL; - int gotlock = 0; + struct utmpx *ux; _DIAGASSERT(utx != NULL); - if (utx == NULL) + if (utx == NULL) { + errno = EINVAL; return NULL; + } - if (strcmp(_PATH_UTMPX, utfile) == 0) - if ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0)) - return utmp_update(utx); - + UTMPX_LOCK; + if ((ux = _pututxline(utx)) != NULL && utfile_system) { + _utmpx_asl(ux); /* the equivalent of wtmpx and lastlogx */ +#ifdef UTMP_COMPAT + _write_utmp_compat(ux); +#endif /* UTMP_COMPAT */ + } + UTMPX_UNLOCK; + return ux; +} - (void)memcpy(&temp, utx, sizeof(temp)); +__private_extern__ struct utmpx * +_pututxline(const struct utmpx *utx) +{ + struct utmpx temp, *u = NULL, *x; + const struct utmpx *ux; +#ifdef __LP64__ + struct utmpx32 ut32; +#endif /* __LP64__ */ + struct flock fl; +#define gotlock (fl.l_start >= 0) + + fl.l_start = -1; /* also means we haven't locked */ + if (utfile_system) + if ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0)) { + errno = EPERM; + return NULL; + } if (fp == NULL) { - (void)getutxent(); - if (fp == NULL || readonly) + (void)_getutxent(); + if (fp == NULL || readonly) { + errno = EPERM; return NULL; + } } - if (getutxid(&temp) == NULL) { - setutxent(); - if (getutxid(&temp) == NULL) { - if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1) + /* make a copy as needed, and auto-fill if requested */ + ux = _utmpx_working_copy(utx, &temp, 0); + if (!ux) + return NULL; + + if ((x = _getutxid(ux)) == NULL) { + _setutxent(); + if ((x = _getutxid(ux)) == NULL) { + /* + * utx->ut_type has any original mask bits, while + * ux->ut_type has those mask bits removed. If we + * are trying to record a dead process, and + * UTMPX_DEAD_IF_CORRESPONDING_MASK is set, then since + * there is no matching entry, we return NULL. + */ + if (ux->ut_type == DEAD_PROCESS && + (utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK)) { + errno = EINVAL; + return NULL; + } + /* + * Replace lockf() with fcntl() and a fixed start + * value. We should already be at EOF. + */ + if ((fl.l_start = lseek(fileno(fp), 0, SEEK_CUR)) < 0) + return NULL; + fl.l_len = 0; + fl.l_whence = SEEK_SET; + fl.l_type = F_WRLCK; + if (fcntl(fileno(fp), F_SETLKW, &fl) == -1) return NULL; - gotlock++; if (fseeko(fp, (off_t)0, SEEK_END) == -1) goto fail; } } if (!gotlock) { + /* + * utx->ut_type has any original mask bits, while + * ux->ut_type has those mask bits removed. If we + * are trying to record a dead process, if + * UTMPX_DEAD_IF_CORRESPONDING_MASK is set, but the found + * entry is not a (matching) USER_PROCESS, then return NULL. + */ + if (ux->ut_type == DEAD_PROCESS && + (utx->ut_type & UTMPX_DEAD_IF_CORRESPONDING_MASK) && + x->ut_type != USER_PROCESS) { + errno = EINVAL; + return NULL; + } /* we are not appending */ +#ifdef __LP64__ + if (fseeko(fp, -(off_t)sizeof(ut32), SEEK_CUR) == -1) +#else /* __LP64__ */ if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) +#endif /* __LP64__ */ return NULL; } - if (fwrite(&temp, sizeof (temp), 1, fp) != 1) +#ifdef __LP64__ + _utmpx64_32(ux, &ut32); + if (fwrite(&ut32, sizeof (ut32), 1, fp) != 1) +#else /* __LP64__ */ + if (fwrite(ux, sizeof (*ux), 1, fp) != 1) +#endif /* __LP64__ */ goto fail; if (fflush(fp) == -1) goto fail; - u = memcpy(&ut, &temp, sizeof(ut)); + u = memcpy(&ut, ux, sizeof(ut)); + notify_post(UTMPX_CHANGE_NOTIFICATION); fail: if (gotlock) { - if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1) + int save = errno; + fl.l_type = F_UNLCK; + if (fcntl(fileno(fp), F_SETLK, &fl) == -1) return NULL; + errno = save; } return u; } -static struct utmpx * -utmp_update(const struct utmpx *utx) -{ - char buf[sizeof(*utx) * 4 + 1]; - pid_t pid; - int status; - - _DIAGASSERT(utx != NULL); - - (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx), - VIS_WHITE); - switch (pid = fork()) { - case 0: - (void)execl(_PATH_UTMP_UPDATE, - strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL); - exit(1); - /*NOTREACHED*/ - case -1: - return NULL; - default: - if (waitpid(pid, &status, 0) == -1) - return NULL; - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - return memcpy(&ut, utx, sizeof(ut)); - return NULL; - } - -} - /* * The following are extensions and not part of the X/Open spec. */ int -updwtmpx(const char *file, const struct utmpx *utx) -{ - int fd; - int saved_errno; - - _DIAGASSERT(file != NULL); - _DIAGASSERT(utx != NULL); - - fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK); - - if (fd == -1) { - if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1) - return -1; - (void)memset(&ut, 0, sizeof(ut)); - ut.ut_type = SIGNATURE; - (void)memcpy(ut.ut_user, vers, sizeof(vers)); - if (write(fd, &ut, sizeof(ut)) == -1) - goto failed; - } - if (write(fd, utx, sizeof(*utx)) == -1) - goto failed; - if (close(fd) == -1) - return -1; - return 0; - - failed: - saved_errno = errno; - (void) close(fd); - errno = saved_errno; - return -1; -} - - -int utmpxname(const char *fname) { size_t len; - _DIAGASSERT(fname != NULL); + UTMPX_LOCK; + if (fname == NULL) { + strcpy(utfile, _PATH_UTMPX); + utfile_system = 1; + _endutxent(); + UTMPX_UNLOCK; + return 1; + } len = strlen(fname); - if (len >= sizeof(utfile)) + if (len >= sizeof(utfile)) { + UTMPX_UNLOCK; return 0; + } /* must end in x! */ - if (fname[len - 1] != 'x') + if (fname[len - 1] != 'x') { + UTMPX_UNLOCK; return 0; + } (void)strlcpy(utfile, fname, sizeof(utfile)); - endutxent(); + _endutxent(); + utfile_system = 0; + UTMPX_UNLOCK; return 1; } - +#ifdef LEGACY_UTMP_APIS void getutmp(const struct utmpx *ux, struct utmp *u) { - _DIAGASSERT(ux != NULL); - _DIAGASSERT(u != NULL); - - (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); + bzero(u, sizeof(*u)); + (void)memcpy(u->ut_name, ux->ut_user, sizeof(u->ut_name)); (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); u->ut_time = ux->ut_tv.tv_sec; @@ -384,109 +489,16 @@ void getutmpx(const struct utmp *u, struct utmpx *ux) { - _DIAGASSERT(ux != NULL); - _DIAGASSERT(u != NULL); - - (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); + bzero(ux, sizeof(*ux)); + (void)memcpy(ux->ut_user, u->ut_name, sizeof(u->ut_name)); + ux->ut_user[sizeof(u->ut_name)] = 0; (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); + ux->ut_line[sizeof(u->ut_line)] = 0; (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); + ux->ut_host[sizeof(u->ut_host)] = 0; ux->ut_tv.tv_sec = u->ut_time; ux->ut_tv.tv_usec = 0; - (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); - ux->ut_pid = 0; + ux->ut_pid = getpid(); ux->ut_type = USER_PROCESS; - ux->ut_session = 0; - ux->ut_exit.e_termination = 0; - ux->ut_exit.e_exit = 0; -} - -int -lastlogxname(const char *fname) -{ - size_t len; - - _DIAGASSERT(fname != NULL); - - len = strlen(fname); - - if (len >= sizeof(llfile)) - return 0; - - /* must end in x! */ - if (fname[len - 1] != 'x') - return 0; - - (void)strlcpy(llfile, fname, sizeof(llfile)); - return 1; -} - -struct lastlogx * -getlastlogx(uid_t uid, struct lastlogx *ll) -{ - - return __getlastlogx13(_PATH_LASTLOGX, uid, ll); -} - -struct lastlogx * -__getlastlogx13(const char *fname, uid_t uid, struct lastlogx *ll) -{ - DBT key, data; - DB *db; - - _DIAGASSERT(fname != NULL); - _DIAGASSERT(ll != NULL); - - db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL); - - if (db == NULL) - return NULL; - - key.data = &uid; - key.size = sizeof(uid); - - if ((db->get)(db, &key, &data, 0) != 0) - goto error; - - if (data.size != sizeof(*ll)) { - errno = EFTYPE; - goto error; - } - - if (ll == NULL) - if ((ll = malloc(sizeof(*ll))) == NULL) - goto done; - - (void)memcpy(ll, data.data, sizeof(*ll)); - goto done; -error: - ll = NULL; -done: - (db->close)(db); - return ll; -} - -int -updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) -{ - DBT key, data; - int error = 0; - DB *db; - - _DIAGASSERT(fname != NULL); - _DIAGASSERT(ll != NULL); - - db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0, DB_HASH, NULL); - - if (db == NULL) - return -1; - - key.data = &uid; - key.size = sizeof(uid); - data.data = ll; - data.size = sizeof(*ll); - if ((db->put)(db, &key, &data, 0) != 0) - error = -1; - - (db->close)(db); - return error; } +#endif /* LEGACY_UTMP_APIS */