utmp

simple login manager
git clone git://git.suckless.org/utmp
Log | Files | Refs | README | LICENSE

commit f7dcd4d7f36a82128f339f6446e87a7520e252cc
parent e3e5dc6339876da57c7c634f035511e76eb7b345
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date:   Wed, 14 Aug 2013 16:36:08 +0200

Fix portability problems

utmp interface is a very system dependet part in Unix. There are
three different interfaces: SystemV, POSIX and BSD, and they are
incompatibles between them. Utmp was using POSIX interface, but
not all the systems implement it (for examle OpenBSD doesn't it),
so it is desirable add the code for the three interfaces. This
patch adds this new code without using a ifdef hell.

Diffstat:
Makefile | 30++++++++++++++++--------------
README | 22++++++++++++++++++++++
bsd.c | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
config.bsd | 16++++++++++++++++
config.mk | 23-----------------------
config.posix | 15+++++++++++++++
config.sysv | 15+++++++++++++++
configure | 10++++++++++
posix.c | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
utmp.1 | 5-----
utmp.c | 116+++++++++++--------------------------------------------------------------------
11 files changed, 295 insertions(+), 142 deletions(-)

diff --git a/Makefile b/Makefile @@ -3,37 +3,39 @@ include config.mk -SRC = utmp.c -OBJ = ${SRC:.c=.o} +DIST = LICENSE Makefile config.mk utmp.1 utmp.c bsd.c posix.c +VERSION = 0.2 -all: options utmp +all: options utmp options: @echo utmp build options: @echo "CFLAGS = ${CFLAGS}" + @echo "CPPFLAGS = ${CPPFLAGS}" @echo "LDFLAGS = ${LDFLAGS}" + @echo "LDLIBS = ${LDLIBS}" @echo "CC = ${CC}" .c.o: @echo CC $< - @${CC} -c ${CFLAGS} $< + @${CC} $(CFLAGS) $(CPPFLAGS) -c $< -${OBJ}: config.mk - -utmp: ${OBJ} +utmp: $(OBJS) @echo CC -o $@ - @${CC} -o $@ ${OBJ} ${LDFLAGS} + @$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ +distclean: clean + @echo cleaning for distribution + @rm config.mk clean: @echo cleaning - @rm -f utmp ${OBJ} utmp-${VERSION}.tar.gz + @rm -f utmp utmp-${VERSION}.tar.gz *.o dist: clean @echo creating dist tarball @mkdir -p utmp-${VERSION} - @cp -R LICENSE Makefile config.mk utmp.1 ${SRC} utmp-${VERSION} - @tar -cf utmp-${VERSION}.tar utmp-${VERSION} - @gzip utmp-${VERSION}.tar + @cp -R $(DIST) utmp-${VERSION} + @tar -cf - utmp-${VERSION} | gzip > utmp-${VERSION}.tar.gz @rm -rf utmp-${VERSION} install: all @@ -43,7 +45,7 @@ install: all @chmod 755 ${DESTDIR}${PREFIX}/bin/utmp @chgrp ${GROUP} ${DESTDIR}${PREFIX}/bin/utmp @chmod g+s ${DESTDIR}${PREFIX}/bin/utmp - @echo installing manual page to ${DESTDIR}${PREFIX}/man1 + @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 @mkdir -p ${DESTDIR}${MANPREFIX}/man1 @sed "s/VERSION/${VERSION}/g" < utmp.1 > ${DESTDIR}${MANPREFIX}/man1/utmp.1 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/utmp.1 @@ -54,4 +56,4 @@ uninstall: @echo removing manual page from ${DESTDIR}${PREFIX}/man1 @rm -f ${DESTDIR}${MANPREFIX}/man1/utmp.1 -.PHONY: all options clean dist install uninstall +.PHONY: options clean dist install uninstall diff --git a/README b/README @@ -0,0 +1,22 @@ + +utmp is a small program which update the utmp record of the +current tty. It is designed for helping in some terminal emulators +or session manager which lack support for it. + +Compile: +------- + +There are three different interfaces to utmp; SystemV, BSD and POSIX. +SystemV and POSIX are basically the same interface (POSIX has +better definitons for utmp fields, like for example ut_pid which +in some system was a short instead of a pid_t), but BSD is very +different. utmp has implemented the three interfaces, and +it supplies three different config files, so the user only has +to rename the proper one to config.mk. It is also added a +basic configure, which selects POSIX interface for all plataforms +except for OpenBSD. + + $ ./configure + $ make + # make install + diff --git a/bsd.c b/bsd.c @@ -0,0 +1,61 @@ + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <util.h> +#include <grp.h> +#include <utmp.h> +#include <pwd.h> + +#include "utmp.h" + +extern void die(const char *fmt, ...); +extern struct passwd *pass; +extern gid_t egid, gid; +static struct utmp utmp; + +void +addutmp(void) +{ + unsigned ptyid; + char *pts, *cp, *host; + + + if (!(host = getenv("DISPLAY"))) + host = "-"; + + if (strlen(pass->pw_name) > sizeof(utmp.ut_name)) + die("incorrect username %s", pass->pw_name); + + if ((pts = ttyname(STDIN_FILENO)) == NULL) + die("error getting pty name:%s", strerror(errno)); + + for (cp = pts + strlen(pts) - 1; isdigit(*cp); --cp) + /* nothing */; + + ptyid = atoi(++cp); + if (ptyid > 999 || strlen(pts + 5) > sizeof(utmp.ut_line)) + die("Incorrect pts name %s\n", pts); + + /* remove /dev/ from pts */ + strncpy(utmp.ut_line, pts + 5, sizeof(utmp.ut_line)); + strncpy(utmp.ut_name, pass->pw_name, sizeof(utmp.ut_name)); + strncpy(utmp.ut_host, host, sizeof(utmp.ut_host)); + time(&utmp.ut_time); + + setgid(egid); + login(&utmp); + setgid(gid); +} + +void +delutmp(void) +{ + setgid(egid); + logout(utmp.ut_line); + setgid(gid); +} + diff --git a/config.bsd b/config.bsd @@ -0,0 +1,16 @@ + +# Customize below to fit your system. +GROUP = utmp + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +# flags +CPPFLAGS += -DVERSION=\"${VERSION}\" +LDLIBS += -lutil + +# Objects + +OBJS = utmp.o bsd.o + diff --git a/config.mk b/config.mk @@ -1,23 +0,0 @@ -# utmp version -VERSION = 0.1 - -# Customize below to fit your system. - -GROUP = utmp - -# paths -PREFIX = /usr/local -MANPREFIX = ${PREFIX}/share/man - -# includes and libs -INCS = -I. -I/usr/include -LIBS = -L/usr/lib -lc - -# flags -CPPFLAGS = -DVERSION=\"${VERSION}\" -CFLAGS += -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} -LDFLAGS += -s ${LIBS} - -# compiler and linker -CC ?= cc - diff --git a/config.posix b/config.posix @@ -0,0 +1,15 @@ + +# Customize below to fit your system. +GROUP = utmp + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +# flags +CPPFLAGS += -DVERSION=\"${VERSION}\" + +# Objects + +OBJS = utmp.o posix.o + diff --git a/config.sysv b/config.sysv @@ -0,0 +1,15 @@ + +# Customize below to fit your system. +GROUP = utmp + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +# flags +CPPFLAGS += -DUTMP_SYSTEMV -DVERSION=\"${VERSION}\" + +# Objects + +OBJS = utmp.o posix.o + diff --git a/configure b/configure @@ -0,0 +1,10 @@ +#!/bin/sh + +case `uname` in +OpenBSD) + ln config.bsd config.mk + ;; +*) + ln config.posix config.mk + ;; +esac diff --git a/posix.c b/posix.c @@ -0,0 +1,124 @@ + +#define _POSIX_C_SOURCE 200112L + +#include <errno.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <sys/types.h> +#include <unistd.h> +#include <utmpx.h> +#include <pwd.h> +#include <grp.h> + +#ifdef UTMP_SYSTEMV +#include <utmp.h> +#define getutxent getutent +#define getutxid getutid +#define getutxline getutline +#define pututxline pututline +#define setutxent setutent +#define endutxent endutent +#define utmpx utmp +#else +#include <utmpx.h> +#endif + +extern void die(const char *fmt, ...); +static struct utmpx utmp; +extern struct passwd *pass; +extern gid_t egid, gid; + + +/* + * From utmp(5) + * xterm and other terminal emulators directly create a USER_PROCESS + * record and generate the ut_id by using the string that suffix part of + * the terminal name (the characters following /dev/[pt]ty). If they find + * a DEAD_PROCESS for this ID, they recycle it, otherwise they create a new + * entry. If they can, they will mark it as DEAD_PROCESS on exiting and it + * is advised that they null ut_line, ut_time, ut_user, and ut_host as well. + */ + +struct utmpx * +findutmp(int type) +{ + struct utmpx *r; + + utmp.ut_type = type; + setutxent(); + for(;;) { + /* + * we can not use getutxline because we can search in + * DEAD_PROCESS to + */ + if(!(r = getutxid(&utmp))) + break; + if(!strcmp(r->ut_line, utmp.ut_line)) + break; + memset(r, 0, sizeof(*r)); /* for Solaris, IRIX64 and HPUX */ + } + return r; +} + +void +addutmp(void) +{ + unsigned ptyid; + char *pts, *cp, buf[5] = {'x'}; + + if (strlen(pass->pw_name) > sizeof(utmp.ut_user)) + die("incorrect username %s", pass->pw_name); + + if ((pts = ttyname(STDIN_FILENO)) == NULL) + die("error getting pty name\n"); + + for (cp = pts + strlen(pts) - 1; isdigit(*cp); --cp) + /* nothing */; + + ptyid = atoi(++cp); + if (ptyid > 999 || strlen(pts + 5) > sizeof(utmp.ut_line)) + die("Incorrect pts name %s\n", pts); + sprintf(buf + 1, "%03d", ptyid); + strncpy(utmp.ut_id, buf, 4); + + /* remove /dev/ part of the string */ + strncpy(utmp.ut_line, pts + 5, sizeof(utmp.ut_line)); + + if(!findutmp(DEAD_PROCESS)) + findutmp(USER_PROCESS); + + utmp.ut_type = USER_PROCESS; + strncpy(utmp.ut_user, pass->pw_name, sizeof(utmp.ut_user)); + utmp.ut_pid = getpid(); + utmp.ut_tv.tv_sec = time(NULL); + utmp.ut_tv.tv_usec = 0; + /* don't use no standard fields host and session */ + + setgid(egid); + if(!pututxline(&utmp)) + die("error adding utmp entry:%s", strerror(errno)); + setgid(gid); + endutxent(); +} + +void +delutmp(void) +{ + struct utmpx *r; + + setutxent(); + if((r = getutxline(&utmp)) != NULL) { + r->ut_type = DEAD_PROCESS; + r->ut_tv.tv_usec = r->ut_tv.tv_sec = 0; + setgid(egid); + if (!pututxline(r)) + die("error removing utmp entry:%s", strerror(errno)); + setgid(gid); + } + endutxent(); +} + diff --git a/utmp.1 b/utmp.1 @@ -20,8 +20,3 @@ are passed to the child shell. Written by Roberto E. Vargas Caballero .SH LICENSE See the LICENSE file for the terms of distribution. -.SH BUGS -utmp uses the posix interface defined in POSIX.1-2001. OpenBSD -and others BSD system don't implement these standard functions, so -this code could not be portable to them. - diff --git a/utmp.c b/utmp.c @@ -1,5 +1,4 @@ -/* See LICENSE for license details. */ -#define _POSIX_C_SOURCE 200112L + #include <errno.h> #include <ctype.h> @@ -7,18 +6,21 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <time.h> #include <sys/types.h> #include <unistd.h> -#include <utmpx.h> #include <pwd.h> #include <grp.h> #include <sys/wait.h> -static struct utmpx utmp; -static struct passwd *pass; -static gid_t egid, gid; + +#ifndef _POSIX_SAVED_IDS +#error "This program needs saved id behaviour" +#endif + + +struct passwd *pass; +gid_t egid, gid; void @@ -27,110 +29,25 @@ die(const char *fmt, ...) va_list va; va_start(va, fmt); vfprintf(stderr, fmt, va); + putc('\n', stderr); va_end(va); exit(EXIT_FAILURE); } -/* - * From utmp(5) - * xterm and other terminal emulators directly create a USER_PROCESS - * record and generate the ut_id by using the string that suffix part of - * the terminal name (the characters following /dev/[pt]ty). If they find - * a DEAD_PROCESS for this ID, they recycle it, otherwise they create a new - * entry. If they can, they will mark it as DEAD_PROCESS on exiting and it - * is advised that they null ut_line, ut_time, ut_user, and ut_host as well. - */ - -struct utmpx * -findutmp(int type) -{ - struct utmpx *r; - - utmp.ut_type = type; - setutxent(); - for(;;) { - /* - * we can not use getutxline because we can search in - * DEAD_PROCESS to - */ - if(!(r = getutxid(&utmp))) - break; - if(!strcmp(r->ut_line, utmp.ut_line)) - break; - memset(r, 0, sizeof(*r)); /* for Solaris, IRIX64 and HPUX */ - } - return r; -} - -void -addutmp(int fd) -{ - unsigned ptyid; - char *pts, *cp, buf[5] = {'x'}; - - if ((pts = ttyname(fd)) == NULL) - die("error getting pty name\n"); - - for (cp = pts + strlen(pts) - 1; isdigit(*cp); --cp) - /* nothing */; - - ptyid = atoi(++cp); - if (ptyid > 999 || strlen(pts + 5) > sizeof(utmp.ut_line)) - die("Incorrect pts name %s\n", pts); - sprintf(buf + 1, "%03d", ptyid); - strncpy(utmp.ut_id, buf, 4); - - /* remove /dev/ part of the string */ - strcpy(utmp.ut_line, pts + 5); - - if(!findutmp(DEAD_PROCESS)) - findutmp(USER_PROCESS); - - utmp.ut_type = USER_PROCESS; - strcpy(utmp.ut_user, pass->pw_name); - utmp.ut_pid = getpid(); - utmp.ut_tv.tv_sec = time(NULL); - utmp.ut_tv.tv_usec = 0; - /* don't use no standard fields host and session */ - - setgid(egid); - if(!pututxline(&utmp)) - perror("add utmp entry"); - setgid(gid); - endutxent(); -} - -void -delutmp(void) -{ - struct utmpx *r; - - setutxent(); - if((r = getutxline(&utmp)) != NULL) { - r->ut_type = DEAD_PROCESS; - r->ut_tv.tv_usec = r->ut_tv.tv_sec = 0; - setgid(egid); - pututxline(r); - setgid(gid); - } - endutxent(); -} - int main(int argc, char *argv[]) { int status; uid_t uid; + extern void addutmp(void), delutmp(void); egid = getegid(); gid = getgid(); setgid(gid); pass = getpwuid(uid = getuid()); - if(!pass || !pass->pw_name || - strlen(pass->pw_name) + 1 > sizeof(utmp.ut_user)) { - die("Process is running with an incorrect uid %d\n", uid); - } + if (!pass || !pass->pw_name) + die("Process is running with an incorrect uid %d", uid); setenv("LOGNAME", pass->pw_name, 1); setenv("USER", pass->pw_name, 1); @@ -140,11 +57,11 @@ main(int argc, char *argv[]) switch (fork()) { case 0: execv(getenv("SHELL"), ++argv); - die("error executing shell:%s\n", strerror(errno)); + die("error executing shell:%s", strerror(errno)); case -1: - die("error spawning child:%s\n", strerror(errno)); + die("error spawning child:%s", strerror(errno)); default: - addutmp(STDIN_FILENO); + addutmp(); if (wait(&status) == -1) { fprintf(stderr, "error waiting child:%s\n", strerror(errno)); @@ -153,4 +70,3 @@ main(int argc, char *argv[]) } return 0; } -