utmp

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

commit 014564adfe81bca6e03036ee6bc16ec6ab6029d1
Author: Christoph Lohmann <20h@r-36.net>
Date:   Tue, 30 Oct 2012 06:37:48 +0100

Initial commit of utmp(1).
Diffstat:
LICENSE | 24++++++++++++++++++++++++
Makefile | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
config.mk | 23+++++++++++++++++++++++
utmp.1 | 27+++++++++++++++++++++++++++
utmp.c | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 287 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2012, Roberto E. Vargas Caballero <k0ga@shike2.com> + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT +HOLDER 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. diff --git a/Makefile b/Makefile @@ -0,0 +1,57 @@ +# utmp - simple login +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = utmp.c +OBJ = ${SRC:.c=.o} + +all: options utmp + +options: + @echo utmp build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: config.mk + +utmp: ${OBJ} + @echo CC -o $@ + @${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + @echo cleaning + @rm -f utmp ${OBJ} utmp-${VERSION}.tar.gz + +dist: clean + @echo creating dist tarball + @mkdir -p utmp-${VERSION} + @cp -R LICENSE Makefile config.mk utmp.1 ${SRC} st-${VERSION} + @tar -cf utmp-${VERSION}.tar st-${VERSION} + @gzip utmp-${VERSION}.tar + @rm -rf utmp-${VERSION} + +install: all + @echo installing executable file to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f utmp ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/utmp + @chmod g+s ${DESTDIR}${PREFIX}/bin/utmp + @chgrp ${GROUP} ${DESTDIR}${PREFIX}/bin/utmp + @echo installing manual page to ${DESTDIR}${PREFIX}/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 + +uninstall: + @echo removing executable file from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/utmp + @echo removing manual page from ${DESTDIR}${PREFIX}/man1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/utmp.1 + +.PHONY: all options clean dist install uninstall diff --git a/config.mk b/config.mk @@ -0,0 +1,23 @@ +# 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/utmp.1 b/utmp.1 @@ -0,0 +1,27 @@ +.TH UTMP 1 utmp\-VERSION +.SH NAME +utmp \- manage utmp entry for a session +.SH SYPNOSIS +.B utmp +.RB [ +.IR shell\ arguments +.RB ] +.SH DESCRIPTION +.B utmp +adds an entry to utmp for a shell. It trusts the +.I SHELL +environment variable, and in case this one is not set, it gets the +user defined shell from the +.I /etc/passwd +file. All the arguments passed to +.B utmp +are passed to the child shell. +.SH AUTHORS +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 @@ -0,0 +1,156 @@ +/* See LICENSE for license details. */ +#define _POSIX_C_SOURCE 200112L + +#include <errno.h> +#include <ctype.h> +#include <stdarg.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> +#include <sys/wait.h> + +static struct utmpx utmp; +static struct passwd *pass; +static gid_t egid, gid; + + +void +die(const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + vfprintf(stderr, fmt, va); + 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; + + 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); + } + + setenv("LOGNAME", pass->pw_name, 1); + setenv("USER", pass->pw_name, 1); + setenv("SHELL", pass->pw_shell, 0); + setenv("HOME", pass->pw_dir, 0); + + switch (fork()) { + case 0: + execv(getenv("SHELL"), ++argv); + die("error executing shell:%s\n", strerror(errno)); + case -1: + die("error spawning child:%s\n", strerror(errno)); + default: + addutmp(STDIN_FILENO); + if (wait(&status) == -1) { + fprintf(stderr, "error waiting child:%s\n", + strerror(errno)); + } + delutmp(); + } + return 0; +} +