--- utmpx.c.orig 2010-06-24 19:43:32.000000000 -0700 +++ utmpx.c 2010-06-29 11:25:57.000000000 -0700 @@ -42,111 +42,240 @@ __RCSID("$NetBSD: utmpx.c,v 1.25 2008/04 #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 UNIFDEF_LEGACY_UTMP_APIS #include <utmp.h> +#endif /* UNIFDEF_LEGACY_UTMP_APIS */ #include <utmpx.h> +#include <utmpx-darwin.h> +#include <errno.h> #include <vis.h> +#include <notify.h> -static FILE *fp; -static int readonly = 0; -static struct utmpx ut; -static char utfile[MAXPATHLEN] = _PATH_UTMPX; +/* This is the default struct _utmpx shared by the POSIX APIs */ +__private_extern__ +struct _utmpx __utx__ = { + __UTX_MAGIC__, /* magic */ + {}, /* ut */ + PTHREAD_MUTEX_INITIALIZER, /* utmpx_mutex */ + _PATH_UTMPX, /* utfile */ + NULL, /* fp */ + 1, /* utfile_system */ + 0, /* readonly */ +}; + +static struct utmpx *__getutxid(struct _utmpx *, const struct utmpx *); -static struct utmpx *utmp_update(const struct utmpx *); +__private_extern__ const char _utmpx_vers[] = "utmpx-1.00"; -static const char vers[] = "utmpx-1.00"; +__private_extern__ void +__setutxent(struct _utmpx *U) +{ + + (void)memset(&U->ut, 0, sizeof(U->ut)); + if (U->fp == NULL) + return; +#ifdef __LP64__ + (void)fseeko(U->fp, (off_t)sizeof(struct utmpx32), SEEK_SET); +#else /* __LP64__ */ + (void)fseeko(U->fp, (off_t)sizeof(U->ut), SEEK_SET); +#endif /* __LP64__ */ +} void -setutxent() +_setutxent(struct _utmpx *U) { - (void)memset(&ut, 0, sizeof(ut)); - if (fp == NULL) - return; - (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET); + TEST_UTMPX_T("_setutxent", U); + UTMPX_LOCK(U); + __setutxent(U); + UTMPX_UNLOCK(U); } void -endutxent() +setutxent() { + _setutxent(&__utx__); +} + - (void)memset(&ut, 0, sizeof(ut)); - if (fp != NULL) { - (void)fclose(fp); - fp = NULL; - readonly = 0; +__private_extern__ void +__endutxent(struct _utmpx *U) +{ + (void)memset(&U->ut, 0, sizeof(U->ut)); + if (U->fp != NULL) { + int saveerrno = errno; + (void)fclose(U->fp); + errno = saveerrno; + U->fp = NULL; + U->readonly = 0; } } -struct utmpx * -getutxent() +void +_endutxent(struct _utmpx *U) { + TEST_UTMPX_T("_endutxent", U); + UTMPX_LOCK(U); + __endutxent(U); + UTMPX_UNLOCK(U); +} - if (fp == NULL) { + +void +endutxent() +{ + _endutxent(&__utx__); +} + + +__private_extern__ struct utmpx * +__getutxent(struct _utmpx *U) +{ + int saveerrno; +#ifdef __LP64__ + struct utmpx32 ut32; +#endif /* __LP64__ */ + + if (U->fp == NULL) { struct stat st; - if ((fp = fopen(utfile, "r+")) == NULL) - if ((fp = fopen(utfile, "w+")) == NULL) { - if ((fp = fopen(utfile, "r")) == NULL) + if ((U->fp = fopen(U->utfile, "r+")) == NULL) + if ((U->fp = fopen(U->utfile, "w+")) == NULL) { + if ((U->fp = fopen(U->utfile, "r")) == NULL) goto fail; else - readonly = 1; + U->readonly = 1; } - + + fcntl(fileno(U->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) + if (fstat(fileno(U->fp), &st) == -1) goto failclose; if (st.st_size == 0) { /* new file, add signature record */ - (void)memset(&ut, 0, sizeof(ut)); - ut.ut_type = SIGNATURE; - (void)memcpy(ut.ut_user, vers, sizeof(vers)); - if (fwrite(&ut, sizeof(ut), 1, fp) != 1) +#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, U->fp) != 1) +#else /* __LP64__ */ + (void)memset(&U->ut, 0, sizeof(U->ut)); + U->ut.ut_type = SIGNATURE; + (void)memcpy(U->ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers)); + if (fwrite(&U->ut, sizeof(U->ut), 1, U->fp) != 1) +#endif /* __LP64__ */ goto failclose; } else { /* old file, read signature record */ - if (fread(&ut, sizeof(ut), 1, fp) != 1) +#ifdef __LP64__ + if (fread(&ut32, sizeof(ut32), 1, U->fp) != 1) +#else /* __LP64__ */ + if (fread(&U->ut, sizeof(U->ut), 1, U->fp) != 1) +#endif /* __LP64__ */ goto failclose; - if (memcmp(ut.ut_user, vers, sizeof(vers)) != 0 || - ut.ut_type != SIGNATURE) +#ifdef __LP64__ + if (memcmp(ut32.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 || + ut32.ut_type != SIGNATURE) +#else /* __LP64__ */ + if (memcmp(U->ut.ut_user, _utmpx_vers, sizeof(_utmpx_vers)) != 0 || + U->ut.ut_type != SIGNATURE) +#endif /* __LP64__ */ + { + errno = EINVAL; goto failclose; + } } } - if (fread(&ut, sizeof(ut), 1, fp) != 1) +#ifdef __LP64__ + if (fread(&ut32, sizeof(ut32), 1, U->fp) != 1) +#else /* __LP64__ */ + if (fread(&U->ut, sizeof(U->ut), 1, U->fp) != 1) +#endif /* __LP64__ */ goto fail; - return &ut; +#ifdef __LP64__ + _utmpx32_64(&ut32, &U->ut); +#endif /* __LP64__ */ + return &U->ut; failclose: - (void)fclose(fp); + saveerrno = errno; + (void)fclose(U->fp); + errno = saveerrno; + U->fp = NULL; fail: - (void)memset(&ut, 0, sizeof(ut)); + (void)memset(&U->ut, 0, sizeof(U->ut)); return NULL; } struct utmpx * -getutxid(const struct utmpx *utx) +_getutxent(struct _utmpx *U) { + struct utmpx *ret; + + TEST_UTMPX_T("_getutxent", U); + UTMPX_LOCK(U); + ret = __getutxent(U); + UTMPX_UNLOCK(U); + return ret; +} + + +struct utmpx * +getutxent() +{ + return _getutxent(&__utx__); +} - _DIAGASSERT(utx != NULL); + +struct utmpx * +_getutxid(struct _utmpx *U, const struct utmpx *utx) +{ + struct utmpx temp; + const struct utmpx *ux; + struct utmpx *ret; if (utx->ut_type == EMPTY) return NULL; + TEST_UTMPX_T("_getutxid", U); + UTMPX_LOCK(U); + /* make a copy as needed, and auto-fill if requested */ + ux = _utmpx_working_copy(utx, &temp, 1); + if (!ux) { + UTMPX_UNLOCK(U); + return NULL; + } + + ret = __getutxid(U, ux); + UTMPX_UNLOCK(U); + return ret; +} + + +struct utmpx * +getutxid(const struct utmpx *utx) +{ + return _getutxid(&__utx__, utx); +} + + +static struct utmpx * +__getutxid(struct _utmpx *U, const struct utmpx *utx) +{ + do { - if (ut.ut_type == EMPTY) + if (U->ut.ut_type == EMPTY) continue; switch (utx->ut_type) { case EMPTY: @@ -155,21 +284,21 @@ getutxid(const struct utmpx *utx) case BOOT_TIME: case OLD_TIME: case NEW_TIME: - if (ut.ut_type == utx->ut_type) - return &ut; + if (U->ut.ut_type == utx->ut_type) + return &U->ut; break; case INIT_PROCESS: case LOGIN_PROCESS: case USER_PROCESS: case DEAD_PROCESS: - switch (ut.ut_type) { + switch (U->ut.ut_type) { case INIT_PROCESS: case LOGIN_PROCESS: case USER_PROCESS: case DEAD_PROCESS: - if (memcmp(ut.ut_id, utx->ut_id, - sizeof(ut.ut_id)) == 0) - return &ut; + if (memcmp(U->ut.ut_id, utx->ut_id, + sizeof(U->ut.ut_id)) == 0) + return &U->ut; break; default: break; @@ -178,188 +307,253 @@ getutxid(const struct utmpx *utx) default: return NULL; } - } while (getutxent() != NULL); + } while (__getutxent(U) != NULL); return NULL; } -struct utmpx * -getutxline(const struct utmpx *utx) +static struct utmpx * +__getutxline(struct _utmpx *U, const struct utmpx *utx) { - - _DIAGASSERT(utx != NULL); - do { - switch (ut.ut_type) { + switch (U->ut.ut_type) { case EMPTY: break; case LOGIN_PROCESS: case USER_PROCESS: - if (strncmp(ut.ut_line, utx->ut_line, - sizeof(ut.ut_line)) == 0) - return &ut; + if (strncmp(U->ut.ut_line, utx->ut_line, + sizeof(U->ut.ut_line)) == 0) + return &U->ut; break; default: break; } - } while (getutxent() != NULL); + } while (__getutxent(U) != NULL); return NULL; } struct utmpx * -pututxline(const struct utmpx *utx) +_getutxline(struct _utmpx *U, const struct utmpx *utx) +{ + struct utmpx *ret; + + TEST_UTMPX_T("_getutxline", U); + UTMPX_LOCK(U); + ret = __getutxline(U, utx); + UTMPX_UNLOCK(U); + return ret; +} + + +struct utmpx * +getutxline(const struct utmpx *utx) { - struct utmpx temp, *u = NULL; - int gotlock = 0; + return _getutxline(&__utx__, utx); +} - _DIAGASSERT(utx != NULL); - if (utx == NULL) +struct utmpx * +_pututxline(struct _utmpx *U, const struct utmpx *utx) +{ + struct utmpx *ux; + + 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); + TEST_UTMPX_T("_pututxline", U); + UTMPX_LOCK(U); + if ((ux = __pututxline(&__utx__, utx)) != NULL && __utx__.utfile_system) { + _utmpx_asl(ux); /* the equivalent of wtmpx and lastlogx */ +#ifdef UTMP_COMPAT + _write_utmp_compat(ux); +#endif /* UTMP_COMPAT */ + } + UTMPX_UNLOCK(U); + return ux; +} - (void)memcpy(&temp, utx, sizeof(temp)); +struct utmpx * +pututxline(const struct utmpx *utx) +{ + return _pututxline(&__utx__, utx); +} + +__private_extern__ struct utmpx * +__pututxline(struct _utmpx *U, 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 (U->utfile_system) + if ((U->fp != NULL && U->readonly) || (U->fp == NULL && geteuid() != 0)) { + errno = EPERM; + return NULL; + } - if (fp == NULL) { - (void)getutxent(); - if (fp == NULL || readonly) + if (U->fp == NULL) { + (void)__getutxent(U); + if (U->fp == NULL || U->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(U, ux)) == NULL) { + __setutxent(U); + if ((x = __getutxid(U, 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; - gotlock++; - if (fseeko(fp, (off_t)0, SEEK_END) == -1) + } + /* + * Replace lockf() with fcntl() and a fixed start + * value. We should already be at EOF. + */ + if ((fl.l_start = lseek(fileno(U->fp), 0, SEEK_CUR)) < 0) + return NULL; + fl.l_len = 0; + fl.l_whence = SEEK_SET; + fl.l_type = F_WRLCK; + if (fcntl(fileno(U->fp), F_SETLKW, &fl) == -1) + return NULL; + if (fseeko(U->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 */ - if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) +#ifdef __LP64__ + if (fseeko(U->fp, -(off_t)sizeof(ut32), SEEK_CUR) == -1) +#else /* __LP64__ */ + if (fseeko(U->fp, -(off_t)sizeof(U->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, U->fp) != 1) +#else /* __LP64__ */ + if (fwrite(ux, sizeof (*ux), 1, U->fp) != 1) +#endif /* __LP64__ */ goto fail; - if (fflush(fp) == -1) + if (fflush(U->fp) == -1) goto fail; - u = memcpy(&ut, &temp, sizeof(ut)); + u = memcpy(&U->ut, ux, sizeof(U->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(U->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) +__private_extern__ int +__utmpxname(struct _utmpx *U, const char *fname) { size_t len; - _DIAGASSERT(fname != NULL); + if (fname == NULL) { + if(!U->utfile_system) + free(U->utfile); + U->utfile = _PATH_UTMPX; + U->utfile_system = 1; + __endutxent(U); + return 1; + } len = strlen(fname); - if (len >= sizeof(utfile)) + if (len >= MAXPATHLEN) return 0; /* must end in x! */ if (fname[len - 1] != 'x') return 0; - (void)strlcpy(utfile, fname, sizeof(utfile)); - endutxent(); + if (U->utfile_system) + U->utfile = NULL; + U->utfile_system = 0; + if ((U->utfile = reallocf(U->utfile, len + 1)) == NULL) + return 0; + + (void)strcpy(U->utfile, fname); + __endutxent(U); return 1; } +int +_utmpxname(struct _utmpx *U, const char *fname) +{ + int ret; + + TEST_UTMPX_T("_utmpxname", U); + UTMPX_LOCK(U); + ret = __utmpxname(U, fname); + UTMPX_UNLOCK(U); + return ret; +} + +int +utmpxname(const char *fname) +{ + return _utmpxname(&__utx__, fname); +} +#ifdef UNIFDEF_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; @@ -369,82 +563,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; -} - -struct lastlogx * -getlastlogx(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, 0644, 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 /* UNIFDEF_LEGACY_UTMP_APIS */