commit 014564adfe81bca6e03036ee6bc16ec6ab6029d1
Author: Christoph Lohmann <20h@r-36.net>
Date: Tue, 30 Oct 2012 06:37:48 +0100
Initial commit of utmp(1).
Diffstat:
A | LICENSE | | | 24 | ++++++++++++++++++++++++ |
A | Makefile | | | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | config.mk | | | 23 | +++++++++++++++++++++++ |
A | utmp.1 | | | 27 | +++++++++++++++++++++++++++ |
A | 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;
+}
+