[dev] [utmp][PATCH] Fix portability problems

From: Roberto E. Vargas Caballero <k0ga_AT_shike2.com>
Date: Wed, 14 Aug 2013 16:56:11 +0200

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.
---
 Makefile     |  30 ++++++++-------
 README       |  22 +++++++++++
 bsd.c        |  60 +++++++++++++++++++++++++++++
 config.bsd   |  16 ++++++++
 config.mk    |  23 -----------
 config.posix |  15 ++++++++
 config.sysv  |  15 ++++++++
 configure    |  10 +++++
 posix.c      | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 utmp.1       |   5 ---
 utmp.c       | 116 ++++++++-----------------------------------------------
 11 files changed, 293 insertions(+), 142 deletions(-)
 create mode 100644 README
 create mode 100644 bsd.c
 create mode 100644 config.bsd
 delete mode 100644 config.mk
 create mode 100644 config.posix
 create mode 100644 config.sysv
 create mode 100755 configure
 create mode 100644 posix.c
diff --git a/Makefile b/Makefile
index 8c36390..a7c37ad 100644
--- a/Makefile
+++ b/Makefile
_AT_@ -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:
 	_AT_echo utmp build options:
 	_AT_echo "CFLAGS   = ${CFLAGS}"
+	_AT_echo "CPPFLAGS = ${CPPFLAGS}"
 	_AT_echo "LDFLAGS  = ${LDFLAGS}"
+	_AT_echo "LDLIBS	= ${LDLIBS}"
 	_AT_echo "CC       = ${CC}"
 
 .c.o:
 	_AT_echo CC $<
-	_AT_${CC} -c ${CFLAGS} $<
+	_AT_${CC} $(CFLAGS) $(CPPFLAGS) -c $<
 
-${OBJ}: config.mk
-
-utmp: ${OBJ}
+utmp: $(OBJS)
 	_AT_echo CC -o $@
-	_AT_${CC} -o $@ ${OBJ} ${LDFLAGS}
+	_AT_$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
 
+distclean: clean
+	_AT_echo cleaning for distribution
+	_AT_rm config.mk
 clean:
 	_AT_echo cleaning
-	_AT_rm -f utmp ${OBJ} utmp-${VERSION}.tar.gz
+	_AT_rm -f utmp utmp-${VERSION}.tar.gz *.o
 
 dist: clean
 	_AT_echo creating dist tarball
 	_AT_mkdir -p utmp-${VERSION}
-	_AT_cp -R LICENSE Makefile config.mk utmp.1 ${SRC} utmp-${VERSION}
-	_AT_tar -cf utmp-${VERSION}.tar utmp-${VERSION}
-	_AT_gzip utmp-${VERSION}.tar
+	_AT_cp -R $(DIST) utmp-${VERSION}
+	_AT_tar -cf -  utmp-${VERSION} | gzip > utmp-${VERSION}.tar.gz
 	_AT_rm -rf utmp-${VERSION}
 
 install: all
_AT_@ -43,7 +45,7 @@ install: all
 	_AT_chmod 755 ${DESTDIR}${PREFIX}/bin/utmp
 	_AT_chgrp ${GROUP} ${DESTDIR}${PREFIX}/bin/utmp
 	_AT_chmod g+s ${DESTDIR}${PREFIX}/bin/utmp
-	_AT_echo installing manual page to ${DESTDIR}${PREFIX}/man1
+	_AT_echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
 	_AT_mkdir -p ${DESTDIR}${MANPREFIX}/man1
 	_AT_sed "s/VERSION/${VERSION}/g" < utmp.1 > ${DESTDIR}${MANPREFIX}/man1/utmp.1
 	_AT_chmod 644 ${DESTDIR}${MANPREFIX}/man1/utmp.1
_AT_@ -54,4 +56,4 @@ uninstall:
 	_AT_echo removing manual page from ${DESTDIR}${PREFIX}/man1
 	_AT_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
new file mode 100644
index 0000000..f578223
--- /dev/null
+++ b/README
_AT_@ -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
new file mode 100644
index 0000000..29270dd
--- /dev/null
+++ b/bsd.c
_AT_@ -0,0 +1,60 @@
+
+#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 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
new file mode 100644
index 0000000..86a49c0
--- /dev/null
+++ b/config.bsd
_AT_@ -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
deleted file mode 100644
index 08aa8b3..0000000
--- a/config.mk
+++ /dev/null
_AT_@ -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
new file mode 100644
index 0000000..8d8c8c1
--- /dev/null
+++ b/config.posix
_AT_@ -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
new file mode 100644
index 0000000..9820595
--- /dev/null
+++ b/config.sysv
_AT_@ -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
new file mode 100755
index 0000000..8ea80ba
--- /dev/null
+++ b/configure
_AT_@ -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
new file mode 100644
index 0000000..04f398b
--- /dev/null
+++ b/posix.c
_AT_@ -0,0 +1,123 @@
+
+#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
+
+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
index 7df9cf2..ec2409d 100644
--- a/utmp.1
+++ b/utmp.1
_AT_@ -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
index 013f020..6ab5289 100644
--- a/utmp.c
+++ b/utmp.c
_AT_@ -1,5 +1,4 @@
-/* See LICENSE for license details. */
-#define _POSIX_C_SOURCE	200112L
+
 
 #include <errno.h>
 #include <ctype.h>
_AT_@ -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
_AT_@ -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);
_AT_@ -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));
_AT_@ -153,4 +70,3 @@ main(int argc, char *argv[])
 	}
 	return 0;
 }
-
-- 
1.8.3.3
Received on Wed Aug 14 2013 - 16:56:11 CEST

This archive was generated by hypermail 2.3.0 : Wed Aug 14 2013 - 17:00:10 CEST