From: Alex Hornung Date: Sun, 4 Oct 2009 17:44:56 +0000 (+0100) Subject: utmpx - Bring in utmpx,wtmpx and lastlogx support X-Git-Tag: v2.11.0~459 X-Git-Url: https://gitweb.dragonflybsd.org/~nant/dragonfly.git/commitdiff_plain/59a92d1877d6bb6c4fc9d7be5e51943661e50c2f utmpx - Bring in utmpx,wtmpx and lastlogx support * This commit introduces the necessary support for utmpx, wtmpx and lastlogx, as well as updating many base utils to work with these while mostly maintaining compatibility with the old utmp, wtmp and lastlog. * The new last(1) supports wtmpx but defaults to wtmp as not all wtmp writers have been updated for wtmpx. * All utmp readers support both utmp and utmpx now. * lastlogin (the only lastlog reader) supports both lastlog and lastlogx. * The utils who(1) and finger have been almost directly replaced by their NetBSD equivalent. In case of who(1) the only custom modification is the behaviour of '-b' to be as it has always been. * Partially-Obtained-from: NetBSD --- diff --git a/etc/Makefile b/etc/Makefile index a282667117..a1846a5722 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -323,6 +323,8 @@ distribution: ${DESTDIR}/var/log/maillog ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ ${DESTDIR}/var/log/lastlog + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ + ${DESTDIR}/var/log/lastlogx ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ ${DESTDIR}/var/log/messages ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 600 /dev/null \ @@ -333,8 +335,12 @@ distribution: ${DESTDIR}/var/log/ppp.log ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ ${DESTDIR}/var/log/wtmp + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ + ${DESTDIR}/var/log/wtmpx ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ ${DESTDIR}/var/run/utmp + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ + ${DESTDIR}/var/run/utmpx ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 ${.CURDIR}/minfree \ ${DESTDIR}/var/crash cd ${.CURDIR}/..; ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 444 \ diff --git a/etc/newsyslog.conf b/etc/newsyslog.conf index 790f4cb063..af62974252 100644 --- a/etc/newsyslog.conf +++ b/etc/newsyslog.conf @@ -32,6 +32,7 @@ /var/log/ppp.log root:network 640 3 100 * Z /var/log/security 600 10 100 * Z /var/log/wtmp 644 3 * @01T05 B +/var/log/wtmpx 644 3 * @01T05 B /var/log/daily.log 640 7 * @T00 ZN /var/log/weekly.log 640 5 1 $W6D0 ZN /var/log/monthly.log 640 12 * $M1D0 ZN diff --git a/games/dm/Makefile b/games/dm/Makefile index 3d153f6fa8..f32d29f714 100644 --- a/games/dm/Makefile +++ b/games/dm/Makefile @@ -3,9 +3,17 @@ # $DragonFly: src/games/dm/Makefile,v 1.4 2006/10/08 16:22:35 pavalos Exp $ # -DLOG log games +.PATH: ${.CURDIR}/../../usr.bin/who + PROG= dm +SRCS= dm.c utmpentry.c MAN= dm.8 dm.conf.5 BINGRP= games BINMODE=2555 +DPADD= ${LIBUTIL} +LDADD= -lutil + +CFLAGS+= -I${.CURDIR}/../../usr.bin/who -DSUPPORT_UTMP -DSUPPORT_UTMPX + .include diff --git a/games/dm/dm.c b/games/dm/dm.c index 83d86d6838..9be520a983 100644 --- a/games/dm/dm.c +++ b/games/dm/dm.c @@ -50,7 +50,7 @@ #include #include #include -#include +#include "utmpentry.h" #include "pathnames.h" @@ -257,18 +257,13 @@ load(void) static int users(void) { + struct utmpentry *ep; + int nusers = 0; - int nusers, utmp; - struct utmp buf; + getutentries(NULL, &ep); + for (; ep; ep = ep->next) + ++nusers; - if ((utmp = open(_PATH_UTMP, O_RDONLY, 0)) < 0) { - fprintf(stderr, "dm: %s: %s\n", - _PATH_UTMP, strerror(errno)); - exit(1); - } - for (nusers = 0; read(utmp, (char *)&buf, sizeof(struct utmp)) > 0;) - if (buf.ut_name[0] != '\0') - ++nusers; return(nusers); } diff --git a/include/Makefile b/include/Makefile index d7fcf49aef..f0043092dc 100644 --- a/include/Makefile +++ b/include/Makefile @@ -24,8 +24,8 @@ INCS= a.out.h ar.h assert.h bitstring.h complex.h cpio.h ctype.h db.h \ stddef.h stdint.h stdio.h stdlib.h \ string.h stringlist.h strings.h struct.h sysexits.h tar.h time.h \ timeconv.h \ - timers.h ttyent.h tzfile.h unistd.h ulimit.h utime.h utmp.h uuid.h \ - vis.h \ + timers.h ttyent.h tzfile.h unistd.h ulimit.h utime.h utmp.h utmpx.h \ + uuid.h vis.h \ wchar.h wctype.h wordexp.h .if defined(WANT_HESIOD) diff --git a/include/utmp.h b/include/utmp.h index 6431f8cc49..7316087dc4 100644 --- a/include/utmp.h +++ b/include/utmp.h @@ -64,4 +64,9 @@ struct utmp { time_t ut_time; }; +int utmpname(const char *); +void setutent(void); +struct utmp *getutent(void); +void endutent(void); + #endif /* !_UTMP_H_ */ diff --git a/include/utmpx.h b/include/utmpx.h new file mode 100644 index 0000000000..7e6aadab17 --- /dev/null +++ b/include/utmpx.h @@ -0,0 +1,132 @@ +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _UTMPX_H_ +#define _UTMPX_H_ + +#include +#include +#include + +#define _PATH_UTMPX "/var/run/utmpx" +#define _PATH_WTMPX "/var/log/wtmpx" +#define _PATH_LASTLOGX "/var/log/lastlogx" +#define _PATH_UTMP_UPDATE "/usr/libexec/utmp_update" + +#define _UTX_USERSIZE 32 +#define _UTX_LINESIZE 32 +#define _UTX_IDSIZE 4 +#define _UTX_HOSTSIZE 256 + +#define UTX_USERSIZE _UTX_USERSIZE +#define UTX_LINESIZE _UTX_LINESIZE +#define UTX_IDSIZE _UTX_IDSIZE +#define UTX_HOSTSIZE _UTX_HOSTSIZE + + +#define EMPTY 0 +#define RUN_LVL 1 +#define BOOT_TIME 2 +#define OLD_TIME 3 +#define NEW_TIME 4 +#define INIT_PROCESS 5 +#define LOGIN_PROCESS 6 +#define USER_PROCESS 7 +#define DEAD_PROCESS 8 + +#define ACCOUNTING 9 +#define SIGNATURE 10 +#define DOWN_TIME 11 + +/* + * Strings placed in the ut_line field to indicate special type entries + */ +#define RUNLVL_MSG "run-level %c" +#define BOOT_MSG "system boot" +#define OTIME_MSG "old time" +#define NTIME_MSG "new time" +#define DOWN_MSG "system down" + +#define ut_user ut_name +#define ut_xtime ut_tv.tv_sec + +typedef enum { + UTX_DB_UTMPX, + UTX_DB_WTMPX, + UTX_DB_LASTLOGX +} utx_db_t; + +struct exit_status +{ + uint16_t e_termination; /* termination status */ + uint16_t e_exit; /* exit status */ +}; + +struct utmpx { + char ut_name[_UTX_USERSIZE]; /* login name */ + char ut_id[_UTX_IDSIZE]; /* inittab id */ + char ut_line[_UTX_LINESIZE]; /* tty name */ + char ut_host[_UTX_HOSTSIZE]; /* host name */ + uint8_t ut_unused[16]; /* reserved for future use */ + uint16_t ut_session; /* session id used for windowing */ + uint16_t ut_type; /* type of this entry */ + pid_t ut_pid; /* process id creating the entry */ + struct exit_status ut_exit; /* process termination/exit status */ + struct sockaddr_storage ut_ss; /* address where entry was made from */ + struct timeval ut_tv; /* time entry was created */ + uint8_t ut_unused2[16]; /* reserved for future use */ +}; + + +void endutxent(void); +struct utmpx *getutxent(void); +struct utmpx *getutxid(const struct utmpx *); +struct utmpx *getutxline(const struct utmpx *); +struct utmpx *pututxline(const struct utmpx *); +void setutxent(void); + +/* NON-STANDARD functions, NetBSD specific stuff */ +struct lastlogx { + struct timeval ll_tv; /* time entry was created */ + char ll_line[_UTX_LINESIZE]; /* tty name */ + char ll_host[_UTX_HOSTSIZE]; /* host name */ + struct sockaddr_storage ll_ss; /* address where entry was made from */ +}; + +int updwtmpx(const char *, const struct utmpx *); +struct lastlogx *getlastlogx(const char *, uid_t, struct lastlogx *); +int updlastlogx(const char *, uid_t, struct lastlogx *); +struct utmp; +void getutmp(const struct utmpx *, struct utmp *); +void getutmpx(const struct utmp *, struct utmpx *); +int utmpxname(const char *); +int setutxdb(utx_db_t, char *); + +#endif /* _UTMPX_H_ */ + diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 4a7c246347..8756c5abbf 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -36,6 +36,7 @@ SRCS+= _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \ sysctlnametomib.c syslog.c telldir.c termios.c time.c times.c \ toascii.c tolower.c toupper.c ttyname.c ttyslot.c \ ualarm.c ucontext.c ulimit.c uname.c unvis.c usleep.c utime.c \ + utmp.c utmpx.c \ valloc.c vis.c wait.c wait3.c waitpid.c wordexp.c .if ${LIB} != {c_rtld} diff --git a/lib/libc/gen/utmp.c b/lib/libc/gen/utmp.c new file mode 100644 index 0000000000..6446d736dc --- /dev/null +++ b/lib/libc/gen/utmp.c @@ -0,0 +1,102 @@ +/* $NetBSD: utmp.c,v 1.8 2009/01/11 02:46:27 christos Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include "namespace.h" +#include +#include +#include +#include +#include +#include +#include + +static struct utmp utmp; +static FILE *ut; +static char utfile[MAXPATHLEN] = _PATH_UTMP; + +void +setutent(void) +{ + if (ut == NULL) + return; + (void)fseeko(ut, (off_t)0, SEEK_SET); +} + +struct utmp * +getutent(void) +{ + if (ut == NULL) { + struct stat st; + off_t numentries; + if ((ut = fopen(utfile, "r")) == NULL) + return NULL; + if (fstat(fileno(ut), &st) == -1) + goto out; + /* + * If we have a an old version utmp file bail. + */ + numentries = st.st_size / sizeof(utmp); + if ((off_t)(numentries * sizeof(utmp)) != st.st_size) + goto out; + } + if (fread(&utmp, sizeof(utmp), 1, ut) == 1) + return &utmp; +out: + (void)fclose(ut); + return NULL; +} + +void +endutent(void) +{ + if (ut != NULL) { + (void)fclose(ut); + ut = NULL; + } +} + +int +utmpname(const char *fname) +{ + size_t len = strlen(fname); + + if (len >= sizeof(utfile)) + return 0; + + /* must not end in x! */ + if (fname[len - 1] == 'x') + return 0; + + (void)strlcpy(utfile, fname, sizeof(utfile)); + endutent(); + return 1; +} diff --git a/lib/libc/gen/utmpx.c b/lib/libc/gen/utmpx.c new file mode 100644 index 0000000000..3b41791fef --- /dev/null +++ b/lib/libc/gen/utmpx.c @@ -0,0 +1,498 @@ +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include "namespace.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static FILE *fp = NULL; +static int readonly = 0; +static struct utmpx ut; +static char utfile[MAXPATHLEN] = _PATH_UTMPX; + +static struct utmpx *utmp_update(const struct utmpx *); + +static const char vers[] = "utmpx-2.00"; +static utx_db_t dbtype = UTX_DB_UTMPX; +DB *lastlogx_db = NULL; + +static int +_open_db(char *fname) +{ + struct stat st; + + if ((fp = fopen(fname, "r+")) == NULL) + if ((fp = fopen(fname, "w+")) == NULL) { + if ((fp = fopen(fname, "r")) == NULL) + goto fail; + else + readonly = 1; + } + + /* get file size in order to check if new file */ + if (fstat(fileno(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) + goto failclose; + } else { + /* old file, read signature record */ + if (fread(&ut, sizeof(ut), 1, fp) != 1) + goto failclose; + if (memcmp(ut.ut_user, vers, 5) != 0 || + ut.ut_type != SIGNATURE) { + errno = EINVAL; + goto failclose; + } + } + + return 0; +failclose: + (void)fclose(fp); +fail: + (void)memset(&ut, 0, sizeof(ut)); + return -1; +} + +int +setutxdb(utx_db_t db_type, char *fname) +{ + switch (db_type) { + case UTX_DB_UTMPX: + if (fname == NULL) + fname = _PATH_UTMPX; + break; + + case UTX_DB_WTMPX: + if (fname == NULL) + fname = _PATH_WTMPX; + break; + + default: + errno = EINVAL; + return -1; + } + + /* A previous db file is still open */ + if (fp != NULL) { + fclose(fp); + fp = NULL; + } + if ((_open_db(fname)) == -1) + return -1; + +done: + dbtype = db_type; + return 0; +} + +void +setutxent() +{ + (void)memset(&ut, 0, sizeof(ut)); + if (fp == NULL) + return; + +#if 0 + if (dbtype != UTX_DB_UTMPX) + setutxdb(UTX_DB_UTMPX, utfile); +#endif + (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET); +} + +void +endutxent() +{ + (void)memset(&ut, 0, sizeof(ut)); + + if (fp != NULL) { + (void)fclose(fp); + fp = NULL; + readonly = 0; + } +} + +struct utmpx * +getutxent() +{ + if (fp == NULL) { + if ((_open_db(utfile)) == -1) + goto fail; + } + + if (fread(&ut, sizeof(ut), 1, fp) != 1) + goto fail; + + return &ut; +fail: + (void)memset(&ut, 0, sizeof(ut)); + return NULL; +} + +struct utmpx * +getutxid(const struct utmpx *utx) +{ + + _DIAGASSERT(utx != NULL); + + if (utx->ut_type == EMPTY) + return NULL; + + do { + if (ut.ut_type == EMPTY) + continue; + switch (utx->ut_type) { + case EMPTY: + return NULL; + case RUN_LVL: + case BOOT_TIME: + case OLD_TIME: + case NEW_TIME: + if (ut.ut_type == utx->ut_type) + return &ut; + break; + case INIT_PROCESS: + case LOGIN_PROCESS: + case USER_PROCESS: + case DEAD_PROCESS: + switch (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; + break; + default: + break; + } + break; + default: + return NULL; + } + } while (getutxent() != NULL); + return NULL; +} + +struct utmpx * +getutxline(const struct utmpx *utx) +{ + + _DIAGASSERT(utx != NULL); + + do { + switch (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; + break; + default: + break; + } + } while (getutxent() != NULL); + return NULL; +} + +struct utmpx * +pututxline(const struct utmpx *utx) +{ + struct passwd *pw; + struct lastlogx ll; + struct utmpx temp, *u = NULL; + int gotlock = 0; + + _DIAGASSERT(utx != NULL); + + if (utx == NULL) + return NULL; + + if (utx->ut_type == USER_PROCESS) { + ll.ll_tv = utx->ut_tv; + strcpy(ll.ll_host, utx->ut_host); + strcpy(ll.ll_line, utx->ut_line); + pw = getpwnam(utx->ut_name); + if (pw != NULL) + updlastlogx(_PATH_LASTLOGX, pw->pw_uid, &ll); + } + + if (strcmp(_PATH_UTMPX, utfile) == 0) + if ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0)) + return utmp_update(utx); + + + (void)memcpy(&temp, utx, sizeof(temp)); + + if (fp == NULL) { + (void)getutxent(); + if (fp == NULL || readonly) + return NULL; + } + + if (getutxid(&temp) == NULL) { + setutxent(); + if (getutxid(&temp) == NULL) { + if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1) + return NULL; + gotlock++; + if (fseeko(fp, (off_t)0, SEEK_END) == -1) + goto fail; + } + } + + if (!gotlock) { + /* we are not appending */ + if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) + return NULL; + } + + if (fwrite(&temp, sizeof (temp), 1, fp) != 1) + goto fail; + + if (fflush(fp) == -1) + goto fail; + + u = memcpy(&ut, &temp, sizeof(ut)); +fail: + if (gotlock) { + if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1) + return NULL; + } + 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); + + len = strlen(fname); + + if (len >= sizeof(utfile)) + return 0; + + /* must end in x! */ + if (fname[len - 1] != 'x') + return 0; + + (void)strlcpy(utfile, fname, sizeof(utfile)); + endutxent(); + return 1; +} + +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)); + (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; +} + +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)); + (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); + (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); + 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_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; +} diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index 3a81293461..a5fdef6d21 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -6,9 +6,10 @@ LIB= util SHLIB_MAJOR= 4 CFLAGS+=-DINET6 CFLAGS+=-D_CTYPE_H_DISABLE_MACROS_ -SRCS= flopen.c login.c login_tty.c logout.c logwtmp.c pty.c \ +SRCS= flopen.c login.c login_tty.c logout.c logwtmp.c logwtmpx.c pty.c \ login_cap.c login_class.c login_auth.c login_times.c login_ok.c \ - login_crypt.c _secure_path.c uucplock.c property.c auth.c \ + login_crypt.c loginx.c logoutx.c _secure_path.c uucplock.c \ + property.c auth.c \ realhostname.c fparseln.c stub.c pidfile.c trimdomain.c \ dehumanize_number.c humanize_number.c humanize_unsigned.c pw_util.c INCS= libutil.h login_cap.h @@ -16,7 +17,8 @@ WARNS?= 3 CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../libc/gen/ -MAN+= flopen.3 login.3 login_auth.3 login_tty.3 logout.3 logwtmp.3 pty.3 \ +MAN+= flopen.3 login.3 loginx.3 login_auth.3 login_tty.3 logout.3 logwtmp.3 \ + pty.3 \ login_cap.3 login_class.3 login_times.3 login_ok.3 \ _secure_path.3 uucplock.3 property.3 auth.3 realhostname.3 \ realhostname_sa.3 trimdomain.3 fparseln.3 pidfile.3 \ @@ -35,6 +37,7 @@ MLINKS+=login_cap.3 login_getclassbyname.3 login_cap.3 login_close.3 \ login_cap.3 login_getcapnum.3 login_cap.3 login_getcapsize.3 \ login_cap.3 login_getcapbool.3 login_cap.3 login_getpath.3 \ login_cap.3 login_getpwclass.3 login_cap.3 login_setcryptfmt.3 +MLINKS+=loginx.3 logoutx.3 loginx.3 logwtmpx.3 MLINKS+=login_class.3 setusercontext.3 login_class.3 setclasscontext.3 \ login_class.3 setclassenvironment.3 login_class.3 setclassresources.3 MLINKS+=login_times.3 parse_lt.3 login_times.3 in_ltm.3 \ diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h index 77bf3df7c9..c79b25ee40 100644 --- a/lib/libutil/libutil.h +++ b/lib/libutil/libutil.h @@ -59,15 +59,19 @@ typedef struct _property { struct termios; struct winsize; struct utmp; +struct utmpx; struct in_addr; struct sockaddr; __BEGIN_DECLS int flopen(const char *, int, ...); void login(struct utmp *); +void loginx(const struct utmpx *); int login_tty(int); int logout(const char *); +int logoutx(const char *, int, int); void logwtmp(const char *, const char *, const char *); +void logwtmpx(const char *, const char *, const char *, int, int); void trimdomain(char *, int); int openpty(int *, int *, char *, struct termios *, struct winsize *); int forkpty(int *, char *, struct termios *, struct winsize *); diff --git a/lib/libutil/loginx.3 b/lib/libutil/loginx.3 new file mode 100644 index 0000000000..a2962a919c --- /dev/null +++ b/lib/libutil/loginx.3 @@ -0,0 +1,93 @@ +.\" $NetBSD: loginx.3,v 1.2 2003/04/16 13:35:15 wiz Exp $ +.\" +.\" Copyright (c) 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Thomas Klausner. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd September 26, 2002 +.Dt LOGINX 3 +.Os +.Sh NAME +.Nm loginx , +.Nm logoutx , +.Nm logwtmpx +.Nd login utility functions +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft void +.Fn loginx "const struct utmpx *ut" +.Ft int +.Fn logoutx "const char *line" "int status" "int type" +.Ft void +.Fn logwtmpx "const char *line" "const char *name" "const char *host" "int status" "int type" +.Sh DESCRIPTION +The +.Fn loginx , +.Fn logoutx , +and +.Fn logwtmpx +operate on the +.Xr utmpx 5 +database of currently logged in users, and the +.Xr wtmpx 5 +database of logins and logouts. +.Pp +The +.Fn loginx +function updates the +.Pa /var/run/utmpx +and +.Pa /var/log/wtmpx +databases with the information from +.Fa ut . +.Pp +.Fn logoutx +updates the entry corresponding to +.Fa line +with the type and status from +.Fa type +and +.Fa status . +.Pp +.Fn logwtmpx +writes an entry filled with data from +.Fa line , +.Fa name , +.Fa host , +.Fa status , +and +.Fa type +to the +.Xr wtmpx 5 +database. +.Sh RETURN VALUES +.Fn logoutx +returns 1 on success, and 0 if no corresponding entry was found. +.Sh SEE ALSO +.Xr endutxent 3 , +.Xr utmpx 5 diff --git a/include/utmp.h b/lib/libutil/loginx.c similarity index 54% copy from include/utmp.h copy to lib/libutil/loginx.c index 6431f8cc49..d111b3a91d 100644 --- a/include/utmp.h +++ b/lib/libutil/loginx.c @@ -1,11 +1,8 @@ +/* $NetBSD: loginx.c,v 1.2 2003/08/07 16:44:59 agc Exp $ */ + /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -15,11 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -34,34 +27,25 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)utmp.h 8.2 (Berkeley) 1/21/94 - * $FreeBSD: src/include/utmp.h,v 1.9 1999/08/27 23:44:53 peter Exp $ - * $DragonFly: src/include/utmp.h,v 1.2 2003/06/17 04:25:56 dillon Exp $ */ -#ifndef _UTMP_H_ -#define _UTMP_H_ - -#define _PATH_UTMP "/var/run/utmp" -#define _PATH_WTMP "/var/log/wtmp" -#define _PATH_LASTLOG "/var/log/lastlog" - -#define UT_NAMESIZE 16 /* see MAXLOGNAME in */ -#define UT_LINESIZE 8 -#define UT_HOSTSIZE 16 +#include -struct lastlog { - time_t ll_time; - char ll_line[UT_LINESIZE]; - char ll_host[UT_HOSTSIZE]; -}; +#include -struct utmp { - char ut_line[UT_LINESIZE]; - char ut_name[UT_NAMESIZE]; - char ut_host[UT_HOSTSIZE]; - time_t ut_time; -}; +#include +#include +#include +#include +#include +#include +#include +#include +#include -#endif /* !_UTMP_H_ */ +void +loginx(const struct utmpx *ut) +{ + (void)pututxline(ut); + (void)updwtmpx(_PATH_WTMPX, ut); +} diff --git a/include/utmp.h b/lib/libutil/logoutx.c similarity index 54% copy from include/utmp.h copy to lib/libutil/logoutx.c index 6431f8cc49..6793853fef 100644 --- a/include/utmp.h +++ b/lib/libutil/logoutx.c @@ -1,11 +1,8 @@ +/* $NetBSD: logoutx.c,v 1.2 2003/08/07 16:44:59 agc Exp $ */ + /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -15,11 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -34,34 +27,40 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)utmp.h 8.2 (Berkeley) 1/21/94 - * $FreeBSD: src/include/utmp.h,v 1.9 1999/08/27 23:44:53 peter Exp $ - * $DragonFly: src/include/utmp.h,v 1.2 2003/06/17 04:25:56 dillon Exp $ */ -#ifndef _UTMP_H_ -#define _UTMP_H_ - -#define _PATH_UTMP "/var/run/utmp" -#define _PATH_WTMP "/var/log/wtmp" -#define _PATH_LASTLOG "/var/log/lastlog" - -#define UT_NAMESIZE 16 /* see MAXLOGNAME in */ -#define UT_LINESIZE 8 -#define UT_HOSTSIZE 16 +#include -struct lastlog { - time_t ll_time; - char ll_line[UT_LINESIZE]; - char ll_host[UT_HOSTSIZE]; -}; +#include +#include +#include -struct utmp { - char ut_line[UT_LINESIZE]; - char ut_name[UT_NAMESIZE]; - char ut_host[UT_HOSTSIZE]; - time_t ut_time; -}; +#include +#include +#include +#include +#include +#include +#include +#include +#include -#endif /* !_UTMP_H_ */ +int +logoutx(const char *line, int status, int type) +{ + struct utmpx *utp, ut; + (void)strlcpy(ut.ut_line, line, sizeof(ut.ut_line)); + if ((utp = getutxline(&ut)) == NULL) { + endutxent(); + return 0; + } + utp->ut_type = type; + if (WIFEXITED(status)) + utp->ut_exit.e_exit = (uint16_t)WEXITSTATUS(status); + if (WIFSIGNALED(status)) + utp->ut_exit.e_termination = (uint16_t)WTERMSIG(status); + (void)gettimeofday(&utp->ut_tv, NULL); + (void)pututxline(utp); + endutxent(); + return 1; +} diff --git a/include/utmp.h b/lib/libutil/logwtmpx.c similarity index 54% copy from include/utmp.h copy to lib/libutil/logwtmpx.c index 6431f8cc49..f703149d8c 100644 --- a/include/utmp.h +++ b/lib/libutil/logwtmpx.c @@ -1,11 +1,8 @@ +/* $NetBSD: logwtmpx.c,v 1.2 2003/08/07 16:44:59 agc Exp $ */ + /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -15,11 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors + * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * @@ -34,34 +27,44 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)utmp.h 8.2 (Berkeley) 1/21/94 - * $FreeBSD: src/include/utmp.h,v 1.9 1999/08/27 23:44:53 peter Exp $ - * $DragonFly: src/include/utmp.h,v 1.2 2003/06/17 04:25:56 dillon Exp $ */ -#ifndef _UTMP_H_ -#define _UTMP_H_ +#include + +#include +#include +#include +#include +#include -#define _PATH_UTMP "/var/run/utmp" -#define _PATH_WTMP "/var/log/wtmp" -#define _PATH_LASTLOG "/var/log/lastlog" +#include +#include +#include +#include +#include +#include +#include -#define UT_NAMESIZE 16 /* see MAXLOGNAME in */ -#define UT_LINESIZE 8 -#define UT_HOSTSIZE 16 +void +logwtmpx(const char *line, const char *name, const char *host, int status, + int type) +{ + struct utmpx ut; -struct lastlog { - time_t ll_time; - char ll_line[UT_LINESIZE]; - char ll_host[UT_HOSTSIZE]; -}; + _DIAGASSERT(line != NULL); + _DIAGASSERT(name != NULL); + _DIAGASSERT(host != NULL); -struct utmp { - char ut_line[UT_LINESIZE]; - char ut_name[UT_NAMESIZE]; - char ut_host[UT_HOSTSIZE]; - time_t ut_time; -}; + (void)memset(&ut, 0, sizeof(ut)); + (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + ut.ut_type = type; + if (WIFEXITED(status)) + ut.ut_exit.e_exit = (uint16_t)WEXITSTATUS(status); + if (WIFSIGNALED(status)) + ut.ut_exit.e_termination = (uint16_t)WTERMSIG(status); + (void)gettimeofday(&ut.ut_tv, NULL); + (void)updwtmpx(_PATH_WTMPX, &ut); +} -#endif /* !_UTMP_H_ */ diff --git a/lib/pam_module/pam_lastlog/pam_lastlog.c b/lib/pam_module/pam_lastlog/pam_lastlog.c index 5cb931c865..52d0e6abe1 100644 --- a/lib/pam_module/pam_lastlog/pam_lastlog.c +++ b/lib/pam_module/pam_lastlog/pam_lastlog.c @@ -57,7 +57,6 @@ #include #include #include -#include #define PAM_SM_SESSION @@ -65,137 +64,297 @@ #include #include +#define SUPPORT_UTMP 1 +#define SUPPORT_UTMPX 1 + +#ifdef SUPPORT_UTMP +#include +static void doutmp(const char *, const char *, const char *, + const struct timeval *); +static void dolastlog(pam_handle_t *, int, const struct passwd *, const char *, + const char *, const struct timeval *); +#endif + +#ifdef SUPPORT_UTMPX +#include +static void doutmpx(const char *, const char *, const char *, + const struct sockaddr_storage *ss, const struct timeval *); +static void dolastlogx(pam_handle_t *, int, const struct passwd *, const char *, + const char *, const struct sockaddr_storage *ss, const struct timeval *); +#endif + +#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) +static void domsg(pam_handle_t *, time_t, const char *, size_t, const char *, + size_t); +#endif + +static void +logit(int level, const char *fmt, ...) +{ + va_list ap; + + openlog("pam_lastlog", LOG_PID, LOG_AUTHPRIV); + va_start(ap, fmt); + vsyslog(level, fmt, ap); + va_end(ap); + closelog(); +} + + PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, - int argc __unused, const char *argv[] __unused) + int argc __unused, const char *argv[] __unused) { - struct passwd *pwd; - struct utmp utmp; - struct lastlog ll; - time_t t; - const char *user; - const void *rhost, *tty; - off_t llpos; - int fd, pam_err; + struct passwd *pwd, pwres; + struct timeval now; + const char *user, *rhost, *tty; + const void *vrhost, *vtty; + int pam_err; + int quiet; + char pwbuf[1024]; +#ifdef LOGIN_CAP + login_cap_t *lc; +#endif pam_err = pam_get_user(pamh, &user, NULL); if (pam_err != PAM_SUCCESS) - return (pam_err); - if (user == NULL || (pwd = getpwnam(user)) == NULL) - return (PAM_SERVICE_ERR); + return pam_err; + + if (user == NULL || + getpwnam_r(user, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0 || + pwd == NULL) + return PAM_SERVICE_ERR; + PAM_LOG("Got user: %s", user); - pam_err = pam_get_item(pamh, PAM_RHOST, &rhost); - if (pam_err != PAM_SUCCESS) { - PAM_LOG("No PAM_RHOST"); + pam_err = pam_get_item(pamh, PAM_RHOST, &vrhost); + if (pam_err != PAM_SUCCESS) goto err; - } - pam_err = pam_get_item(pamh, PAM_TTY, &tty); - if (pam_err != PAM_SUCCESS) { - PAM_LOG("No PAM_TTY"); + rhost = (const char *)vrhost; + + pam_err = pam_get_item(pamh, PAM_TTY, &vtty); + if (pam_err != PAM_SUCCESS) goto err; - } + tty = (const char *)vtty; + if (tty == NULL) { - PAM_LOG("No PAM_TTY"); pam_err = PAM_SERVICE_ERR; goto err; } + if (strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)) == 0) - tty = (const char *)tty + strlen(_PATH_DEV); - if (*(const char *)tty == '\0') - return (PAM_SERVICE_ERR); - - fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT, 0644); - if (fd == -1) { - PAM_LOG("Failed to open %s", _PATH_LASTLOG); - goto file_err; - } + tty = tty + strlen(_PATH_DEV); - /* - * Record session in lastlog(5). - */ - llpos = (off_t)(pwd->pw_uid * sizeof(ll)); - if (lseek(fd, llpos, L_SET) != llpos) - goto file_err; - if ((flags & PAM_SILENT) == 0) { - if (read(fd, &ll, sizeof ll) == sizeof ll && ll.ll_time != 0) { - t = ll.ll_time; - if (*ll.ll_host != '\0') - pam_info(pamh, "Last login: %.*s from %.*s", - 24 - 5, ctime(&t), - (int)sizeof(ll.ll_host), ll.ll_host); - else - pam_info(pamh, "Last login: %.*s on %.*s", - 24 - 5, ctime(&t), - (int)sizeof(ll.ll_line), ll.ll_line); - } - if (lseek(fd, llpos, L_SET) != llpos) - goto file_err; + if (*tty == '\0') { + pam_err = PAM_SERVICE_ERR; + goto err; } - bzero(&ll, sizeof(ll)); - ll.ll_time = time(NULL); + (void)gettimeofday(&now, NULL); + + if ((flags & PAM_SILENT) != 0) + quiet = 1; + else { +#ifdef LOGIN_CAP + lc = login_getpwclass(pwd); + quiet = login_getcapbool(lc, "hushlogin", 0); + login_close(lc); +#else + quiet = 0; +#endif + } +#ifdef SUPPORT_UTMPX + dolastlogx(pamh, quiet, pwd, rhost, tty, NULL, &now); + doutmpx(user, rhost, tty, NULL, &now); + quiet = 1; +#endif +#ifdef SUPPORT_UTMP + doutmp(user, rhost, tty, &now); + dolastlog(pamh, quiet, pwd, rhost, tty, &now); +#endif +err: + if (openpam_get_option(pamh, "no_fail")) + return PAM_SUCCESS; + return pam_err; +} - /* note: does not need to be NUL-terminated */ - strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); - if (rhost != NULL && *(const char *)rhost != '\0') - /* note: does not need to be NUL-terminated */ - strncpy(ll.ll_host, rhost, sizeof(ll.ll_host)); +PAM_EXTERN int +pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused, + int argc __unused, const char *argv[] __unused) +{ + const void *vtty; + const char *tty; - if (write(fd, (char *)&ll, sizeof(ll)) != sizeof(ll) || close(fd) != 0) - goto file_err; + pam_get_item(pamh, PAM_TTY, &vtty); + if (vtty == NULL) + return PAM_SERVICE_ERR; + tty = (const char *)vtty; - PAM_LOG("Login recorded in %s", _PATH_LASTLOG); + if (strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)) == 0) + tty = tty + strlen(_PATH_DEV); - /* - * Record session in utmp(5) and wtmp(5). - */ - bzero(&utmp, sizeof(utmp)); - utmp.ut_time = time(NULL); - /* note: does not need to be NUL-terminated */ - strncpy(utmp.ut_name, user, sizeof(utmp.ut_name)); - if (rhost != NULL && *(const char *)rhost != '\0') - strncpy(utmp.ut_host, rhost, sizeof(utmp.ut_host)); - strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); - login(&utmp); + if (*tty == '\0') + return PAM_SERVICE_ERR; - return (PAM_SUCCESS); +#ifdef SUPPORT_UTMPX + if (logoutx(tty, 0, DEAD_PROCESS)) + logwtmpx(tty, "", "", 0, DEAD_PROCESS); + else + logit(LOG_NOTICE, "%s(): no utmpx record for %s", + __func__, tty); +#endif -file_err: - syslog(LOG_ERR, "%s: %m", _PATH_LASTLOG); - if (fd != -1) - close(fd); - pam_err = PAM_SYSTEM_ERR; -err: - if (openpam_get_option(pamh, "no_fail")) - return (PAM_SUCCESS); - return (pam_err); +#ifdef SUPPORT_UTMP + if (logout(tty)) + logwtmp(tty, "", ""); + else + logit(LOG_NOTICE, "%s(): no utmp record for %s", + __func__, tty); +#endif + return PAM_SUCCESS; } -PAM_EXTERN int -pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused, - int argc __unused, const char *argv[] __unused) +#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) +static void +domsg(pam_handle_t *pamh, time_t t, const char *host, size_t hsize, + const char *line, size_t lsize) { - const void *tty; + char buf[MAXHOSTNAMELEN + 32], *promptresp = NULL; int pam_err; - pam_err = pam_get_item(pamh, PAM_TTY, (const void **)&tty); - if (pam_err != PAM_SUCCESS) - goto err; - if (strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)) == 0) - tty = (const char *)tty + strlen(_PATH_DEV); - if (*(const char *)tty == '\0') - return (PAM_SERVICE_ERR); - if (logout(tty) != 1) - syslog(LOG_ERR, "%s(): no utmp record for %s", - __func__, (const char *)tty); - logwtmp(tty, "", ""); - return (PAM_SUCCESS); - - err: - if (openpam_get_option(pamh, "no_fail")) - return (PAM_SUCCESS); - return (pam_err); + if (*host) { + (void)snprintf(buf, sizeof(buf), "from %.*s ", + (int)hsize, host); + host = buf; + } + + pam_err = pam_prompt(pamh, PAM_TEXT_INFO, &promptresp, + "Last login: %.24s %son %.*s\n", ctime(&t), host, (int)lsize, line); + + if (pam_err == PAM_SUCCESS && promptresp) + free(promptresp); +} +#endif + +#ifdef SUPPORT_UTMPX +static void +doutmpx(const char *username, const char *hostname, const char *tty, + const struct sockaddr_storage *ss, const struct timeval *now) +{ + struct utmpx utmpx; + const char *t; + + memset((void *)&utmpx, 0, sizeof(utmpx)); + utmpx.ut_tv = *now; + (void)strncpy(utmpx.ut_name, username, sizeof(utmpx.ut_name)); + if (hostname) { + (void)strncpy(utmpx.ut_host, hostname, sizeof(utmpx.ut_host)); + if (ss) + utmpx.ut_ss = *ss; + } + (void)strncpy(utmpx.ut_line, tty, sizeof(utmpx.ut_line)); + utmpx.ut_type = USER_PROCESS; + utmpx.ut_pid = getpid(); + t = tty + strlen(tty); + if ((size_t)(t - tty) >= sizeof(utmpx.ut_id)) { + (void)strncpy(utmpx.ut_id, t - sizeof(utmpx.ut_id), + sizeof(utmpx.ut_id)); + } else { + (void)strncpy(utmpx.ut_id, tty, sizeof(utmpx.ut_id)); + } + if (pututxline(&utmpx) == NULL) + logit(LOG_NOTICE, "Cannot update utmpx %m"); + endutxent(); + if (updwtmpx(_PATH_WTMPX, &utmpx) != 0) + logit(LOG_NOTICE, "Cannot update wtmpx %m"); +} + +static void +dolastlogx(pam_handle_t *pamh, int quiet, const struct passwd *pwd, + const char *hostname __unused, const char *tty __unused, + const struct sockaddr_storage *ss __unused, + const struct timeval *now __unused) +{ + struct lastlogx ll; + if (!quiet) { + if (getlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != NULL) + domsg(pamh, (time_t)ll.ll_tv.tv_sec, ll.ll_host, + sizeof(ll.ll_host), ll.ll_line, + sizeof(ll.ll_line)); + } +#if 0 + ll.ll_tv = *now; + (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); + + if (hostname) + (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); + else + (void)memset(ll.ll_host, 0, sizeof(ll.ll_host)); + + if (ss) + ll.ll_ss = *ss; + else + (void)memset(&ll.ll_ss, 0, sizeof(ll.ll_ss)); + + if (updlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != 0) + logit(LOG_NOTICE, "Cannot update lastlogx %m"); + PAM_LOG("Login recorded in %s", _PATH_LASTLOGX); +#endif } +#endif + +#ifdef SUPPORT_UTMP +static void +doutmp(const char *username, const char *hostname, const char *tty, + const struct timeval *now) +{ + struct utmp utmp; + + (void)memset((void *)&utmp, 0, sizeof(utmp)); + utmp.ut_time = now->tv_sec; + (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); + if (hostname) + (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); + (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); + login(&utmp); +} + +static void +dolastlog(pam_handle_t *pamh, int quiet, const struct passwd *pwd, + const char *hostname, const char *tty, const struct timeval *now) +{ + struct lastlog ll; + int fd; + + if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) == -1) { + logit(LOG_NOTICE, "Cannot open `%s' %m", _PATH_LASTLOG); + return; + } + (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET); + + if (!quiet) { + if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && + ll.ll_time != 0) + domsg(pamh, ll.ll_time, ll.ll_host, + sizeof(ll.ll_host), ll.ll_line, + sizeof(ll.ll_line)); + (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET); + } + + ll.ll_time = now->tv_sec; + (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); + + if (hostname) + (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); + else + (void)memset(ll.ll_host, 0, sizeof(ll.ll_host)); + + (void)write(fd, &ll, sizeof(ll)); + (void)close(fd); + + PAM_LOG("Login recorded in %s", _PATH_LASTLOG); +} +#endif PAM_MODULE_ENTRY("pam_lastlog"); + diff --git a/libexec/Makefile b/libexec/Makefile index 9c5162755c..4b158f1459 100644 --- a/libexec/Makefile +++ b/libexec/Makefile @@ -29,6 +29,7 @@ SUBDIR= atrun \ tcpd \ telnetd \ tftpd \ + utmp_update \ xtend \ ypxfr diff --git a/libexec/talkd/Makefile b/libexec/talkd/Makefile index b85e7e87f7..501d1d31f7 100644 --- a/libexec/talkd/Makefile +++ b/libexec/talkd/Makefile @@ -1,12 +1,16 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 # $FreeBSD: src/libexec/talkd/Makefile,v 1.6.2.2 2001/10/18 12:30:42 des Exp $ -.PATH: ${.CURDIR}/../../usr.bin/wall +.PATH: ${.CURDIR}/../../usr.bin/wall ${.CURDIR}/../../usr.bin/who PROG= ntalkd -SRCS= talkd.c announce.c process.c table.c print.c ttymsg.c +SRCS= talkd.c announce.c process.c table.c print.c ttymsg.c utmpentry.c MAN= talkd.8 +DPADD= ${LIBUTIL} +LDADD= -lutil + CFLAGS+=-I${.CURDIR}/../../usr.bin/wall +CFLAGS+=-I${.CURDIR}/../../usr.bin/who -DSUPPORT_UTMP -DSUPPORT_UTMPX .include diff --git a/libexec/talkd/process.c b/libexec/talkd/process.c index 3c7257ebdb..70ec159c68 100644 --- a/libexec/talkd/process.c +++ b/libexec/talkd/process.c @@ -54,7 +54,7 @@ #include #include #include -#include +#include "utmpentry.h" #include "extern.h" @@ -183,47 +183,41 @@ do_announce(CTL_MSG *mp, CTL_RESPONSE *rp) int find_user(const char *name, char *tty) { - struct utmp ubuf; + struct utmpentry *ep; int status; - FILE *fd; struct stat statb; time_t best = 0; - char line[sizeof(ubuf.ut_line) + 1]; - char ftty[sizeof(_PATH_DEV) - 1 + sizeof(line)]; + char ftty[sizeof(_PATH_DEV) + sizeof(ep->line)]; + + getutentries(NULL, &ep); - if ((fd = fopen(_PATH_UTMP, "r")) == NULL) { - warnx("can't read %s", _PATH_UTMP); - return (FAILED); - } #define SCMPN(a, b) strncmp(a, b, sizeof (a)) status = NOT_HERE; (void) strcpy(ftty, _PATH_DEV); - while (fread((char *) &ubuf, sizeof ubuf, 1, fd) == 1) - if (SCMPN(ubuf.ut_name, name) == 0) { - strncpy(line, ubuf.ut_line, sizeof(ubuf.ut_line)); - line[sizeof(ubuf.ut_line)] = '\0'; + for (; ep; ep = ep->next) + if (SCMPN(ep->name, name) == 0) { if (*tty == '\0' || best != 0) { if (best == 0) status = PERMISSION_DENIED; /* no particular tty was requested */ (void) strcpy(ftty + sizeof(_PATH_DEV) - 1, - line); + ep->line); if (stat(ftty, &statb) == 0) { if (!(statb.st_mode & 020)) continue; if (statb.st_atime > best) { best = statb.st_atime; - (void) strcpy(tty, line); + (void) strcpy(tty, ep->line); status = SUCCESS; continue; } } } - if (strcmp(line, tty) == 0) { + if (strcmp(ep->line, tty) == 0) { status = SUCCESS; break; } } - fclose(fd); + return (status); } diff --git a/libexec/utmp_update/Makefile b/libexec/utmp_update/Makefile new file mode 100644 index 0000000000..ac9f33f6c2 --- /dev/null +++ b/libexec/utmp_update/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.2 2002/09/18 06:21:15 lukem Exp $ + +PROG= utmp_update +MAN= utmp_update.8 +BINOWN= root +BINMODE=4555 + +.include diff --git a/libexec/utmp_update/utmp_update.8 b/libexec/utmp_update/utmp_update.8 new file mode 100644 index 0000000000..afa7c7ab65 --- /dev/null +++ b/libexec/utmp_update/utmp_update.8 @@ -0,0 +1,61 @@ +.\" $NetBSD: utmp_update.8,v 1.1 2002/12/16 22:45:15 wiz Exp $ +.\" +.\" Copyright (c) 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Thomas Klausner. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd December 12, 2002 +.Dt UTMP_UPDATE 8 +.Os +.Sh NAME +.Nm utmp_update +.Nd update utmpx database +.Sh SYNOPSIS +.Nm +.Ar utmpx_entry +.Sh DESCRIPTION +.Nm +is a helper program to allow a user to update his own +.Xr utmpx 5 +entry. +.Nm +does some consistency checks on the +.Xr strvis 3 Ns No -encoded +.Ar utmpx_entry +and then updates the +.Xr utmpx 5 +database of currently logged in users. +.Pp +.Nm +should not be called directly, but will normally only be called by +.Xr pututxline 3 +if the privileges of the calling user are not sufficient. +.Sh EXIT STATUS +.Nm +returns 0 on success, and 1 if an error occurred. +.Sh SEE ALSO +.Xr pututxline 3 , +.Xr utmpx 5 diff --git a/libexec/utmp_update/utmp_update.c b/libexec/utmp_update/utmp_update.c new file mode 100644 index 0000000000..1722c0de2e --- /dev/null +++ b/libexec/utmp_update/utmp_update.c @@ -0,0 +1,137 @@ +/* $NetBSD: utmp_update.c,v 1.8 2008/04/28 20:23:04 martin Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int, char *[]); + +int +main(int argc, char *argv[]) +{ + struct utmpx *utx; + size_t len; + struct passwd *pwd; + struct stat st; + int fd; + int res; + uid_t euid, ruid; + char tty[MAXPATHLEN]; + + euid = geteuid(); + ruid = getuid(); + if (seteuid(ruid) == -1) + err(1, "seteuid"); + + if (argc != 2) { + (void)fprintf(stderr, "Usage: %s \n", + getprogname()); + exit(1); + } + + len = strlen(argv[1]); + + if (len > sizeof(*utx) * 4 + 1 || len < sizeof(*utx)) + errx(1, "Bad argument"); + + if ((utx = malloc(len)) == NULL) + err(1, NULL); + + res = strunvis((char *)utx, argv[1]); + if (res != (int)sizeof(*utx)) + errx(1, "Decoding error %s %d != %zu", argv[1], res, sizeof(*utx)); + + switch (utx->ut_type) { + case USER_PROCESS: + case DEAD_PROCESS: + break; + default: + errx(1, "Invalid utmpx type %d", (int)utx->ut_type); + } + + if (ruid != 0) { + if ((pwd = getpwuid(ruid)) == NULL) + errx(1, "User %lu does not exist in password database", + (long)ruid); + + if (strcmp(pwd->pw_name, utx->ut_name) != 0) + errx(1, "Current user `%s' does not match " + "`%s' in utmpx entry", pwd->pw_name, utx->ut_name); + } + + (void)snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, utx->ut_line); + fd = open(tty, O_RDONLY|O_NONBLOCK, 0); + if (fd != -1) { + if (fstat(fd, &st) == -1) + err(1, "Cannot stat `%s'", tty); + if (ruid != 0 && st.st_uid != ruid) + errx(1, "%s: Is not owned by you", tty); + if (!isatty(fd)) + errx(1, "%s: Not a tty device", tty); + (void)close(fd); + if (access(tty, W_OK|R_OK) == -1) + err(1, "%s", tty); + } else { + struct utmpx utold, *utoldp; + /* + * A daemon like ftpd that does not use a tty line? + * We only allow it to kill its own existing entries + */ + if (utx->ut_type != DEAD_PROCESS) + err(1, "Cannot open `%s'", tty); + + (void)memcpy(utold.ut_line, utx->ut_line, sizeof(utx->ut_line)); + if ((utoldp = getutxline(&utold)) == NULL) + err(1, "Cannot find existing entry for `%s'", + utx->ut_line); + if (utoldp->ut_pid != getppid()) + err(1, "Cannot modify entry for `%s'", tty); + } + + (void)seteuid(euid); + if (pututxline(utx) == NULL) + err(1, "Cannot update utmp entry"); + + return 0; +} diff --git a/sbin/init/Makefile b/sbin/init/Makefile index 4a63947d38..1aae303eb0 100644 --- a/sbin/init/Makefile +++ b/sbin/init/Makefile @@ -10,7 +10,7 @@ INSTALLFLAGS= -b -B.bak .if !defined(NOFSCHG) INSTALLFLAGS+= -fschg .endif -CFLAGS+=-DDEBUGSHELL -DSECURE -DLOGIN_CAP -DCOMPAT_SYSV_INIT +CFLAGS+=-DDEBUGSHELL -DSECURE -DLOGIN_CAP -DCOMPAT_SYSV_INIT -DSUPPORT_UTMPX DPADD= ${LIBUTIL} ${LIBCRYPT} LDADD= -lutil -lcrypt diff --git a/sbin/init/init.c b/sbin/init/init.c index 49a66ea063..2734f614b2 100644 --- a/sbin/init/init.c +++ b/sbin/init/init.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -109,7 +110,7 @@ static void setctty(const char *); typedef struct init_session { int se_index; /* index of entry in ttys file */ pid_t se_process; /* controlling process */ - time_t se_started; /* used to avoid thrashing */ + struct timeval se_started; /* used to avoid thrashing */ int se_flags; /* status of session */ #define SE_SHUTDOWN 0x1 /* session won't be restarted */ #define SE_PRESENT 0x2 /* session is in /etc/ttys */ @@ -137,6 +138,14 @@ static void badsys(int); static int runshutdown(void); static char *strk(char *); +#define DEATH 'd' +#define SINGLE_USER 's' +#define RUNCOM 'r' +#define READ_TTYS 't' +#define MULTI_USER 'm' +#define CLEAN_TTYS 'T' +#define CATATONIA 'c' + static state_func_t single_user(void); static state_func_t runcom(void); static state_func_t read_ttys(void); @@ -171,6 +180,16 @@ static void add_session(session_t *); static void del_session(session_t *); static session_t *find_session(pid_t); +#ifdef SUPPORT_UTMPX +static struct timeval boot_time; +state_t current_state = death; +static void session_utmpx(const session_t *, int); +static void make_utmpx(const char *, const char *, int, pid_t, + const struct timeval *, int); +static char get_runlevel(const state_t); +static void utmpx_set_runlevel(char, char); +#endif + static int Reboot = FALSE; static int howto = RB_AUTOBOOT; @@ -179,6 +198,7 @@ static volatile sig_atomic_t clang; static session_t *sessions; state_t requested_transition = runcom; + /* * The mother of all processes. */ @@ -191,6 +211,9 @@ main(int argc, char **argv) sigset_t mask; struct stat sts; +#ifdef SUPPORT_UTMPX + (void)gettimeofday(&boot_time, NULL); +#endif /* SUPPORT_UTMPX */ /* Dispose of random users. */ if (getuid() != 0) @@ -560,8 +583,14 @@ setsecuritylevel(int newlevel) static void transition(state_t s) { - for (;;) + for (;;) { +#ifdef SUPPORT_UTMPX + utmpx_set_runlevel(get_runlevel(current_state), + get_runlevel(s)); + current_state = s; +#endif s = (state_t) (*s)(); + } } /* @@ -573,6 +602,10 @@ clear_session_logs(session_t *sp) { char *line = sp->se_device + sizeof(_PATH_DEV) - 1; +#ifdef SUPPORT_UTMPX + if (logoutx(line, 0, DEAD_PROCESS)) + logwtmpx(line, "", "", 0, DEAD_PROCESS); +#endif if (logout(line)) logwtmp(line, "", ""); } @@ -836,6 +869,9 @@ runcom(void) runcom_mode = AUTOBOOT; /* the default */ /* NB: should send a message to the session logger to avoid blocking. */ +#ifdef SUPPORT_UTMPX + logwtmpx("~", "reboot", "", 0, INIT_PROCESS); +#endif logwtmp("~", "reboot", ""); return (state_func_t) read_ttys; } @@ -874,6 +910,9 @@ add_session(session_t *sp) if ((*session_db->put)(session_db, &key, &data, 0)) emergency("insert %d: %s", sp->se_process, strerror(errno)); +#ifdef SUPPORT_UTMPX + session_utmpx(sp, 1); +#endif } /* @@ -889,6 +928,9 @@ del_session(session_t *sp) if ((*session_db->del)(session_db, &key, 0)) emergency("delete %d: %s", sp->se_process, strerror(errno)); +#ifdef SUPPORT_UTMPX + session_utmpx(sp, 0); +#endif } /* @@ -1061,6 +1103,25 @@ read_ttys(void) session_t *sp, *snext; struct ttyent *typ; +#ifdef SUPPORT_UTMPX + if (sessions == NULL) { + struct stat st; + + make_utmpx("", BOOT_MSG, BOOT_TIME, 0, &boot_time, 0); + + /* + * If wtmpx is not empty, pick the down time from there + */ + if (stat(_PATH_WTMPX, &st) != -1 && st.st_size != 0) { + struct timeval down_time; + + TIMESPEC_TO_TIMEVAL(&down_time, + st.st_atime > st.st_mtime ? + &st.st_atimespec : &st.st_mtimespec); + make_utmpx("", DOWN_MSG, DOWN_TIME, 0, &down_time, 0); + } + } +#endif /* * Destroy any previous session state. * There shouldn't be any, but just in case... @@ -1144,8 +1205,8 @@ start_getty(session_t *sp) int too_quick = 0; char term[64], *env[2]; - if (current_time >= sp->se_started && - current_time - sp->se_started < GETTY_SPACING) { + if (current_time >= sp->se_started.tv_sec && + current_time - sp->se_started.tv_sec < GETTY_SPACING) { if (++sp->se_nspace > GETTY_NSPACE) { sp->se_nspace = 0; too_quick = 1; @@ -1233,7 +1294,7 @@ collect_child(pid_t pid) } sp->se_process = pid; - sp->se_started = time(NULL); + gettimeofday(&sp->se_started, NULL); add_session(sp); } @@ -1295,7 +1356,7 @@ multi_user(void) break; } sp->se_process = pid; - sp->se_started = time(NULL); + gettimeofday(&sp->se_started, NULL); add_session(sp); } @@ -1373,7 +1434,7 @@ clean_ttys(void) ) { /* Don't set SE_SHUTDOWN here */ sp->se_nspace = 0; - sp->se_started = 0; + sp->se_started.tv_sec = sp->se_started.tv_usec = 0; kill(sp->se_process, SIGHUP); } if (old_getty) @@ -1439,6 +1500,9 @@ death(void) static const int death_sigs[2] = { SIGTERM, SIGKILL }; /* NB: should send a message to the session logger to avoid blocking. */ +#ifdef SUPPORT_UTMPX + logwtmpx("~", "shutdown", "", 0, INIT_PROCESS); +#endif logwtmp("~", "shutdown", ""); for (sp = sessions; sp; sp = sp->se_next) { @@ -1663,3 +1727,86 @@ setprocresources(const char *cname) } } #endif + +#ifdef SUPPORT_UTMPX +static void +session_utmpx(const session_t *sp, int add) +{ + const char *name = sp->se_getty ? sp->se_getty : + (sp->se_window ? sp->se_window : ""); + const char *line = sp->se_device + sizeof(_PATH_DEV) - 1; + + make_utmpx(name, line, add ? LOGIN_PROCESS : DEAD_PROCESS, + sp->se_process, &sp->se_started, sp->se_index); +} + +static void +make_utmpx(const char *name, const char *line, int type, pid_t pid, + const struct timeval *tv, int session) +{ + struct utmpx ut; + const char *eline; + + (void)memset(&ut, 0, sizeof(ut)); + (void)strlcpy(ut.ut_name, name, sizeof(ut.ut_name)); + ut.ut_type = type; + (void)strlcpy(ut.ut_line, line, sizeof(ut.ut_line)); + ut.ut_pid = pid; + if (tv) + ut.ut_tv = *tv; + else + (void)gettimeofday(&ut.ut_tv, NULL); + ut.ut_session = session; + + eline = line + strlen(line); + if ((size_t)(eline - line) >= sizeof(ut.ut_id)) + line = eline - sizeof(ut.ut_id); + (void)strncpy(ut.ut_id, line, sizeof(ut.ut_id)); + + if (pututxline(&ut) == NULL) + warning("can't add utmpx record for `%s': %m", ut.ut_line); + endutxent(); +} + +static char +get_runlevel(const state_t s) +{ + if (s == (state_t)single_user) + return SINGLE_USER; + if (s == (state_t)runcom) + return RUNCOM; + if (s == (state_t)read_ttys) + return READ_TTYS; + if (s == (state_t)multi_user) + return MULTI_USER; + if (s == (state_t)clean_ttys) + return CLEAN_TTYS; + if (s == (state_t)catatonia) + return CATATONIA; + return DEATH; +} + +static void +utmpx_set_runlevel(char old, char new) +{ + struct utmpx ut; + + /* + * Don't record any transitions until we did the first transition + * to read ttys, which is when we are guaranteed to have a read-write + * /var. Perhaps use a different variable for this? + */ + if (sessions == NULL) + return; + + (void)memset(&ut, 0, sizeof(ut)); + (void)snprintf(ut.ut_line, sizeof(ut.ut_line), RUNLVL_MSG, new); + ut.ut_type = RUN_LVL; + (void)gettimeofday(&ut.ut_tv, NULL); + ut.ut_exit.e_exit = old; + ut.ut_exit.e_termination = new; + if (pututxline(&ut) == NULL) + warning("can't add utmpx record for `runlevel': %m"); + endutxent(); +} +#endif diff --git a/share/man/man5/Makefile b/share/man/man5/Makefile index d1af3a4e90..7475127cc0 100644 --- a/share/man/man5/Makefile +++ b/share/man/man5/Makefile @@ -61,6 +61,7 @@ MAN= acct.5 \ utf2.5 \ utf8.5 \ utmp.5 \ + utmpx.5 \ uuids.5 \ varsym.conf.5 @@ -78,5 +79,7 @@ MLINKS+=passwd.5 master.passwd.5 MLINKS+=resolver.5 resolv.conf.5 MLINKS+=utmp.5 lastlog.5 \ utmp.5 wtmp.5 +MLINKS+=utmpx.5 lastlogx.5 \ + utmpx.5 wtmpx.5 .include diff --git a/share/man/man5/utmpx.5 b/share/man/man5/utmpx.5 new file mode 100644 index 0000000000..26cb4abd6a --- /dev/null +++ b/share/man/man5/utmpx.5 @@ -0,0 +1,140 @@ +.\" $NetBSD: utmpx.5,v 1.7 2008/04/30 13:10:57 martin Exp $ +.\" +.\" Copyright (c) 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Thomas Klausner. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd January 31, 2007 +.Dt UTMPX 5 +.Os +.Sh NAME +.Nm utmpx , +.Nm wtmpx , +.Nm lastlogx +.Nd user accounting database +.Sh SYNOPSIS +.In utmpx.h +.Sh DESCRIPTION +In contrast to +.Pa utmp +and +.Pa wtmp , +the extended databases in +.Pa utmpx +and +.Pa wtmpx +reserve more space for logging hostnames, and also +information on a process' ID, termination signal and exit status. +.Pp +The +.In utmpx.h +header defines the structures and functions for logging user. +Currently logged in users are tracked in +.Pa /var/run/utmpx , +a list of all logins and logouts, as well as all shutdowns, reboots +and date changes, is kept in +.Pa /var/log/wtmpx , +and the last login of each user is noted in +.Pa /var/log/lastlogx . +.Pp +The interface to the +.Nm utmpx +file is described in +.Xr getutxent 3 . +.Pp +The +.Nm wtmpx +file can grow rapidly on busy systems, and is normally rotated with +.Xr newsyslog 8 . +.Pp +In the event of a date change, a shutdown, or a reboot, the following +items are logged in the +.Nm wtmpx +file: +.Bl -tag -width shutdownxx -compact -offset indent +.It Li date +The system time has been manually or automatically updated by +.Xr date 1 . +The command name +.Em date +is recorded in the field +.Fa ut_name . +In the field +.Fa ut_line , +the character +.Ql \\*(Ba +indicates the time prior to the change, and the character +.Ql \&{ +indicates the new time. +.It Li reboot +.It Li shutdown +A system reboot or shutdown has been initiated. +The character +.Ql \&~ +is placed in the field +.Fa ut_line , +and +.Li reboot +or +.Li shutdown +in the field +.Fa ut_name +(see +.Xr shutdown 8 +and +.Xr reboot 8 ) , +using +.Xr logwtmpx 3 . +.Pp +.El +.Sh FILES +.Bl -tag -width /var/log/lastlogx -compact +.It Pa /var/run/utmpx +The +.Nm utmpx +file. +.It Pa /var/log/wtmpx +The +.Nm wtmpx +file. +.It Pa /var/log/lastlogx +The +.Nm lastlogx +file. +.El +.Sh SEE ALSO +.Xr last 1 , +.Xr login 1 , +.Xr rwho 1 , +.Xr w 1 , +.Xr who 1 , +.Xr endutxent 3 , +.Xr logwtmpx 3 , +.Xr utmp 5 , +.Xr ac 8 , +.Xr init 8 , +.Xr newsyslog 8 , +.Xr reboot 8 diff --git a/usr.bin/finger/Makefile b/usr.bin/finger/Makefile index 4c1b8c8b0a..211705273b 100644 --- a/usr.bin/finger/Makefile +++ b/usr.bin/finger/Makefile @@ -2,8 +2,15 @@ # $FreeBSD: src/usr.bin/finger/Makefile,v 1.1.1.1.14.2 2001/04/25 11:29:20 ru Exp $ # $DragonFly: src/usr.bin/finger/Makefile,v 1.3 2007/08/27 16:50:53 pavalos Exp $ +.PATH: ${.CURDIR}/../../usr.bin/who + PROG= finger -SRCS= finger.c lprint.c net.c sprint.c util.c +SRCS= finger.c lprint.c net.c sprint.c util.c utmpentry.c MAN= finger.1 finger.conf.5 +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +CFLAGS+= -I${.CURDIR}/../../usr.bin/who -DSUPPORT_UTMP -DSUPPORT_UTMPX + .include diff --git a/usr.bin/finger/extern.h b/usr.bin/finger/extern.h index 726322cc4e..26b456ffd7 100644 --- a/usr.bin/finger/extern.h +++ b/usr.bin/finger/extern.h @@ -50,10 +50,11 @@ extern int oflag; extern int lflag; extern int Tflag; extern sa_family_t family; +struct utmpentry; void enter_lastlog(PERSON *); PERSON *enter_person(struct passwd *); -void enter_where(struct utmp *, PERSON *); +void enter_where(struct utmpentry *, PERSON *); PERSON *find_person(char *); int hide(struct passwd *); void lflag_print(void); diff --git a/usr.bin/finger/finger.c b/usr.bin/finger/finger.c index 4089f76e0b..16a14c7e76 100644 --- a/usr.bin/finger/finger.c +++ b/usr.bin/finger/finger.c @@ -73,7 +73,7 @@ #include #include #include -#include +#include "utmpentry.h" #include #include "finger.h" @@ -223,25 +223,19 @@ loginlist(void) PERSON *pn; DBT data, key; struct passwd *pw; - struct utmp user; + struct utmpentry *ep; int r, sflag1; - char name[UT_NAMESIZE + 1]; - if (!freopen(_PATH_UTMP, "r", stdin)) - err(1, "%s", _PATH_UTMP); - name[UT_NAMESIZE] = '\0'; - while (fread((char *)&user, sizeof(user), 1, stdin) == 1) { - if (!user.ut_name[0]) - continue; - if ((pn = find_person(user.ut_name)) == NULL) { - bcopy(user.ut_name, name, UT_NAMESIZE); - if ((pw = getpwnam(name)) == NULL) + getutentries(NULL, &ep); + for (; ep; ep = ep->next) { + if ((pn = find_person(ep->name)) == NULL) { + if ((pw = getpwnam(ep->name)) == NULL) continue; if (hide(pw)) continue; pn = enter_person(pw); } - enter_where(&user, pn); + enter_where(ep, pn); } if (db && lflag) for (sflag1 = R_FIRST;; sflag1 = R_NEXT) { @@ -262,7 +256,7 @@ userlist(int argc, char **argv) { PERSON *pn; DBT data, key; - struct utmp user; + struct utmpentry *ep; struct passwd *pw; int r, sflag1, *used, *ip; char **ap, **nargv, **np, **p; @@ -367,14 +361,11 @@ net: for (p = nargv; *p;) { * Scan thru the list of users currently logged in, saving * appropriate data whenever a match occurs. */ - if (!freopen(_PATH_UTMP, "r", stdin)) - err(1, "%s", _PATH_UTMP); - while (fread((char *)&user, sizeof(user), 1, stdin) == 1) { - if (!user.ut_name[0]) - continue; - if ((pn = find_person(user.ut_name)) == NULL) + getutentries(NULL, &ep); + for (; ep; ep = ep->next) { + if ((pn = find_person(ep->name)) == NULL) continue; - enter_where(&user, pn); + enter_where(ep, pn); } if (db) for (sflag1 = R_FIRST;; sflag1 = R_NEXT) { diff --git a/usr.bin/finger/finger.h b/usr.bin/finger/finger.h index 71edcd4227..39139053d3 100644 --- a/usr.bin/finger/finger.h +++ b/usr.bin/finger/finger.h @@ -60,8 +60,8 @@ typedef struct where { short writable; /* tty is writable */ time_t loginat; /* time of (last) login */ time_t idletime; /* how long idle (if logged in) */ - char tty[UT_LINESIZE+1]; /* null terminated tty line */ - char host[UT_HOSTSIZE+1]; /* null terminated remote host name */ + char *tty; /* null terminated tty line */ + char *host; /* null terminated remote host name */ } WHERE; #define UNPRIV_NAME "nobody" /* Preferred privilege level */ diff --git a/usr.bin/finger/util.c b/usr.bin/finger/util.c index 5279ec13f3..7128808d11 100644 --- a/usr.bin/finger/util.c +++ b/usr.bin/finger/util.c @@ -52,6 +52,7 @@ #include #include #include +#include "utmpentry.h" #include #include "finger.h" #include "pathnames.h" @@ -150,17 +151,15 @@ enter_lastlog(PERSON *pn) } void -enter_where(struct utmp *ut, PERSON *pn) +enter_where(struct utmpentry *ep, PERSON *pn) { WHERE *w; w = walloc(pn); w->info = LOGGEDIN; - bcopy(ut->ut_line, w->tty, UT_LINESIZE); - w->tty[UT_LINESIZE] = 0; - bcopy(ut->ut_host, w->host, UT_HOSTSIZE); - w->host[UT_HOSTSIZE] = 0; - w->loginat = (time_t)ut->ut_time; + w->tty = ep->line; + w->host = ep->host; + w->loginat = (time_t)ep->tv.tv_sec; find_idle_and_ttywrite(w); } diff --git a/usr.bin/last/Makefile b/usr.bin/last/Makefile index 8924186db1..5ef792d61c 100644 --- a/usr.bin/last/Makefile +++ b/usr.bin/last/Makefile @@ -5,4 +5,6 @@ PROG= last WARNS?= 1 +CFLAGS+=-DSUPPORT_UTMP -DSUPPORT_UTMPX + .include diff --git a/usr.bin/last/last.c b/usr.bin/last/last.c index 92c596930f..0eb4f778ad 100644 --- a/usr.bin/last/last.c +++ b/usr.bin/last/last.c @@ -1,3 +1,5 @@ +/* $NetBSD: last.c,v 1.15 2000/06/30 06:19:58 simonb Exp $ */ + /* * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. @@ -29,103 +31,126 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD: src/usr.bin/last/last.c,v 1.10.6.11 2002/11/02 23:00:26 mux Exp $ - * $DragonFly: src/usr.bin/last/last.c,v 1.5 2004/09/14 00:13:36 drhodus Exp $ - * - * @(#) Copyright (c) 1987, 1993, 1994 The Regents of the University of California. All rights reserved. - * @(#)last.c 8.2 (Berkeley) 4/2/94 */ +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; +#endif +__RCSID("$NetBSD: last.c,v 1.15 2000/06/30 06:19:58 simonb Exp $"); +#endif /* not lint */ + #include #include #include #include -#include -#include #include #include #include #include #include #include +#include #include +#ifdef SUPPORT_UTMPX +#include +#endif +#ifdef SUPPORT_UTMP #include -#include +#endif + +#ifndef UT_NAMESIZE +#define UT_NAMESIZE 8 +#define UT_LINESIZE 8 +#define UT_HOSTSIZE 16 +#endif +#ifndef SIGNATURE +#define SIGNATURE -1 +#endif + + + +#define NO 0 /* false/no */ +#define YES 1 /* true/yes */ + +#define TBUFLEN 30 /* length of time string buffer */ +#define TFMT "%a %b %d %R" /* strftime format string */ +#define LTFMT "%a %b %d %Y %T" /* strftime long format string */ +#define TFMTS "%R" /* strftime format string - time only */ +#define LTFMTS "%T" /* strftime long format string - " */ -#define NO 0 /* false/no */ -#define YES 1 /* true/yes */ -#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; +/* fmttime() flags */ +#define FULLTIME 0x1 /* show year, seconds */ +#define TIMEONLY 0x2 /* show time only, not date */ +#define GMT 0x4 /* show time at GMT, for offsets only */ -static struct utmp buf[1024]; /* utmp read buffer */ +#define MAXUTMP 1024; typedef struct arg { - char *name; /* argument */ + char *name; /* argument */ #define HOST_TYPE -2 #define TTY_TYPE -3 #define USER_TYPE -4 - int type; /* type of arg */ - struct arg *next; /* linked list pointer */ + int type; /* type of arg */ + struct arg *next; /* linked list pointer */ } ARG; -ARG *arglist; /* head of linked list */ - -LIST_HEAD(ttylisthead, ttytab) ttylist; - -struct ttytab { - time_t logout; /* log out time */ - char tty[UT_LINESIZE + 1]; /* terminal name */ - LIST_ENTRY(ttytab) list; -}; - -static const char *crmsg; /* cause of last reboot */ -static long currentout, /* current logout value */ - maxrec; /* records to display */ -static const char *file = _PATH_WTMP; /* wtmp file */ -static int sflag = 0; /* show delta in seconds */ -static int width = 5; /* show seconds in delta */ -static int yflag; /* show year */ -static int d_first; -static int snapfound = 0; /* found snapshot entry? */ -static time_t snaptime; /* if != 0, we will only - * report users logged in - * at this snapshot time - */ +static ARG *arglist; /* head of linked list */ + +typedef struct ttytab { + time_t logout; /* log out time */ + char tty[128]; /* terminal name */ + struct ttytab *next; /* linked list pointer */ +} TTY; +static TTY *ttylist; /* head of linked list */ + +static time_t currentout; /* current logout value */ +static long maxrec; /* records to display */ +static int fulltime = 0; /* Display seconds? */ int main(int, char *[]); -void addarg(int, char *); -time_t dateconv(char *); -void doentry(struct utmp *); -void hostconv(char *); -void onintr(int); -void printentry(struct utmp *, struct ttytab *); -char *ttyconv(char *); -char *ttyconv(char *); -int want(struct utmp *); -void usage(void); -void wtmp(void); - -void -usage(void) + +static void addarg(int, char *); +static TTY *addtty(const char *); +static void hostconv(char *); +static char *ttyconv(char *); +#ifdef SUPPORT_UTMPX +static void wtmpx(const char *, int, int, int); +#endif +#ifdef SUPPORT_UTMP +static void wtmp(const char *, int, int, int); +#endif +static char *fmttime(time_t, int); +static void usage(void); + +static +void usage(void) { - (void)fprintf(stderr, -"usage: last [-#] [-y] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h host]\n" -"\t[-t tty] [-s|w] [user ...]\n"); + (void)fprintf(stderr, "Usage: %s [-#%s] [-f file] [-t tty]" + " [-h hostname] [-T] [user ...]\n", getprogname(), +#ifdef SUPPORT_UTMPX + "w" +#else + "" +#endif + ); exit(1); } int -main(int argc, char **argv) +main(int argc, char *argv[]) { int ch; char *p; - - (void) setlocale(LC_TIME, ""); - d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); + char *file = NULL; + int namesize = UT_NAMESIZE; + int linesize = UT_LINESIZE; + int hostsize = UT_HOSTSIZE; maxrec = -1; - snaptime = 0; - while ((ch = getopt(argc, argv, "0123456789d:f:h:st:wy")) != -1) + + while ((ch = getopt(argc, argv, "0123456789f:h:H:L:N:t:T")) != -1) switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -134,17 +159,15 @@ main(int argc, char **argv) * a number after a dash. */ if (maxrec == -1) { - p = strchr(argv[optind - 1], ch); - if (p == NULL) - p = strchr(argv[optind], ch); - maxrec = atol(p); + p = argv[optind - 1]; + if (p[0] == '-' && p[1] == ch && !p[2]) + maxrec = atol(++p); + else + maxrec = atol(argv[optind] + 1); if (!maxrec) exit(0); } break; - case 'd': - snaptime = dateconv(optarg); - break; case 'f': file = optarg; break; @@ -152,25 +175,26 @@ main(int argc, char **argv) hostconv(optarg); addarg(HOST_TYPE, optarg); break; - case 's': - sflag++; /* Show delta as seconds */ - break; case 't': addarg(TTY_TYPE, ttyconv(optarg)); break; - case 'w': - width = 8; + case 'U': + namesize = atoi(optarg); + break; + case 'L': + linesize = atoi(optarg); + break; + case 'H': + hostsize = atoi(optarg); break; - case 'y': - yflag++; + case 'T': + fulltime = 1; break; case '?': default: usage(); } - if (sflag && width == 8) usage(); - if (argc) { setlinebuf(stdout); for (argv += optind; *argv; ++argv) { @@ -182,220 +206,50 @@ main(int argc, char **argv) addarg(USER_TYPE, *argv); } } - wtmp(); - exit(0); -} - -/* - * wtmp -- - * read through the wtmp file - */ -void -wtmp(void) -{ - struct utmp *bp; /* current structure */ - struct stat stb; /* stat of file for size */ - long bl; - int bytes, wfd; - char ct[80]; - struct tm *tm; - - LIST_INIT(&ttylist); - - if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) - err(1, "%s", file); - bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); - - (void)time(&buf[0].ut_time); - (void)signal(SIGINT, onintr); - (void)signal(SIGQUIT, onintr); - - while (--bl >= 0) { - if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || - (bytes = read(wfd, buf, sizeof(buf))) == -1) - err(1, "%s", file); - for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) - doentry(bp); - } - tm = localtime(&buf[0].ut_time); - (void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm); - printf("%s", ct); -} - -/* - * doentry -- - * process a single wtmp entry - */ -void -doentry(struct utmp *bp) -{ - struct ttytab *tt, *ttx; /* ttylist entry */ - - /* - * if the terminal line is '~', the machine stopped. - * see utmp(5) for more info. - */ - if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { - /* everybody just logged out */ - for (tt = LIST_FIRST(&ttylist); tt;) { - LIST_REMOVE(tt, list); - ttx = tt; - tt = LIST_NEXT(tt, list); - free(ttx); - } - currentout = -bp->ut_time; - crmsg = strncmp(bp->ut_name, "shutdown", UT_NAMESIZE) ? - "crash" : "shutdown"; - /* - * if we're in snapshot mode, we want to exit if this - * shutdown/reboot appears while we we are tracking the - * active range - */ - if (snaptime && snapfound) - exit(0); - /* - * don't print shutdown/reboot entries unless flagged for - */ - if (!snaptime && want(bp)) - printentry(bp, NULL); - return; - } - /* - * if the line is '{' or '|', date got set; see - * utmp(5) for more info. - */ - if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') && - !bp->ut_line[1]) { - if (want(bp) && !snaptime) - printentry(bp, NULL); - return; - } - /* find associated tty */ - LIST_FOREACH(tt, &ttylist, list) - if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) - break; - - if (tt == NULL) { - /* add new one */ - tt = malloc(sizeof(struct ttytab)); - if (tt == NULL) - err(1, "malloc failure"); - tt->logout = currentout; - strncpy(tt->tty, bp->ut_line, UT_LINESIZE); - LIST_INSERT_HEAD(&ttylist, tt, list); - } - - /* - * print record if not in snapshot mode and wanted - * or in snapshot mode and in snapshot range - */ - if (bp->ut_name[0] && (want(bp) || (bp->ut_time < snaptime && - (tt->logout > snaptime || tt->logout < 1)))) { - snapfound = 1; - /* - * when uucp and ftp log in over a network, the entry in - * the utmp file is the name plus their process id. See - * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. - */ - if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) - bp->ut_line[3] = '\0'; - else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) - bp->ut_line[4] = '\0'; - printentry(bp, tt); - } - tt->logout = bp->ut_time; -} - -/* - * printentry -- - * output an entry - * - * If `tt' is non-NULL, use it and `crmsg' to print the logout time or - * logout type (crash/shutdown) as appropriate. - */ -void -printentry(struct utmp *bp, struct ttytab *tt) -{ - char ct[80]; - struct tm *tm; - time_t delta; /* time difference */ - - if (maxrec != -1 && !maxrec--) - exit(0); - tm = localtime(&bp->ut_time); - (void) strftime(ct, sizeof(ct), d_first ? - (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") : - (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm); - printf("%-*.*s %-*.*s %-*.*s %s%c", - UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, - UT_LINESIZE, UT_LINESIZE, bp->ut_line, - UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, - ct, tt == NULL ? '\n' : ' '); - if (tt == NULL) - return; - if (!tt->logout) { - puts(" still logged in"); - return; - } - if (tt->logout < 0) { - tt->logout = -tt->logout; - printf("- %s", crmsg); - } else { - tm = localtime(&tt->logout); - (void) strftime(ct, sizeof(ct), "%R", tm); - printf("- %s", ct); - } - delta = tt->logout - bp->ut_time; - if (sflag) { - printf(" (%8ld)\n", (long)delta); - } else { - tm = gmtime(&delta); - (void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm); - if (delta < 86400) - printf(" (%s)\n", ct); + if (file == NULL) { + /* XXX: for now, default to utmp */ +#ifdef SUPPORT_UTMP + if (access(_PATH_WTMP, R_OK) == 0) + file = _PATH_WTMP; else - printf(" (%ld+%s)\n", (long)delta / 86400, ct); +#endif +#ifdef SUPPORT_UTMPX + if (access(_PATH_WTMPX, R_OK) == 0) + file = _PATH_WTMPX; +#endif + if (file == NULL) +#if defined(SUPPORT_UTMPX) && defined(SUPPORT_UTMP) + errx(1, "Cannot access `%s' or `%s'", _PATH_WTMPX, + _PATH_WTMP); +#elif defined(SUPPORT_UTMPX) + errx(1, "Cannot access `%s'", _PATH_WTMPX); +#elif defined(SUPPORT_UTMP) + errx(1, "Cannot access `%s'", _PATH_WTMP); +#else + errx(1, "No utmp or utmpx support compiled in."); +#endif } +#if defined(SUPPORT_UTMPX) && defined(SUPPORT_UTMP) + if (file[strlen(file) - 1] == 'x') + wtmpx(file, namesize, linesize, hostsize); + else + wtmp(file, namesize, linesize, hostsize); +#elif defined(SUPPORT_UTMPX) + wtmpx(file, namesize, linesize, hostsize); +#elif defined(SUPPORT_UTMP) + wtmp(file, namesize, linesize, hostsize); +#else + errx(1, "Nu utmp or utmpx support compiled in."); +#endif + exit(0); } -/* - * want -- - * see if want this entry - */ -int -want(struct utmp *bp) -{ - ARG *step; - - if (snaptime) - return (NO); - - if (!arglist) - return (YES); - - for (step = arglist; step; step = step->next) - switch(step->type) { - case HOST_TYPE: - if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) - return (YES); - break; - case TTY_TYPE: - if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) - return (YES); - break; - case USER_TYPE: - if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) - return (YES); - break; - } - return (NO); -} /* * addarg -- * add an entry to a linked list of arguments */ -void +static void addarg(int type, char *arg) { ARG *cur; @@ -408,17 +262,34 @@ addarg(int type, char *arg) arglist = cur; } +/* + * addtty -- + * add an entry to a linked list of ttys + */ +static TTY * +addtty(const char *ttyname) +{ + TTY *cur; + + if (!(cur = (TTY *)malloc((u_int)sizeof(TTY)))) + err(1, "malloc failure"); + cur->next = ttylist; + cur->logout = currentout; + memmove(cur->tty, ttyname, sizeof(cur->tty)); + return (ttylist = cur); +} + /* * hostconv -- * convert the hostname to search pattern; if the supplied host name * has a domain attached that is the same as the current domain, rip * off the domain suffix since that's what login(1) does. */ -void +static void hostconv(char *arg) { static int first = 1; - static char *hostdot, name[MAXHOSTNAMELEN]; + static char *hostdot, name[MAXHOSTNAMELEN + 1]; char *argdot; if (!(argdot = strchr(arg, '.'))) @@ -427,6 +298,7 @@ hostconv(char *arg) first = 0; if (gethostname(name, sizeof(name))) err(1, "gethostname"); + name[sizeof(name) - 1] = '\0'; hostdot = strchr(name, '.'); } if (hostdot && !strcasecmp(hostdot, argdot)) @@ -437,7 +309,7 @@ hostconv(char *arg) * ttyconv -- * convert tty to correct name. */ -char * +static char * ttyconv(char *arg) { char *mval; @@ -464,94 +336,51 @@ ttyconv(char *arg) } /* - * dateconv -- - * Convert the snapshot time in command line given in the format - * [[CC]YY]MMDDhhmm[.SS]] to a time_t. - * Derived from atime_arg1() in usr.bin/touch/touch.c + * fmttime -- + * return pointer to (static) formatted time string. */ -time_t -dateconv(char *arg) +static char * +fmttime(time_t t, int flags) { - time_t timet; - struct tm *t; - int yearset; - char *p; - - /* Start with the current time. */ - if (time(&timet) < 0) - err(1, "time"); - if ((t = localtime(&timet)) == NULL) - err(1, "localtime"); - - /* [[CC]YY]MMDDhhmm[.SS] */ - if ((p = strchr(arg, '.')) == NULL) - t->tm_sec = 0; /* Seconds defaults to 0. */ - else { - if (strlen(p + 1) != 2) - goto terr; - *p++ = '\0'; - t->tm_sec = ATOI2(p); - } - - yearset = 0; - switch (strlen(arg)) { - case 12: /* CCYYMMDDhhmm */ - t->tm_year = ATOI2(arg); - t->tm_year *= 100; - yearset = 1; - /* FALLTHROUGH */ - case 10: /* YYMMDDhhmm */ - if (yearset) { - yearset = ATOI2(arg); - t->tm_year += yearset; - } else { - yearset = ATOI2(arg); - if (yearset < 69) - t->tm_year = yearset + 2000; - else - t->tm_year = yearset + 1900; - } - t->tm_year -= 1900; /* Convert to UNIX time. */ - /* FALLTHROUGH */ - case 8: /* MMDDhhmm */ - t->tm_mon = ATOI2(arg); - --t->tm_mon; /* Convert from 01-12 to 00-11 */ - t->tm_mday = ATOI2(arg); - t->tm_hour = ATOI2(arg); - t->tm_min = ATOI2(arg); - break; - case 4: /* hhmm */ - t->tm_hour = ATOI2(arg); - t->tm_min = ATOI2(arg); - break; - default: - goto terr; - } - t->tm_isdst = -1; /* Figure out DST. */ - timet = mktime(t); - if (timet == -1) -terr: errx(1, - "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); - return timet; + struct tm *tm; + static char tbuf[TBUFLEN]; + + tm = (flags & GMT) ? gmtime(&t) : localtime(&t); + strftime(tbuf, sizeof(tbuf), + (flags & TIMEONLY) + ? (flags & FULLTIME ? LTFMTS : TFMTS) + : (flags & FULLTIME ? LTFMT : TFMT), + tm); + return (tbuf); } +#ifdef SUPPORT_UTMP +#define TYPE(a) 0 +#define NAMESIZE UT_NAMESIZE +#define LINESIZE UT_LINESIZE +#define HOSTSIZE UT_HOSTSIZE +#define ut_timefld ut_time +#define FIRSTVALID 0 +#include "want.c" +#undef TYPE +#undef NAMESIZE +#undef LINESIZE +#undef HOSTSIZE +#undef ut_timefld +#undef FIRSTVALID +#endif -/* - * onintr -- - * on interrupt, we inform the user how far we've gotten - */ -void -onintr(int signo) -{ - char ct[80]; - struct tm *tm; - - tm = localtime(&buf[0].ut_time); - (void) strftime(ct, sizeof(ct), - d_first ? "%a %e %b %R" : "%a %b %e %R", - tm); - printf("\ninterrupted %s\n", ct); - if (signo == SIGINT) - exit(1); - (void)fflush(stdout); /* fix required for rsh */ -} +#ifdef SUPPORT_UTMPX +#define utmp utmpx +#define want wantx +#define wtmp wtmpx +#define buf bufx +#define onintr onintrx +#define TYPE(a) (a)->ut_type +#define NAMESIZE UTX_USERSIZE +#define LINESIZE UTX_LINESIZE +#define HOSTSIZE UTX_HOSTSIZE +#define ut_timefld ut_xtime +#define FIRSTVALID 1 +#include "want.c" +#endif diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c index 554c38bc68..a3b793c2fe 100644 --- a/usr.bin/login/login.c +++ b/usr.bin/login/login.c @@ -240,8 +240,8 @@ main(int argc, char **argv) snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); ttyn = tname; } - if ((tty = strrchr(ttyn, '/')) != NULL) - ++tty; + if (strncmp(ttyn, _PATH_DEV, sizeof(_PATH_DEV) -1) == 0) + tty = ttyn + sizeof(_PATH_DEV) -1; else tty = ttyn; diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile index eb67d471f6..2d8d1f7aef 100644 --- a/usr.bin/systat/Makefile +++ b/usr.bin/systat/Makefile @@ -1,14 +1,22 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 # $DragonFly: src/usr.bin/systat/Makefile,v 1.7 2008/11/10 04:59:45 swildner Exp $ +.PATH: ${.CURDIR}/../../usr.bin/who + PROG= systat CFLAGS+=-DINET6 -I${.CURDIR}/../../sys SRCS= cmds.c cmdtab.c convtbl.c devs.c fetch.c ifcmds.c ifstat.c iostat.c \ keyboard.c main.c mbufs.c netcmds.c netstat.c pigs.c sensors.c swap.c \ - icmp.c mode.c ip.c tcp.c vmstat.c ip6.c icmp6.c vmmeter.c symbols.c + icmp.c mode.c ip.c tcp.c vmstat.c ip6.c icmp6.c vmmeter.c symbols.c \ + utmpentry.c DPADD= ${LIBCURSES} ${LIBTERMCAP} ${LIBM} ${LIBKVM} ${LIBDEVSTAT} ${LIBKINFO} LDADD= -lcurses -ltermcap -lm -lkvm -ldevstat -lkinfo BINGRP= kmem BINMODE=2555 +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +CFLAGS+= -I${.CURDIR}/../../usr.bin/who -DSUPPORT_UTMP -DSUPPORT_UTMPX + .include diff --git a/usr.bin/systat/vmstat.c b/usr.bin/systat/vmstat.c index 87e1e5a845..2ff380bd95 100644 --- a/usr.bin/systat/vmstat.c +++ b/usr.bin/systat/vmstat.c @@ -63,7 +63,7 @@ #include #include #include -#include +#include "utmpentry.h" #include #include "systat.h" #include "extern.h" @@ -109,7 +109,6 @@ static void putlongdoublez(long double, int, int, int, int, int); static int ucount(void); static int ncpu; -static int ut; static char buf[26]; static time_t t; static double etime; @@ -119,16 +118,12 @@ static char **intrname; static int nextintsrow; static int extended_vm_stats; -struct utmp utmp; WINDOW * openkre(void) { - ut = open(_PATH_UTMP, O_RDONLY); - if (ut < 0) - error("No utmp"); return (stdscr); } @@ -136,7 +131,6 @@ void closekre(WINDOW *w) { - (void) close(ut); if (w == NULL) return; wclear(w); @@ -646,15 +640,13 @@ cmdkre(const char *cmd, char *args) static int ucount(void) { + struct utmpentry *ep; int nusers = 0; - if (ut < 0) - return (0); - while (read(ut, &utmp, sizeof(utmp))) - if (utmp.ut_name[0] != '\0') - nusers++; + getutentries(NULL, &ep); + for (; ep; ep = ep->next) + nusers++; - lseek(ut, 0L, L_SET); return (nusers); } diff --git a/usr.bin/users/Makefile b/usr.bin/users/Makefile index 6b9ad03122..cf0929cadd 100644 --- a/usr.bin/users/Makefile +++ b/usr.bin/users/Makefile @@ -1,6 +1,13 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 # $DragonFly: src/usr.bin/users/Makefile,v 1.3 2007/08/27 16:51:00 pavalos Exp $ +.PATH: ${.CURDIR}/../../usr.bin/who PROG= users +SRCS= users.c utmpentry.c + +DPADD= ${LIBUTIL} +LDADD= -lutil + +CFLAGS+= -I${.CURDIR}/../../usr.bin/who -DSUPPORT_UTMP -DSUPPORT_UTMPX .include diff --git a/usr.bin/users/users.c b/usr.bin/users/users.c index 473312ef26..a0ae65ea24 100644 --- a/usr.bin/users/users.c +++ b/usr.bin/users/users.c @@ -42,9 +42,9 @@ #include #include #include -#include +#include "utmpentry.h" -typedef char namebuf[UT_NAMESIZE]; +typedef char namebuf[32]; static int scmp(const void *, const void *); static void usage(void); @@ -52,11 +52,11 @@ static void usage(void); int main(int argc, char **argv) { + struct utmpentry *ep; namebuf *names = NULL; int ncnt = 0; int nmax = 0; int cnt; - struct utmp utmp; int ch; while ((ch = getopt(argc, argv, "")) != -1) { @@ -68,27 +68,24 @@ main(int argc, char **argv) argc -= optind; argv += optind; - if (!freopen(_PATH_UTMP, "r", stdin)) - err(1, "can't open %s", _PATH_UTMP); - while (fread((char *)&utmp, sizeof(utmp), 1, stdin) == 1) { - if (*utmp.ut_name) { - if (ncnt >= nmax) { - nmax += 32; - names = realloc(names, - sizeof (*names) * nmax); - if (names == NULL) - err(1, "realloc"); - } - strncpy(names[ncnt], utmp.ut_name, UT_NAMESIZE); - ++ncnt; + getutentries(NULL, &ep); + for (; ep; ep = ep->next) { + if (ncnt >= nmax) { + nmax += 32; + names = realloc(names, + sizeof (*names) * nmax); + if (names == NULL) + err(1, "realloc"); } + strcpy(names[ncnt], ep->name); + ++ncnt; } if (ncnt) { - qsort(names, ncnt, UT_NAMESIZE, scmp); - printf("%.*s", UT_NAMESIZE, names[0]); + qsort(names, ncnt, 32, scmp); + printf("%.*s", 32, names[0]); for (cnt = 1; cnt < ncnt; ++cnt) { - if (strncmp(names[cnt], names[cnt - 1], UT_NAMESIZE)) - printf(" %.*s", UT_NAMESIZE, names[cnt]); + if (strcmp(names[cnt], names[cnt - 1])) + printf(" %.*s", 32, names[cnt]); } printf("\n"); } @@ -105,5 +102,5 @@ usage(void) int scmp(const void *p, const void *q) { - return(strncmp(p, q, UT_NAMESIZE)); + return(strncmp(p, q, 32)); } diff --git a/usr.bin/w/Makefile b/usr.bin/w/Makefile index fa49280ca7..2e668bc429 100644 --- a/usr.bin/w/Makefile +++ b/usr.bin/w/Makefile @@ -12,6 +12,7 @@ LDADD= -lkvm LINKS= ${BINDIR}/w ${BINDIR}/uptime WARNS?= 2 +CFLAGS+= -DSUPPORT_UTMP -DSUPPORT_UTMPX .PATH: ${.CURDIR}/../../bin/ps .include diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c index c208f5e4e9..023748b3c2 100644 --- a/usr.bin/w/w.c +++ b/usr.bin/w/w.c @@ -70,7 +70,12 @@ #include #include #include +#ifdef SUPPORT_UTMP #include +#endif +#ifdef SUPPORT_UTMPX +#include +#endif #include #include @@ -79,10 +84,13 @@ #include "extern.h" struct timeval boottime; +#ifdef SUPPORT_UTMP struct utmp utmp; +#endif struct winsize ws; kvm_t *kd; time_t now; /* the current time of day */ +time_t then; time_t uptime; /* time of last reboot & elapsed time since */ int ttywidth; /* width of tty */ int argwidth; /* width of tty */ @@ -94,24 +102,33 @@ int use_ampm; /* use AM/PM time */ int use_comma; /* use comma as floats separator */ char **sel_users; /* login array of particular users selected */ char domain[MAXHOSTNAMELEN]; +int maxname = 8, maxline = 3, maxhost = 16; /* * One of these per active utmp entry. */ struct entry { struct entry *next; - struct utmp utmp; + char name[UTX_USERSIZE + 1]; + char line[UTX_LINESIZE + 1]; + char host[UTX_HOSTSIZE + 1]; + char type[2]; + struct timeval tv; dev_t tdev; /* dev_t of terminal */ time_t idle; /* idle time of terminal in seconds */ struct kinfo_proc *kp; /* `most interesting' proc */ char *args; /* arg list of interesting process */ struct kinfo_proc *dkp; /* debug option proc list */ + pid_t pid; /* pid or ~0 if not known */ } *ep, *ehead = NULL, **nextp = &ehead; #define debugproc(p) *((struct kinfo_proc **)&(p)->kp_spare[0]) static void pr_header(time_t *, int); +#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) static struct stat *ttystat(char *, int); +static void process(struct entry *); +#endif static void usage(int); static int this_is_uptime(const char *s); @@ -123,12 +140,15 @@ main(int argc, char **argv) struct kinfo_proc *kp; struct kinfo_proc *dkp; struct hostent *hp; - struct stat *stp; - FILE *ut; in_addr_t l; - time_t touched; int ch, i, nentries, nusers, wcmd, longidle, dropgid; char *memf, *nlistf, *p, *x; +#ifdef SUPPORT_UTMP + struct utmp *ut; +#endif +#ifdef SUPPORT_UTMPX + struct utmpx *utx; +#endif char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX]; (void)setlocale(LC_ALL, ""); @@ -195,62 +215,116 @@ main(int argc, char **argv) errx(1, "%s", errbuf); (void)time(&now); - if ((ut = fopen(_PATH_UTMP, "r")) == NULL) - err(1, "%s", _PATH_UTMP); +#ifdef SUPPORT_UTMPX + setutxent(); +#endif +#ifdef SUPPORT_UTMP + setutent(); +#endif if (*argv) sel_users = argv; - for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) { - if (utmp.ut_name[0] == '\0') + nusers = 0; +#ifdef SUPPORT_UTMPX + while ((utx = getutxent()) != NULL) { + if (utx->ut_type != USER_PROCESS) continue; - if (!(stp = ttystat(utmp.ut_line, UT_LINESIZE))) - continue; /* corrupted record */ ++nusers; - if (wcmd == 0) - continue; + if (sel_users) { int usermatch; char **user; usermatch = 0; for (user = sel_users; !usermatch && *user; user++) - if (!strncmp(utmp.ut_name, *user, UT_NAMESIZE)) + if (!strncmp(utx->ut_name, *user, UTX_USERSIZE)) usermatch = 1; if (!usermatch) continue; } + if ((ep = calloc(1, sizeof(struct entry))) == NULL) - errx(1, "calloc"); - *nextp = ep; - nextp = &ep->next; - memmove(&ep->utmp, &utmp, sizeof(struct utmp)); - ep->tdev = stp->st_rdev; -#ifdef CPU_CONSDEV - /* - * If this is the console device, attempt to ascertain - * the true console device dev_t. - */ - if (ep->tdev == 0) { - int mib[2]; - size_t size; - - mib[0] = CTL_MACHDEP; - mib[1] = CPU_CONSDEV; - size = sizeof(dev_t); - (void)sysctl(mib, 2, &ep->tdev, &size, NULL, 0); + err(1, NULL); + (void)memcpy(ep->name, utx->ut_name, sizeof(utx->ut_name)); + (void)memcpy(ep->line, utx->ut_line, sizeof(utx->ut_line)); + (void)memcpy(ep->host, utx->ut_host, sizeof(utx->ut_host)); + ep->name[sizeof(utx->ut_name)] = '\0'; + ep->line[sizeof(utx->ut_line)] = '\0'; + ep->host[sizeof(utx->ut_host)] = '\0'; +#if 1 + /* XXX: Actually we don't support the utx->ut_ss stuff yet */ + if (!nflag || getnameinfo((struct sockaddr *)&utx->ut_ss, + utx->ut_ss.ss_len, ep->host, sizeof(ep->host), NULL, 0, + NI_NUMERICHOST) != 0) { + (void)memcpy(ep->host, utx->ut_host, + sizeof(utx->ut_host)); + ep->host[sizeof(utx->ut_host)] = '\0'; } #endif - touched = stp->st_atime; - if (touched < ep->utmp.ut_time) { - /* tty untouched since before login */ - touched = ep->utmp.ut_time; + ep->type[0] = 'x'; + ep->tv = utx->ut_tv; + ep->pid = utx->ut_pid; + + *nextp = ep; + nextp = &(ep->next); + if (wcmd != 0) + process(ep); + } +#endif + +#ifdef SUPPORT_UTMP + while ((ut = getutent()) != NULL) { + if (ut->ut_name[0] == '\0') + continue; + ++nusers; + if (sel_users) { + int usermatch; + char **user; + + usermatch = 0; + for (user = sel_users; !usermatch && *user; user++) + if (!strncmp(ut->ut_name, *user, UT_NAMESIZE)) + usermatch = 1; + if (!usermatch) + continue; } - if ((ep->idle = now - touched) < 0) - ep->idle = 0; + + /* Don't process entries that we have utmpx for */ + for (ep = ehead; ep != NULL; ep = ep->next) { + if (strncmp(ep->line, ut->ut_line, + sizeof(ut->ut_line)) == 0) + break; + } + if (ep != NULL) { + --nusers; /* Duplicate entry */ + continue; + } + + if ((ep = calloc(1, sizeof(struct entry))) == NULL) + err(1, NULL); + (void)memcpy(ep->name, ut->ut_name, sizeof(ut->ut_name)); + (void)memcpy(ep->line, ut->ut_line, sizeof(ut->ut_line)); + (void)memcpy(ep->host, ut->ut_host, sizeof(ut->ut_host)); + ep->name[sizeof(ut->ut_name)] = '\0'; + ep->line[sizeof(ut->ut_line)] = '\0'; + ep->host[sizeof(ut->ut_host)] = '\0'; + ep->tv.tv_sec = ut->ut_time; + + *nextp = ep; + nextp = &(ep->next); + if (wcmd != 0) + process(ep); } - (void)fclose(ut); +#endif +#ifdef SUPPORT_UTMPX + endutxent(); +#endif +#ifdef SUPPORT_UTMP + endutent(); +#endif + if (header || wcmd == 0) { pr_header(&now, nusers); if (wcmd == 0) { @@ -263,12 +337,12 @@ main(int argc, char **argv) #define HEADER_FROM "FROM" #define HEADER_LOGIN_IDLE "LOGIN@ IDLE " #define HEADER_WHAT "WHAT\n" -#define WUSED (UT_NAMESIZE + UT_LINESIZE + UT_HOSTSIZE + \ +#define WUSED (maxname + maxline + maxhost + \ sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */ (void)printf("%-*.*s %-*.*s %-*.*s %s", - UT_NAMESIZE, UT_NAMESIZE, HEADER_USER, - UT_LINESIZE, UT_LINESIZE, HEADER_TTY, - UT_HOSTSIZE, UT_HOSTSIZE, HEADER_FROM, + maxname, maxname, HEADER_USER, + maxline, maxline, HEADER_TTY, + maxhost, maxhost, HEADER_FROM, HEADER_LOGIN_IDLE HEADER_WHAT); } @@ -299,6 +373,10 @@ main(int argc, char **argv) ep->dkp = kp; debugproc(kp) = dkp; } + if (ep->pid != 0 && ep->pid == kp->kp_pid) { + ep->kp = kp; + break; + } } } if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && @@ -337,6 +415,23 @@ main(int argc, char **argv) *nextp = save; } } +#if defined(SUPPORT_UTMP) && defined(SUPPORT_UTMPX) + else if (ehead != NULL) { + struct entry *from = ehead, *save; + + ehead = NULL; + while (from != NULL) { + for (nextp = &ehead; + (*nextp) && strcmp(from->line, (*nextp)->line) > 0; + nextp = &(*nextp)->next) + continue; + save = from; + from = from->next; + save->next = *nextp; + *nextp = save; + } + } +#endif if (!nflag) { if (gethostname(domain, sizeof(domain)) < 0 || @@ -349,10 +444,10 @@ main(int argc, char **argv) } for (ep = ehead; ep != NULL; ep = ep->next) { - char host_buf[UT_HOSTSIZE + 1]; + char host_buf[UTX_HOSTSIZE + 1]; - host_buf[UT_HOSTSIZE] = '\0'; - strncpy(host_buf, ep->utmp.ut_host, UT_HOSTSIZE); + host_buf[UTX_HOSTSIZE] = '\0'; + strncpy(host_buf, ep->host, maxhost); p = *host_buf ? host_buf : "-"; if ((x = strchr(p, ':')) != NULL) *x++ = '\0'; @@ -396,13 +491,14 @@ main(int argc, char **argv) } } (void)printf("%-*.*s %-*.*s %-*.*s ", - UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name, - UT_LINESIZE, UT_LINESIZE, - strncmp(ep->utmp.ut_line, "tty", 3) && - strncmp(ep->utmp.ut_line, "cua", 3) ? - ep->utmp.ut_line : ep->utmp.ut_line + 3, - UT_HOSTSIZE, UT_HOSTSIZE, *p ? p : "-"); - pr_attime(&ep->utmp.ut_time, &now); + maxname, maxname, ep->name, + maxline, maxline, + strncmp(ep->line, "tty", 3) && + strncmp(ep->line, "cua", 3) ? + ep->line : ep->line + 3, + maxhost, maxhost, *p ? p : "-"); + then = (time_t)ep->tv.tv_sec; + pr_attime(&then, &now); longidle = pr_idle(ep->idle); (void)printf("%.*s\n", argwidth - longidle, ep->args); } @@ -516,3 +612,51 @@ this_is_uptime(const char *s) return (0); return (-1); } + +#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) +static void +process(struct entry *ep) +{ + struct stat *stp; + time_t touched; + int max; + + if ((max = strlen(ep->name)) > maxname) + maxname = max; + if ((max = strlen(ep->line)) > maxline) + maxline = max; + if ((max = strlen(ep->host)) > maxhost) + maxhost = max; + + ep->tdev = 0; + ep->idle = (time_t)-1; + + if (!(stp = ttystat(ep->line, maxline))) + return; /* corrupted record */ + + ep->tdev = stp->st_rdev; +#ifdef CPU_CONSDEV + /* + * If this is the console device, attempt to ascertain + * the true console device dev_t. + */ + if (ep->tdev == 0) { + int mib[2]; + size_t size; + + mib[0] = CTL_MACHDEP; + mib[1] = CPU_CONSDEV; + size = sizeof(dev_t); + (void)sysctl(mib, 2, &ep->tdev, &size, NULL, 0); + } +#endif + + touched = stp->st_atime; + if (touched < ep->tv.tv_sec) { + /* tty untouched since before login */ + touched = ep->tv.tv_sec; + } + if ((ep->idle = now - touched) < 0) + ep->idle = 0; +} +#endif diff --git a/usr.bin/wall/Makefile b/usr.bin/wall/Makefile index 46967ae9af..e0ceefb491 100644 --- a/usr.bin/wall/Makefile +++ b/usr.bin/wall/Makefile @@ -1,9 +1,16 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 # $DragonFly: src/usr.bin/wall/Makefile,v 1.2 2007/08/27 16:51:01 pavalos Exp $ +.PATH: ${.CURDIR}/../../usr.bin/who + PROG= wall -SRCS= ttymsg.c wall.c +SRCS= ttymsg.c wall.c utmpentry.c BINGRP= tty BINMODE=2555 +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +CFLAGS+= -I${.CURDIR}/../../usr.bin/who -DSUPPORT_UTMP -DSUPPORT_UTMPX + .include diff --git a/usr.bin/wall/wall.c b/usr.bin/wall/wall.c index c5733e6409..ab3f335127 100644 --- a/usr.bin/wall/wall.c +++ b/usr.bin/wall/wall.c @@ -56,7 +56,7 @@ #include #include #include -#include +#include "utmpentry.h" #include "ttymsg.h" @@ -76,17 +76,14 @@ int main(int argc, char *argv[]) { struct iovec iov; - struct utmp utmp; + struct utmpentry *ep; int ch; int ingroup; - FILE *fp; struct wallgroup *g; struct group *grp; char **np; const char *p; struct passwd *pw; - char line[sizeof(utmp.ut_line) + 1]; - char username[sizeof(utmp.ut_name) + 1]; (void)setlocale(LC_CTYPE, ""); @@ -123,18 +120,15 @@ main(int argc, char *argv[]) makemsg(*argv); - if (!(fp = fopen(_PATH_UTMP, "r"))) - err(1, "cannot read %s", _PATH_UTMP); iov.iov_base = mbuf; iov.iov_len = mbufsize; + + getutentries(NULL, &ep); /* NOSTRICT */ - while (fread((char *)&utmp, sizeof(utmp), 1, fp) == 1) { - if (!utmp.ut_name[0]) - continue; + for (; ep; ep = ep->next) { if (grouplist) { ingroup = 0; - strlcpy(username, utmp.ut_name, sizeof(utmp.ut_name)); - pw = getpwnam(username); + pw = getpwnam(ep->name); if (!pw) continue; for (g = grouplist; g && ingroup == 0; g = g->next) { @@ -144,7 +138,7 @@ main(int argc, char *argv[]) ingroup = 1; else if ((grp = getgrgid(g->gid)) != NULL) { for (np = grp->gr_mem; *np; np++) { - if (strcmp(*np, username) == 0) { + if (strcmp(*np, ep->name) == 0) { ingroup = 1; break; } @@ -154,9 +148,11 @@ main(int argc, char *argv[]) if (ingroup == 0) continue; } - strncpy(line, utmp.ut_line, sizeof(utmp.ut_line)); - line[sizeof(utmp.ut_line)] = '\0'; - if ((p = ttymsg(&iov, 1, line, 60*5)) != NULL) + /* skip [xgk]dm/xserver entries (":0", ":1", etc.) */ + if (ep->line[0] == ':' && isdigit((unsigned char)ep->line[1])) + continue; + + if ((p = ttymsg(&iov, 1, ep->line, 60*5)) != NULL) warnx("%s", p); } exit(0); diff --git a/usr.bin/who/Makefile b/usr.bin/who/Makefile index 63c15ddad4..68d3b1d324 100644 --- a/usr.bin/who/Makefile +++ b/usr.bin/who/Makefile @@ -3,4 +3,8 @@ PROG= who +SRCS+= who.c utmpentry.c + +CFLAGS+= -DSUPPORT_UTMPX -DSUPPORT_UTMP + .include diff --git a/usr.bin/who/utmpentry.c b/usr.bin/who/utmpentry.c new file mode 100644 index 0000000000..8f3c917113 --- /dev/null +++ b/usr.bin/who/utmpentry.c @@ -0,0 +1,358 @@ +/* $NetBSD: utmpentry.c,v 1.16 2008/10/28 14:01:46 christos Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include +#include + +#ifdef SUPPORT_UTMP +#include +#endif +#ifdef SUPPORT_UTMPX +#include +#endif + +#include "utmpentry.h" + +/* Operations on timespecs. */ +#define timespecclear(tsp) (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L) +#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec) +#define timespeccmp(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#define timespecadd(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (/* CONSTCOND */ 0) +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (/* CONSTCOND */ 0) +#define timespec2ns(x) (((uint64_t)(x)->tv_sec) * 1000000000L + (x)->tv_nsec) + + +/* Fail the compile if x is not true, by constructing an illegal type. */ +#define COMPILE_ASSERT(x) /*LINTED null effect */ \ + ((void)sizeof(struct { unsigned : ((x) ? 1 : -1); })) + + +#ifdef SUPPORT_UTMP +static void getentry(struct utmpentry *, struct utmp *); +static struct timespec utmptime = {0, 0}; +#endif +#ifdef SUPPORT_UTMPX +static void getentryx(struct utmpentry *, struct utmpx *); +static struct timespec utmpxtime = {0, 0}; +#endif +#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) +static int setup(const char *); +static void adjust_size(struct utmpentry *e); +#endif + +int maxname = 8, maxline = 8, maxhost = 16; +int etype = 1 << USER_PROCESS; +static int numutmp = 0; +static struct utmpentry *ehead; + +#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) +static void +adjust_size(struct utmpentry *e) +{ + int max; + + if ((max = strlen(e->name)) > maxname) + maxname = max; + if ((max = strlen(e->line)) > maxline) + maxline = max; + if ((max = strlen(e->host)) > maxhost) + maxhost = max; +} + +static int +setup(const char *fname) +{ + int what = 3; + struct stat st; + const char *sfname; + + if (fname == NULL) { +#ifdef SUPPORT_UTMPX + setutxent(); +#endif +#ifdef SUPPORT_UTMP + setutent(); +#endif + } else { + size_t len = strlen(fname); + if (len == 0) + errx(1, "Filename cannot be 0 length."); + what = fname[len - 1] == 'x' ? 1 : 2; + if (what == 1) { +#ifdef SUPPORT_UTMPX + if (utmpxname(fname) == 0) + warnx("Cannot set utmpx file to `%s'", + fname); +#else + warnx("utmpx support not compiled in"); +#endif + } else { +#ifdef SUPPORT_UTMP + if (utmpname(fname) == 0) + warnx("Cannot set utmp file to `%s'", + fname); +#else + warnx("utmp support not compiled in"); +#endif + } + } +#ifdef SUPPORT_UTMPX + if (what & 1) { + sfname = fname ? fname : _PATH_UTMPX; + if (stat(sfname, &st) == -1) { + warn("Cannot stat `%s'", sfname); + what &= ~1; + } else { + if (timespeccmp(&st.st_mtimespec, &utmpxtime, >)) + utmpxtime = st.st_mtimespec; + else + what &= ~1; + } + } +#endif +#ifdef SUPPORT_UTMP + if (what & 2) { + sfname = fname ? fname : _PATH_UTMP; + if (stat(sfname, &st) == -1) { + warn("Cannot stat `%s'", sfname); + what &= ~2; + } else { + if (timespeccmp(&st.st_mtimespec, &utmptime, >)) + utmptime = st.st_mtimespec; + else + what &= ~2; + } + } +#endif + return what; +} +#endif + +void +endutentries(void) +{ + struct utmpentry *ep; + +#ifdef SUPPORT_UTMP + timespecclear(&utmptime); +#endif +#ifdef SUPPORT_UTMPX + timespecclear(&utmpxtime); +#endif + ep = ehead; + while (ep) { + struct utmpentry *sep = ep; + ep = ep->next; + free(sep); + } + ehead = NULL; + numutmp = 0; +} + +int +getutentries(const char *fname, struct utmpentry **epp) +{ +#ifdef SUPPORT_UTMPX + struct utmpx *utx; +#endif +#ifdef SUPPORT_UTMP + struct utmp *ut; +#endif +#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) + struct utmpentry *ep; + int what = setup(fname); + struct utmpentry **nextp = &ehead; + switch (what) { + case 0: + /* No updates */ + *epp = ehead; + return numutmp; + default: + /* Need to re-scan */ + ehead = NULL; + numutmp = 0; + } +#endif + +#ifdef SUPPORT_UTMPX + while ((what & 1) && (utx = getutxent()) != NULL) { + if (fname == NULL && ((1 << utx->ut_type) & etype) == 0) { + continue; + } + if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) { + warn(NULL); + return 0; + } + getentryx(ep, utx); + *nextp = ep; + nextp = &(ep->next); + } +#endif + +#ifdef SUPPORT_UTMP + if ((etype & (1 << USER_PROCESS)) != 0) { + while ((what & 2) && (ut = getutent()) != NULL) { + if (fname == NULL && (*ut->ut_name == '\0' || + *ut->ut_line == '\0')) + continue; + /* Don't process entries that we have utmpx for */ + for (ep = ehead; ep != NULL; ep = ep->next) { + if (strncmp(ep->line, ut->ut_line, + sizeof(ut->ut_line)) == 0) + break; + } + if (ep != NULL) + continue; + if ((ep = calloc(1, sizeof(*ep))) == NULL) { + warn(NULL); + return 0; + } + getentry(ep, ut); + *nextp = ep; + nextp = &(ep->next); + } + } +#endif + numutmp = 0; +#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) + if (ehead != NULL) { + struct utmpentry *from = ehead, *save; + + ehead = NULL; + while (from != NULL) { + for (nextp = &ehead; + (*nextp) && strcmp(from->line, (*nextp)->line) > 0; + nextp = &(*nextp)->next) + continue; + save = from; + from = from->next; + save->next = *nextp; + *nextp = save; + numutmp++; + } + } + *epp = ehead; + return numutmp; +#else + *epp = NULL; + return 0; +#endif +} + +#ifdef SUPPORT_UTMP +static void +getentry(struct utmpentry *e, struct utmp *up) +{ +#if 1 + COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); + COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); + COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); +#endif + + /* + * e has just been calloc'd. We don't need to clear it or + * append null-terminators, because its length is strictly + * greater than the source string. Use strncpy to _read_ + * up->ut_* because they may not be terminated. For this + * reason we use the size of the _source_ as the length + * argument. + */ + (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name)); + (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line)); + (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host)); + + e->tv.tv_sec = up->ut_time; + e->tv.tv_usec = 0; + e->pid = 0; + e->term = 0; + e->exit = 0; + e->sess = 0; + e->type = USER_PROCESS; + adjust_size(e); +} +#endif + +#ifdef SUPPORT_UTMPX +static void +getentryx(struct utmpentry *e, struct utmpx *up) +{ +#if 1 + COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); + COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); + COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); +#endif + + /* + * e has just been calloc'd. We don't need to clear it or + * append null-terminators, because its length is strictly + * greater than the source string. Use strncpy to _read_ + * up->ut_* because they may not be terminated. For this + * reason we use the size of the _source_ as the length + * argument. + */ + (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name)); + (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line)); + (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host)); + + e->tv = up->ut_tv; + e->pid = up->ut_pid; + e->term = up->ut_exit.e_termination; + e->exit = up->ut_exit.e_exit; + e->sess = up->ut_session; + e->type = up->ut_type; + adjust_size(e); +} +#endif diff --git a/usr.bin/who/utmpentry.h b/usr.bin/who/utmpentry.h new file mode 100644 index 0000000000..421f36ae23 --- /dev/null +++ b/usr.bin/who/utmpentry.h @@ -0,0 +1,76 @@ +/* $NetBSD: utmpentry.h,v 1.6 2008/04/28 20:24:15 martin Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(SUPPORT_UTMPX) +# include +# define WHO_NAME_LEN _UTX_USERSIZE +# define WHO_LINE_LEN _UTX_LINESIZE +# define WHO_HOST_LEN _UTX_HOSTSIZE +#elif defined(SUPPORT_UTMP) +# include +# define WHO_NAME_LEN UT_NAMESIZE +# define WHO_LINE_LEN UT_LINESIZE +# define WHO_HOST_LEN UT_HOSTSIZE +#else +# error Either SUPPORT_UTMPX or SUPPORT_UTMP must be defined! +#endif + + +struct utmpentry { + char name[WHO_NAME_LEN + 1]; + char line[WHO_LINE_LEN + 1]; + char host[WHO_HOST_LEN + 1]; + struct timeval tv; + pid_t pid; + uint16_t term; + uint16_t exit; + uint16_t sess; + uint16_t type; + struct utmpentry *next; +}; + +extern int maxname, maxline, maxhost; +extern int etype; + +/* + * getutentries provides a linked list of struct utmpentry and returns + * the number of entries. The first argument, if not null, names an + * alternate utmp(x) file to look in. + * + * The memory returned by getutentries belongs to getutentries. The + * list returned (or elements of it) may be returned again later if + * utmp hasn't changed in the meantime. + * + * endutentries clears and frees the cached data. + */ + +int getutentries(const char *, struct utmpentry **); +void endutentries(void); diff --git a/usr.bin/who/who.1 b/usr.bin/who/who.1 index 5d27479ab3..8f27d328ba 100644 --- a/usr.bin/who/who.1 +++ b/usr.bin/who/who.1 @@ -1,3 +1,5 @@ +.\" $NetBSD: who.1,v 1.21 2007/01/17 16:37:41 christos Exp $ +.\" .\" Copyright (c) 1986, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -9,11 +11,7 @@ .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors +.\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" @@ -30,126 +28,172 @@ .\" SUCH DAMAGE. .\" .\" @(#)who.1 8.2 (Berkeley) 12/30/93 -.\" $FreeBSD: src/usr.bin/who/who.1,v 1.7.2.5 2003/02/24 23:04:40 trhodes Exp $ -.\" $DragonFly: src/usr.bin/who/who.1,v 1.3 2004/10/29 17:09:09 liamfoy Exp $ .\" -.Dd October 25, 2004 +.Dd January 17, 2007 .Dt WHO 1 .Os .Sh NAME .Nm who -.Nd display who is on the system +.Nd display who is logged in .Sh SYNOPSIS .Nm -.Op Fl bHmqsTu -.Op Cm am I +.Op Fl abdHlmqrstTuv .Op Ar file +.Nm +.Ar am i .Sh DESCRIPTION The .Nm -utility displays information about currently logged in users. -By default, this includes the login name, tty name, date and time of login and -remote hostname if not local. +utility displays a list of all users currently logged on, showing for +each user the login name, tty name, the date and time of login, and +hostname if not local. +.Pp +Available options: .Pp -The options are as follows: -.Bl -tag -width indent +.Bl -tag -width file +.It Fl a +Same as +.Fl -bdlprTtuv . .It Fl b -Show date and time of last reboot. +Time of last system boot. +.It Fl d +Print dead processes. .It Fl H -Write column headings above the output. +Write column headings above the regular output. +.It Fl l +Print system login processes. .It Fl m -Show information about the terminal attached to standard input only. +Only print information about the current terminal. +This is the +.Tn POSIX +way of saying +.Nm +.Ar am i . +.It Fl p +Print active processes spawned by +.Xr init 8 . .It Fl q -.Dq "Quick mode" : -List the names and number of logged in users in columns. -All other command line options are ignored. +.Dq Quick mode : +List only the names and the number of users currently logged on. +When this option is used, all other options are ignored. +.It Fl r +Print the current runlevel. +Supported runlevels are: +.Bl -tag -width "s (SINGLE_USER)" +.It Dv d Pq Dv DEATH +The system has halted. +.It Dv s Pq Dv SINGLE_USER +The system is running in single user mode. +.It Dv r Pq Dv RUNCOM +The system is executing +.Pa /etc/rc . +.It Dv t Pq Dv READ_TTYS +The system is processing +.Pa /etc/ttys . +.It Dv m Pq Dv MULTI_USER +The system is running in multi-user mode. +.It Dv T Pq Dv CLEAN_TTYS +The system is in the process of stopping processes +associated with terminal devices. +.It Dv c Pq Dv CATATONIA +The system is in the process of shutting down and will +not create new processes. +.El .It Fl s -Show the name, line and time fields only. +List only the name, line and time fields. This is the default. .It Fl T -Indicate whether each user is accepting messages. -One of the following characters is written: -.Pp -.Bl -tag -width 1n -compact -.It Li + -User is accepting messages. -.It Li \&- -User is not accepting messages. -.It Li \&? -An error occurred. -.El +Print a character after the user name indicating the state of the +terminal line: +.Sq + +if the terminal is writable; +.Sq - +if it is not; +and +.Sq \&? +if a bad line is encountered. +.It Fl t +Print last system clock change. .It Fl u -Show idle time for each user in hours and minutes as -.Ar hh Ns : Ns Ar mm , -.Ql \&. -if the user has been idle less that a minute, and -.Dq Li old -if the user has been idle more than 24 hours. -.It Cm am I -Equivalent to -.Fl m . -.El -.Pp +Print the idle time for each user, and the associated process ID. +.It Fl v +When printing of more information is requested with +.Fl u , +this switch can be used to also printed +process termination signals, +process exit status, +session id for windowing +and the type of the entry, see documentation of ut_type in +.Xr getutxent 3 . +.It Ar \&am I +Returns the invoker's real user name. +.It Ar file By default, .Nm gathers information from the file -.Pa /var/run/utmp . -An alternate +.Pa /var/run/utmpx . +An alternative .Ar file may be specified which is usually -.Pa /var/log/wtmp +.Pa /var/log/wtmpx (or +.Pa /var/log/wtmp , +or +.Pa /var/log/wtmpx.[0-6] +or .Pa /var/log/wtmp.[0-6] depending on site policy as -.Pa wtmp +.Pa wtmpx can grow quite large and daily versions may or may not be kept around after compression by .Xr ac 8 ) . The +.Pa wtmpx +and .Pa wtmp file contains a record of every login, logout, crash, shutdown and date change since +.Pa wtmpx +and .Pa wtmp -was last truncated or +were last truncated or created. +.El .Pp If +.Pa /var/log/wtmpx +or .Pa /var/log/wtmp -is being used as the file, the user name may be empty -or one of the special characters '|', '}' and '~'. Logouts produce -an output line without any user name. For more information on the +are being used as the file, the user name may be empty +or one of the special characters '|', '}' and '~'. +Logouts produce an output line without any user name. +For more information on the special characters, see .Xr utmp 5 . -.Sh ENVIRONMENT -The -.Ev COLUMNS , LANG , LC_ALL -and -.Ev LC_TIME -environment variables affect the execution of -.Nm -as described in -.Xr environ 7 . .Sh FILES .Bl -tag -width /var/log/wtmp.[0-6] -compact .It Pa /var/run/utmp +.It Pa /var/run/utmpx .It Pa /var/log/wtmp .It Pa /var/log/wtmp.[0-6] +.It Pa /var/log/wtmpx +.It Pa /var/log/wtmpx.[0-6] .El -.Sh EXIT STATUS -.Ex -std .Sh SEE ALSO .Xr last 1 , +.Xr mesg 1 , .Xr users 1 , -.Xr w 1 , -.Xr utmp 5 +.Xr getuid 2 , +.Xr utmp 5 , +.Xr utmpx 5 .Sh STANDARDS The .Nm -utility conforms to -.St -p1003.1-2001 . +utility is expected to conform to +.St -p1003.2-92 . .Sh HISTORY A .Nm -command appeared in -.At v1 . +utility appeared in +.At v6 . diff --git a/usr.bin/who/who.c b/usr.bin/who/who.c index 1176155112..ab9aff8c2e 100644 --- a/usr.bin/who/who.c +++ b/usr.bin/who/who.c @@ -1,6 +1,11 @@ -/*- - * Copyright (c) 2002 Tim J. Robbins. - * All rights reserved. +/* $NetBSD: who.c,v 1.22 2008/07/21 14:19:28 lukem Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,11 +15,14 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -22,129 +30,175 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD: src/usr.bin/who/who.c,v 1.9.2.4 2002/12/21 00:44:58 tjr Exp $ - * $DragonFly: src/usr.bin/who/who.c,v 1.7 2008/06/05 18:06:33 swildner Exp $ */ +#include +#include #include -#include #include -#include #include -#include -#include -#include #include -#include #include #include #include #include #include #include +#ifdef SUPPORT_UTMP #include - -static void print_boottime(void); -static void heading(void); -static void process_utmp(FILE *); -static void quick(FILE *); -static void row(struct utmp *); -static int ttywidth(void); -static void usage(void); -static void whoami(FILE *); - -static int bflag; /* Show date and time of last reboot */ -static int Hflag; /* Write column headings */ -static int mflag; /* Show info about current terminal */ -static int qflag; /* "Quick" mode */ -static int sflag; /* Show name, line, time */ -static int Tflag; /* Show terminal state */ -static int uflag; /* Show idle time */ +#endif +#ifdef SUPPORT_UTMPX +#include +#endif + +#include "utmpentry.h" + +static void print_boottime(void); +static void output_labels(void); +static void who_am_i(const char *, int); +static void usage(void); +static void process(const char *, int); +static void eprint(const struct utmpentry *); +static void print(const char *, const char *, time_t, const char *, pid_t pid, + uint16_t term, uint16_t xit, uint16_t sess, uint16_t type); +static void quick(const char *); + +static int show_term; /* show term state */ +static int show_idle; /* show idle time */ +static int show_details; /* show exit status etc. */ +static int bflag; /* Show date and time of last reboot */ + +struct ut_type_names { + int type; + const char *name; +} ut_type_names[] = { +#ifdef SUPPORT_UTMPX + { EMPTY, "empty" }, + { RUN_LVL, "run level" }, + { BOOT_TIME, "boot time" }, + { OLD_TIME, "old time" }, + { NEW_TIME, "new time" }, + { INIT_PROCESS, "init process" }, + { LOGIN_PROCESS, "login process" }, + { USER_PROCESS, "user process" }, + { DEAD_PROCESS, "dead process" }, + { ACCOUNTING, "accounting" }, + { SIGNATURE, "signature" }, + { DOWN_TIME, "down time" }, +#endif /* SUPPORT_UTMPX */ + { -1, "unknown" } +}; int -main(int argc, char **argv) +main(int argc, char *argv[]) { - int ch; - const char *file; - FILE *fp; + int c, only_current_term, show_labels, quick_mode, default_mode; + int et = 0; - setlocale(LC_TIME, ""); + setlocale(LC_ALL, ""); - while ((ch = getopt(argc, argv, "HTbmqsu")) != -1) { - switch (ch) { - case 'H': /* Write column headings */ - Hflag = 1; - break; - case 'T': /* Show terminal state */ - Tflag = 1; + only_current_term = show_term = show_idle = show_labels = 0; + quick_mode = default_mode = 0; + + while ((c = getopt(argc, argv, "abdHlmpqrsTtuv")) != -1) { + switch (c) { + case 'a': + et = -1; + show_idle = show_details = 1; break; - case 'b': /* Show time and date since last boot */ + case 'b': +#if 0 + et |= (1 << BOOT_TIME); +#endif bflag = 1; break; - case 'm': /* Show info about current terminal */ - mflag = 1; + case 'd': + et |= (1 << DEAD_PROCESS); + break; + case 'H': + show_labels = 1; break; - case 'q': /* "Quick" mode */ - qflag = 1; + case 'l': + et |= (1 << LOGIN_PROCESS); break; - case 's': /* Show name, line, time */ - sflag = 1; + case 'm': + only_current_term = 1; break; - case 'u': /* Show idle time */ - uflag = 1; + case 'p': + et |= (1 << INIT_PROCESS); + break; + case 'q': + quick_mode = 1; + break; + case 'r': + et |= (1 << RUN_LVL); + break; + case 's': + default_mode = 1; + break; + case 'T': + show_term = 1; + break; + case 't': + et |= (1 << NEW_TIME); + break; + case 'u': + show_idle = 1; + break; + case 'v': + show_details = 1; break; default: usage(); - /*NOTREACHED*/ + /* NOTREACHED */ } } argc -= optind; argv += optind; - if (argc >= 2 && strcmp(argv[0], "am") == 0 && - strcasecmp(argv[1], "i") == 0) { - /* "who am i" or "who am I", equivalent to -m */ - mflag = 1; - argc -= 2; - argv += 2; - } - if (argc > 1) - usage(); + if (et != 0) + etype = et; - if (*argv != NULL) - file = *argv; - else - file = _PATH_UTMP; - if ((fp = fopen(file, "r")) == NULL) - err(1, "%s", file); - - if (qflag) - quick(fp); - else { - if (sflag) - Tflag = uflag = 0; - if (bflag) - print_boottime(); - if (Hflag) - heading(); - if (mflag) - whoami(fp); - else - process_utmp(fp); + if (chdir("/dev")) { + err(EXIT_FAILURE, "cannot change directory to /dev"); + /* NOTREACHED */ } - fclose(fp); + if (default_mode) + only_current_term = show_term = show_idle = 0; - exit(0); -} + if (!quick_mode && bflag) + print_boottime(); -static void -usage(void) -{ - fprintf(stderr, "usage: who [-bHmqsTu] [am I] [file]\n"); - exit(1); + switch (argc) { + case 0: /* who */ + if (quick_mode) { + quick(NULL); + } else if (only_current_term) { + who_am_i(NULL, show_labels); + } else { + process(NULL, show_labels); + } + break; + case 1: /* who utmp_file */ + if (quick_mode) { + quick(*argv); + } else if (only_current_term) { + who_am_i(*argv, show_labels); + } else { + process(*argv, show_labels); + } + break; + case 2: /* who am i */ + who_am_i(NULL, show_labels); + break; + default: + usage(); + /* NOTREACHED */ + } + + return 0; } static void @@ -160,152 +214,185 @@ print_boottime(void) } } + +static char * +strrstr(const char *str, const char *pat) +{ + const char *estr; + size_t len; + if (*pat == '\0') + return __DECONST(char *, str); + + len = strlen(pat); + + for (estr = str + strlen(str); str < estr; estr--) + if (strncmp(estr, pat, len) == 0) + return __DECONST(char *, estr); + return NULL; +} + +static void +who_am_i(const char *fname, int show_labels) +{ + struct passwd *pw; + const char *p; + char *t; + time_t now; + struct utmpentry *ehead, *ep; + + /* search through the utmp and find an entry for this tty */ + if ((p = ttyname(STDIN_FILENO)) != NULL) { + + /* strip directory prefixes for ttys */ + if ((t = strrstr(p, "/pts/")) != NULL || + (t = strrchr(p, '/')) != NULL) + p = t + 1; + + (void)getutentries(fname, &ehead); + for (ep = ehead; ep; ep = ep->next) + if (strcmp(ep->line, p) == 0) { + if (show_labels) + output_labels(); + eprint(ep); + return; + } + } else + p = "tty??"; + + (void)time(&now); + pw = getpwuid(getuid()); + if (show_labels) + output_labels(); + print(pw ? pw->pw_name : "?", p, now, "", getpid(), 0, 0, 0, 0); +} + +static void +process(const char *fname, int show_labels) +{ + struct utmpentry *ehead, *ep; + (void)getutentries(fname, &ehead); + if (show_labels) + output_labels(); + for (ep = ehead; ep != NULL; ep = ep->next) + eprint(ep); +} + static void -heading(void) +eprint(const struct utmpentry *ep) { - printf("%-*s ", UT_NAMESIZE, "NAME"); - if (Tflag) - printf("S "); - printf("%-*s ", UT_LINESIZE, "LINE"); - printf("%-*s ", 12, "TIME"); - if (uflag) - printf("IDLE "); - printf("%-*s", UT_HOSTSIZE, "FROM"); - putchar('\n'); + print(ep->name, ep->line, (time_t)ep->tv.tv_sec, ep->host, ep->pid, + ep->term, ep->exit, ep->sess, ep->type); } static void -row(struct utmp *ut) +print(const char *name, const char *line, time_t t, const char *host, + pid_t pid, uint16_t term, uint16_t xit, uint16_t sess, uint16_t type) { - char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE]; struct stat sb; - time_t idle = 0; - static int d_first = -1; - struct tm *tm; - char state = '?'; - - if (d_first < 0) - d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); - - if (Tflag || uflag) { - snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV, - UT_LINESIZE, ut->ut_line); - if (stat(tty, &sb) == 0) { - state = sb.st_mode & (S_IWOTH|S_IWGRP) ? - '+' : '-'; - idle = time(NULL) - sb.st_mtime; - } else { - err(1, "Cannot open %s", tty); + char state; + static time_t now = 0; + time_t idle; + const char *types = NULL; + size_t i; + + state = '?'; + idle = 0; + + for (i = 0; ut_type_names[i].type >= 0; i++) { + types = ut_type_names[i].name; + if (ut_type_names[i].type == type) + break; + } + + if (show_term || show_idle) { + if (now == 0) + time(&now); + + if (stat(line, &sb) == 0) { + state = (sb.st_mode & 020) ? '+' : '-'; + idle = now - sb.st_atime; } + } - printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name); - if (Tflag) - printf("%c ", state); - printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line); - tm = localtime(&ut->ut_time); - strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm); - printf("%-*s ", 12, buf); - if (uflag) { - if (idle < 60) - printf(" . "); - else if (idle < 24 * 60 * 60) - printf("%02d:%02d ", (int)(idle / 60 / 60), - (int)(idle / 60 % 60)); + (void)printf("%-*.*s ", maxname, maxname, name); + + if (show_term) + (void)printf("%c ", state); + + (void)printf("%-*.*s ", maxline, maxline, line); + (void)printf("%.12s ", ctime(&t) + 4); + + if (show_idle) { + if (idle < 60) + (void)printf(" . "); + else if (idle < (24 * 60 * 60)) + (void)printf("%02ld:%02ld ", + (long)(idle / (60 * 60)), + (long)(idle % (60 * 60)) / 60); else - printf(" old "); + (void)printf(" old "); + + (void)printf("\t%6d", pid); + + if (show_details) { + if (type == RUN_LVL) + (void)printf("\tnew=%c old=%c", term, xit); + else + (void)printf("\tterm=%d exit=%d", term, xit); + (void)printf(" sess=%d", sess); + (void)printf(" type=%s ", types); + } } - if (*ut->ut_host != '\0') - printf("(%.*s)", UT_HOSTSIZE, ut->ut_host); - putchar('\n'); + + if (*host) + (void)printf("\t(%.*s)", maxhost, host); + (void)putchar('\n'); } static void -process_utmp(FILE *fp) +output_labels(void) { - struct utmp ut; + (void)printf("%-*.*s ", maxname, maxname, "USER"); - while (fread(&ut, sizeof(ut), 1, fp) == 1) - if (*ut.ut_name != '\0') - row(&ut); -} + if (show_term) + (void)printf("S "); -static void -quick(FILE *fp) -{ - struct utmp ut; - int col, ncols, num; - - ncols = ttywidth(); - col = num = 0; - while (fread(&ut, sizeof(ut), 1, fp) == 1) { - if (*ut.ut_name == '\0') - continue; - printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name); - if (++col < ncols / (UT_NAMESIZE + 1)) - putchar(' '); - else { - col = 0; - putchar('\n'); - } - num++; + (void)printf("%-*.*s ", maxline, maxline, "LINE"); + (void)printf("WHEN "); + + if (show_idle) { + (void)printf("IDLE "); + (void)printf("\t PID"); + + (void)printf("\tCOMMENT"); } - if (col != 0) - putchar('\n'); - printf("# users = %d\n", num); + (void)putchar('\n'); } static void -whoami(FILE *fp) +quick(const char *fname) { - struct utmp ut; - struct passwd *pwd; - const char *name, *p, *tty; - - if ((tty = ttyname(STDIN_FILENO)) == NULL) - tty = "tty??"; - else if ((p = strrchr(tty, '/')) != NULL) - tty = p + 1; - - /* Search utmp for our tty, dump first matching record. */ - while (fread(&ut, sizeof(ut), 1, fp) == 1) - if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty, - UT_LINESIZE) == 0) { - row(&ut); - return; - } + struct utmpentry *ehead, *ep; + int num = 0; + + (void)getutentries(fname, &ehead); + for (ep = ehead; ep != NULL; ep = ep->next) { + (void)printf("%-*s ", maxname, ep->name); + if ((++num % 8) == 0) + (void)putchar('\n'); + } + if (num % 8) + (void)putchar('\n'); - /* Not found; fill the utmp structure with the information we have. */ - memset(&ut, 0, sizeof(ut)); - if ((pwd = getpwuid(getuid())) != NULL) - name = pwd->pw_name; - else - name = "?"; - strncpy(ut.ut_name, name, UT_NAMESIZE); - strncpy(ut.ut_line, tty, UT_LINESIZE); - time(&ut.ut_time); - row(&ut); + (void)printf("# users = %d\n", num); } -static int -ttywidth(void) +static void +usage(void) { - struct winsize ws; - long width; - char *cols, *ep; - - if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') { - errno = 0; - width = strtol(cols, &ep, 10); - if (errno || width <= 0 || width > INT_MAX || ep == cols || - *ep != '\0') - warnx("invalid COLUMNS environment variable ignored"); - else - return (width); - } - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) - return (ws.ws_col); - - return (80); + (void)fprintf(stderr, "Usage: %s [-abdHlmqrsTtuv] [file]\n\t%s am i\n", + getprogname(), getprogname()); + exit(EXIT_FAILURE); } diff --git a/usr.bin/write/Makefile b/usr.bin/write/Makefile index a76bb9953e..cfb6b4a439 100644 --- a/usr.bin/write/Makefile +++ b/usr.bin/write/Makefile @@ -1,8 +1,16 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 # $DragonFly: src/usr.bin/write/Makefile,v 1.3 2007/08/27 16:51:01 pavalos Exp $ +.PATH: ${.CURDIR}/../../usr.bin/who + PROG= write +SRCS= write.c utmpentry.c BINMODE=2555 BINGRP= tty +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +CFLAGS+= -I${.CURDIR}/../../usr.bin/who -DSUPPORT_UTMP -DSUPPORT_UTMPX + .include diff --git a/usr.bin/write/write.c b/usr.bin/write/write.c index fb2daac7f4..1b461a9e14 100644 --- a/usr.bin/write/write.c +++ b/usr.bin/write/write.c @@ -54,7 +54,7 @@ #include #include #include -#include +#include "utmpentry.h" static void done(int); static void do_write(const char *, const char *, uid_t, int *); @@ -132,21 +132,17 @@ usage(void) static int utmp_chk(const char *user, const char *tty) { - struct utmp u; - int ufd; + struct utmpentry *ep; - if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) - err(1, "open failed: %s\n", _PATH_UTMP); + getutentries(NULL, &ep); - while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) { - if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && - strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { - close(ufd); + for (; ep; ep = ep->next) { + if (strcmp(user, ep->name) == 0 && + strcmp(tty, ep->line) == 0) { return(0); } } - close(ufd); return(1); } @@ -164,37 +160,33 @@ utmp_chk(const char *user, const char *tty) static void search_utmp(const char *user, char *tty, size_t ttyl, const char *mytty, uid_t myuid) { - struct utmp u; + struct utmpentry *ep; time_t bestatime, atime; - int ufd, nloggedttys, nttys, msgsok, user_is_me; - char atty[UT_LINESIZE + 1]; + int nloggedttys, nttys, msgsok, user_is_me; - if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) - err(1, "open failed: %s", _PATH_UTMP); + getutentries(NULL, &ep); nloggedttys = nttys = 0; bestatime = 0; user_is_me = 0; - while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) - if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { + for (; ep; ep = ep->next) + if (strcmp(user, ep->name) == 0) { ++nloggedttys; - strlcpy(atty, u.ut_line, ttyl); - if (term_chk(atty, &msgsok, &atime, 0)) + if (term_chk(ep->line, &msgsok, &atime, 0)) continue; /* bad term? skip */ if (myuid && !msgsok) continue; /* skip ttys with msgs off */ - if (strcmp(atty, mytty) == 0) { + if (strcmp(ep->line, mytty) == 0) { user_is_me = 1; continue; /* don't write to yourself */ } ++nttys; if (atime > bestatime) { bestatime = atime; - strlcpy(tty, atty, ttyl); + strlcpy(tty, ep->line, ttyl); } } - close(ufd); if (nloggedttys == 0) errx(1, "%s is not logged in", user); if (nttys == 0) { diff --git a/usr.sbin/lastlogin/lastlogin.8 b/usr.sbin/lastlogin/lastlogin.8 index b1d91536f5..7b255ee629 100644 --- a/usr.sbin/lastlogin/lastlogin.8 +++ b/usr.sbin/lastlogin/lastlogin.8 @@ -63,9 +63,11 @@ utility differs from in that it only prints information regarding the very last login session. The last login database is never turned over or deleted in standard usage. .Sh FILES -.Bl -tag -width /var/log/lastlog -compact +.Bl -tag -width /var/log/lastlogx -compact .It Pa /var/log/lastlog last login database +.It Pa /var/log/lastlogx +last login database .El .Sh SEE ALSO .Xr last 1 , diff --git a/usr.sbin/lastlogin/lastlogin.c b/usr.sbin/lastlogin/lastlogin.c index 505a0e4781..5c2c50f3ee 100644 --- a/usr.sbin/lastlogin/lastlogin.c +++ b/usr.sbin/lastlogin/lastlogin.c @@ -35,6 +35,7 @@ */ #include +#include #include #include @@ -42,18 +43,22 @@ #include #include #include +#include #include static void output(struct passwd *, struct lastlog *); +static void outputx(struct passwd *, struct lastlogx *); static void usage(void); int main(int argc, char **argv) { int ch, i; + long offset; FILE *fp; struct passwd *passwd; struct lastlog last; + struct lastlogx lastx; while ((ch = getopt(argc, argv, "")) != -1) { usage(); @@ -66,12 +71,16 @@ main(int argc, char **argv) /* Process usernames given on the command line. */ if (argc > 1) { - long offset; for (i = 1; i < argc; ++i) { if ((passwd = getpwnam(argv[i])) == NULL) { warnx("user '%s' not found", argv[i]); continue; } + if ((getlastlogx(_PATH_LASTLOGX, passwd->pw_uid, + &lastx)) != NULL) { + outputx(passwd, &lastx); + goto done; + } /* Calculate the offset into the lastlog file. */ offset = (long)(passwd->pw_uid * sizeof(last)); if (fseek(fp, offset, SEEK_SET)) { @@ -88,6 +97,27 @@ main(int argc, char **argv) } /* Read all lastlog entries, looking for active ones */ else { + while ((passwd = getpwent()) != NULL) { + if ((getlastlogx(_PATH_LASTLOGX, passwd->pw_uid, + &lastx)) != NULL) { + if (lastx.ll_tv.tv_sec == 0) + continue; + outputx(passwd, &lastx); + } else { + offset = (long)(passwd->pw_uid * sizeof(last)); + + if (fseek(fp, offset, SEEK_SET)) { + continue; + } + if (fread(&last, sizeof(last), 1, fp) != 1) { + continue; + } + if (last.ll_time == 0) + continue; + output(passwd, &last); + } + } +#if 0 for (i = 0; fread(&last, sizeof(last), 1, fp) == 1; i++) { if (last.ll_time == 0) continue; @@ -96,8 +126,10 @@ main(int argc, char **argv) } if (ferror(fp)) warnx("fread error"); +#endif } +done: setpassent(0); /* Close passwd file pointers */ fclose(fp); @@ -110,11 +142,21 @@ output(struct passwd *p, struct lastlog *l) { printf("%-*.*s %-*.*s %-*.*s %s", UT_NAMESIZE, UT_NAMESIZE, p->pw_name, - UT_LINESIZE, UT_LINESIZE, l->ll_line, + UT_LINESIZE+4, UT_LINESIZE+4, l->ll_line, UT_HOSTSIZE, UT_HOSTSIZE, l->ll_host, (l->ll_time) ? ctime(&(l->ll_time)) : "Never logged in\n"); } +static void +outputx(struct passwd *p, struct lastlogx *l) +{ + printf("%-*.*s %-*.*s %-*.*s %s", + UT_NAMESIZE, UT_NAMESIZE, p->pw_name, + UT_LINESIZE+4, UT_LINESIZE+4, l->ll_line, + UT_HOSTSIZE, UT_HOSTSIZE, l->ll_host, + (l->ll_tv.tv_sec) ? ctime((&l->ll_tv.tv_sec)) : "Never logged in\n"); +} + static void usage(void) { diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile index 147b496148..29a488ecfa 100644 --- a/usr.sbin/syslogd/Makefile +++ b/usr.sbin/syslogd/Makefile @@ -2,16 +2,18 @@ # $FreeBSD: src/usr.sbin/syslogd/Makefile,v 1.14 2008/12/17 16:51:40 obrien Exp $ # $DragonFly: src/usr.sbin/syslogd/Makefile,v 1.3 2004/08/09 20:11:19 dillon Exp $ -.PATH: ${.CURDIR}/../../usr.bin/wall ${.CURDIR}/../nscd +.PATH: ${.CURDIR}/../../usr.bin/wall ${.CURDIR}/../nscd ${.CURDIR}/../../usr.bin/who PROG= syslogd MAN= syslog.conf.5 syslogd.8 -SRCS= pidfile.c syslogd.c ttymsg.c +SRCS= pidfile.c syslogd.c ttymsg.c utmpentry.c DPADD= ${LIBUTIL} LDADD= -lutil CFLAGS+= -DINET6 CFLAGS+= -I${.CURDIR}/../../usr.bin/wall +CFLAGS+= -I${.CURDIR}/../../usr.bin/who -DSUPPORT_UTMP -DSUPPORT_UTMPX + .include diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c index 4aca424bde..4f72452e86 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -96,7 +96,7 @@ #include #include #include -#include +#include "utmpentry.h" #include "pathnames.h" #include "ttymsg.h" @@ -164,7 +164,7 @@ struct filed { #define PRI_GT 0x4 char *f_program; /* program this applies to */ union { - char f_uname[MAXUNAMES][UT_NAMESIZE+1]; + char f_uname[MAXUNAMES][32+1]; struct { char f_hname[MAXHOSTNAMELEN]; struct addrinfo *f_addr; @@ -1355,28 +1355,25 @@ static void wallmsg(struct filed *f, struct iovec *iov, const int iovlen) { static int reenter; /* avoid calling ourselves */ - FILE *uf; - struct utmp ut; + + struct utmpentry *ep; int i; const char *p; - char line[sizeof(ut.ut_line) + 1]; if (reenter++) return; - if ((uf = fopen(_PATH_UTMP, "r")) == NULL) { - logerror(_PATH_UTMP); + + getutentries(NULL, &ep); + if (ep == NULL) { + logerror("getutentries"); reenter = 0; return; } /* NOSTRICT */ - while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) { - if (ut.ut_name[0] == '\0') - continue; + for (; ep; ep = ep->next) { /* We must use strncpy since ut_* may not be NUL terminated. */ - strncpy(line, ut.ut_line, sizeof(line) - 1); - line[sizeof(line) - 1] = '\0'; if (f->f_type == F_WALL) { - if ((p = ttymsg(iov, iovlen, line, TTYMSGTIME)) != + if ((p = ttymsg(iov, iovlen, ep->line, TTYMSGTIME)) != NULL) { errno = 0; /* already in msg */ logerror(p); @@ -1387,9 +1384,8 @@ wallmsg(struct filed *f, struct iovec *iov, const int iovlen) for (i = 0; i < MAXUNAMES; i++) { if (!f->f_un.f_uname[i][0]) break; - if (!strncmp(f->f_un.f_uname[i], ut.ut_name, - UT_NAMESIZE)) { - if ((p = ttymsg(iov, iovlen, line, TTYMSGTIME)) + if (!strcmp(f->f_un.f_uname[i], ep->name)) { + if ((p = ttymsg(iov, iovlen, ep->line, TTYMSGTIME)) != NULL) { errno = 0; /* already in msg */ logerror(p); @@ -1398,7 +1394,6 @@ wallmsg(struct filed *f, struct iovec *iov, const int iovlen) } } } - fclose(uf); reenter = 0; } @@ -2061,9 +2056,9 @@ cfline(const char *line, struct filed *f, const char *prog, const char *host) for (i = 0; i < MAXUNAMES && *p; i++) { for (q = p; *q && *q != ','; ) q++; - strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE); - if ((q - p) > UT_NAMESIZE) - f->f_un.f_uname[i][UT_NAMESIZE] = '\0'; + strncpy(f->f_un.f_uname[i], p, 32); + if ((q - p) > 32) + f->f_un.f_uname[i][32] = '\0'; else f->f_un.f_uname[i][q - p] = '\0'; while (*q == ',' || *q == ' ')