cdown / rpms / util-linux

Forked from rpms/util-linux 2 years ago
Clone
05ad79
From f720781671c9f5421fb8101dd3bcf7b56efca131 Mon Sep 17 00:00:00 2001
05ad79
From: Karel Zak <kzak@redhat.com>
05ad79
Date: Mon, 20 Aug 2018 14:56:08 +0200
05ad79
Subject: [PATCH 178/178] sulogin: backport RHEL-8 version
05ad79
05ad79
The new version is more robust and it does not use mmap()-ed shared
05ad79
memory between sulogins instances. It also provides some fallbacks for
05ad79
s390 machines.
05ad79
05ad79
Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1616264
05ad79
Signed-off-by: Karel Zak <kzak@redhat.com>
05ad79
---
05ad79
 configure.ac                   |   1 +
05ad79
 include/c.h                    |   4 +
05ad79
 login-utils/sulogin-consoles.c | 116 +++++++++++++------
05ad79
 login-utils/sulogin.c          | 253 +++++++++++++++++++++++++++--------------
05ad79
 4 files changed, 252 insertions(+), 122 deletions(-)
05ad79
05ad79
diff --git a/configure.ac b/configure.ac
05ad79
index d561e01d0..3f07df495 100644
05ad79
--- a/configure.ac
05ad79
+++ b/configure.ac
05ad79
@@ -218,6 +218,7 @@ AC_CHECK_HEADERS([ \
05ad79
 	sys/prctl.h \
05ad79
 	sys/queue.h \
05ad79
 	sys/resource.h \
05ad79
+	sys/sysmacros.h \
05ad79
 	sys/socket.h \
05ad79
 	sys/sockio.h \
05ad79
 	sys/stat.h \
05ad79
diff --git a/include/c.h b/include/c.h
05ad79
index 124035ea5..51d439297 100644
05ad79
--- a/include/c.h
05ad79
+++ b/include/c.h
05ad79
@@ -19,6 +19,10 @@
05ad79
 # include <err.h>
05ad79
 #endif
05ad79
 
05ad79
+#ifdef HAVE_SYS_SYSMACROS_H
05ad79
+# include <sys/sysmacros.h>     /* for major, minor */
05ad79
+#endif
05ad79
+
05ad79
 #ifndef HAVE_USLEEP
05ad79
 # include <time.h>
05ad79
 #endif
05ad79
diff --git a/login-utils/sulogin-consoles.c b/login-utils/sulogin-consoles.c
05ad79
index 07af33a6d..2c0eed3a4 100644
05ad79
--- a/login-utils/sulogin-consoles.c
05ad79
+++ b/login-utils/sulogin-consoles.c
05ad79
@@ -36,8 +36,9 @@
05ad79
 # include <linux/serial.h>
05ad79
 # include <linux/major.h>
05ad79
 #endif
05ad79
-#include <fcntl.h>
05ad79
 #include <dirent.h>
05ad79
+#include <errno.h>
05ad79
+#include <fcntl.h>
05ad79
 #include <unistd.h>
05ad79
 
05ad79
 #ifdef USE_SULOGIN_EMERGENCY_MOUNT
05ad79
@@ -74,7 +75,7 @@ static int consoles_debug;
05ad79
 		} while (0)
05ad79
 
05ad79
 static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
05ad79
-dbgprint(const char *mesg, ...)
05ad79
+dbgprint(const char * const mesg, ...)
05ad79
 {
05ad79
 	va_list ap;
05ad79
 	va_start(ap, mesg);
05ad79
@@ -112,7 +113,7 @@ void emergency_do_mounts(void)
05ad79
 	}
05ad79
 
05ad79
 	if (stat("/", &rt) != 0) {
05ad79
-		warn("can not get file status of root file system\n");
05ad79
+		warn("cannot get file status of root file system\n");
05ad79
 		return;
05ad79
 	}
05ad79
 
05ad79
@@ -150,17 +151,19 @@ void emergency_do_mounts(void) { }
05ad79
  * the caller has to free the result
05ad79
  */
05ad79
 static __attribute__((__nonnull__))
05ad79
-char *oneline(const char *file)
05ad79
+char *oneline(const char * const file)
05ad79
 {
05ad79
 	FILE *fp;
05ad79
 	char *ret = NULL;
05ad79
-	size_t len = 0;
05ad79
+	size_t dummy = 0;
05ad79
+	ssize_t len;
05ad79
 
05ad79
 	DBG(dbgprint("reading %s", file));
05ad79
 
05ad79
-	if (!(fp = fopen(file, "re")))
05ad79
+	if (!(fp = fopen(file, "r" UL_CLOEXECSTR)))
05ad79
 		return NULL;
05ad79
-	if (getline(&ret, &len, fp) >= 0) {
05ad79
+	len = getline(&ret, &dummy, fp);
05ad79
+	if (len >= 0) {
05ad79
 		char *nl;
05ad79
 
05ad79
 		if (len)
05ad79
@@ -179,7 +182,7 @@ char *oneline(const char *file)
05ad79
  *  /sys/class/tty, the caller has to free the result.
05ad79
  */
05ad79
 static __attribute__((__malloc__))
05ad79
-char *actattr(const char *tty)
05ad79
+char *actattr(const char * const tty)
05ad79
 {
05ad79
 	char *ret, *path;
05ad79
 
05ad79
@@ -198,7 +201,7 @@ char *actattr(const char *tty)
05ad79
  * /sys/class/tty.
05ad79
  */
05ad79
 static
05ad79
-dev_t devattr(const char *tty)
05ad79
+dev_t devattr(const char * const tty)
05ad79
 {
05ad79
 	dev_t dev = 0;
05ad79
 	char *path, *value;
05ad79
@@ -223,42 +226,77 @@ dev_t devattr(const char *tty)
05ad79
 #endif /* __linux__ */
05ad79
 
05ad79
 /*
05ad79
- * Search below /dev for the characer device in `dev_t comparedev' variable.
05ad79
+ * Search below /dev for the character device in `dev_t comparedev' variable.
05ad79
+ * Note that realpath(3) is used here to avoid not existent devices due the
05ad79
+ * strdup(3) used in our canonicalize_path()!
05ad79
  */
05ad79
 static
05ad79
 #ifdef __GNUC__
05ad79
 __attribute__((__nonnull__,__malloc__,__hot__))
05ad79
 #endif
05ad79
-char* scandev(DIR *dir, dev_t comparedev)
05ad79
+char* scandev(DIR *dir, const dev_t comparedev)
05ad79
 {
05ad79
+	char path[PATH_MAX];
05ad79
 	char *name = NULL;
05ad79
-	struct dirent *dent;
05ad79
-	int fd;
05ad79
+	const struct dirent *dent;
05ad79
+	int len, fd;
05ad79
 
05ad79
 	DBG(dbgprint("scanning /dev for %u:%u", major(comparedev), minor(comparedev)));
05ad79
 
05ad79
+	/*
05ad79
+	 * Try udev links on character devices first.
05ad79
+	 */
05ad79
+	if ((len = snprintf(path, sizeof(path),
05ad79
+			    "/dev/char/%u:%u", major(comparedev), minor(comparedev))) > 0 &&
05ad79
+	    (size_t)len < sizeof(path)) {
05ad79
+
05ad79
+	    name = realpath(path, NULL);
05ad79
+	    if (name)
05ad79
+		    goto out;
05ad79
+	}
05ad79
+
05ad79
 	fd = dirfd(dir);
05ad79
 	rewinddir(dir);
05ad79
 	while ((dent = readdir(dir))) {
05ad79
-		char path[PATH_MAX];
05ad79
 		struct stat st;
05ad79
+
05ad79
+#ifdef _DIRENT_HAVE_D_TYPE
05ad79
+		if (dent->d_type != DT_UNKNOWN && dent->d_type != DT_CHR)
05ad79
+			continue;
05ad79
+#endif
05ad79
 		if (fstatat(fd, dent->d_name, &st, 0) < 0)
05ad79
 			continue;
05ad79
 		if (!S_ISCHR(st.st_mode))
05ad79
 			continue;
05ad79
 		if (comparedev != st.st_rdev)
05ad79
 			continue;
05ad79
-		if ((size_t)snprintf(path, sizeof(path), "/dev/%s", dent->d_name) >= sizeof(path))
05ad79
+		if ((len = snprintf(path, sizeof(path), "/dev/%s", dent->d_name)) < 0 ||
05ad79
+		    (size_t)len >= sizeof(path))
05ad79
 			continue;
05ad79
-#ifdef USE_SULOGIN_EMERGENCY_MOUNT
05ad79
-		if (emergency_flags & MNT_DEVTMPFS)
05ad79
-			mknod(path, S_IFCHR|S_IRUSR|S_IWUSR, comparedev);
05ad79
-#endif
05ad79
 
05ad79
-		name = canonicalize_path(path);
05ad79
-		break;
05ad79
+		name = realpath(path, NULL);
05ad79
+		if (name)
05ad79
+			goto out;
05ad79
 	}
05ad79
 
05ad79
+#ifdef USE_SULOGIN_EMERGENCY_MOUNT
05ad79
+	/*
05ad79
+	 * There was no /dev mounted hence and no device was found hence we create our own.
05ad79
+	 */
05ad79
+	if (!name && (emergency_flags & MNT_DEVTMPFS)) {
05ad79
+
05ad79
+		if ((len = snprintf(path, sizeof(path),
05ad79
+				    "/dev/tmp-%u:%u", major(comparedev), minor(comparedev))) < 0 ||
05ad79
+		    (size_t)len >= sizeof(path))
05ad79
+			goto out;
05ad79
+
05ad79
+		if (mknod(path, S_IFCHR|S_IRUSR|S_IWUSR, comparedev) < 0 && errno != EEXIST)
05ad79
+			goto out;
05ad79
+
05ad79
+		name = realpath(path, NULL);
05ad79
+	}
05ad79
+#endif
05ad79
+out:
05ad79
 	return name;
05ad79
 }
05ad79
 
05ad79
@@ -273,12 +311,12 @@ char* scandev(DIR *dir, dev_t comparedev)
05ad79
  */
05ad79
 static
05ad79
 #ifdef __GNUC__
05ad79
-__attribute__((__nonnull__,__hot__))
05ad79
+__attribute__((__hot__))
05ad79
 #endif
05ad79
-int append_console(struct list_head *consoles, const char *name)
05ad79
+int append_console(struct list_head *consoles, const char * const name)
05ad79
 {
05ad79
 	struct console *restrict tail;
05ad79
-	struct console *last = NULL;
05ad79
+	const struct console *last = NULL;
05ad79
 
05ad79
 	DBG(dbgprint("appenging %s", name));
05ad79
 
05ad79
@@ -300,7 +338,7 @@ int append_console(struct list_head *consoles, const char *name)
05ad79
 	tail->flags = 0;
05ad79
 	tail->fd = -1;
05ad79
 	tail->id = last ? last->id + 1 : 0;
05ad79
-	tail->pid = 0;
05ad79
+	tail->pid = -1;
05ad79
 	memset(&tail->tio, 0, sizeof(tail->tio));
05ad79
 
05ad79
 	return 0;
05ad79
@@ -319,11 +357,11 @@ static int detect_consoles_from_proc(struct list_head *consoles)
05ad79
 	char fbuf[16 + 1];
05ad79
 	DIR *dir = NULL;
05ad79
 	FILE *fc = NULL;
05ad79
-	int maj, min, rc = 1;
05ad79
+	int maj, min, rc = 1, matches;
05ad79
 
05ad79
 	DBG(dbgprint("trying /proc"));
05ad79
 
05ad79
-	fc = fopen("/proc/consoles", "re");
05ad79
+	fc = fopen("/proc/consoles", "r" UL_CLOEXECSTR);
05ad79
 	if (!fc) {
05ad79
 		rc = 2;
05ad79
 		goto done;
05ad79
@@ -332,10 +370,12 @@ static int detect_consoles_from_proc(struct list_head *consoles)
05ad79
 	if (!dir)
05ad79
 		goto done;
05ad79
 
05ad79
-	while (fscanf(fc, "%*s %*s (%16[^)]) %d:%d", fbuf, &maj, &min) == 3) {
05ad79
+	while ((matches = fscanf(fc, "%*s %*s (%16[^)]) %d:%d", fbuf, &maj, &min)) >= 1) {
05ad79
 		char *name;
05ad79
 		dev_t comparedev;
05ad79
 
05ad79
+		if (matches != 3)
05ad79
+			continue;
05ad79
 		if (!strchr(fbuf, 'E'))
05ad79
 			continue;
05ad79
 		comparedev = makedev(maj, min);
05ad79
@@ -509,7 +549,7 @@ done:
05ad79
 
05ad79
 #ifdef TIOCGDEV
05ad79
 static int detect_consoles_from_tiocgdev(struct list_head *consoles,
05ad79
-					int fallback,
05ad79
+					const int fallback,
05ad79
 					const char *device)
05ad79
 {
05ad79
 	unsigned int devnum;
05ad79
@@ -579,7 +619,7 @@ done:
05ad79
  * Returns 1 if stdout and stderr should be reconnected and 0
05ad79
  * otherwise or less than zero on error.
05ad79
  */
05ad79
-int detect_consoles(const char *device, int fallback, struct list_head *consoles)
05ad79
+int detect_consoles(const char *device, const int fallback, struct list_head *consoles)
05ad79
 {
05ad79
 	int fd, reconnect = 0, rc;
05ad79
 	dev_t comparedev = 0;
05ad79
@@ -587,7 +627,7 @@ int detect_consoles(const char *device, int fallback, struct list_head *consoles
05ad79
 	consoles_debug = getenv("CONSOLES_DEBUG") ? 1 : 0;
05ad79
 
05ad79
 	if (!device || !*device)
05ad79
-		fd = dup(fallback);
05ad79
+		fd = fallback >= 0 ? dup(fallback) : - 1;
05ad79
 	else {
05ad79
 		fd = open(device, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
05ad79
 		reconnect = 1;
05ad79
@@ -602,6 +642,14 @@ int detect_consoles(const char *device, int fallback, struct list_head *consoles
05ad79
 		struct stat st;
05ad79
 #ifdef TIOCGDEV
05ad79
 		unsigned int devnum;
05ad79
+#endif
05ad79
+#ifdef __GNU__
05ad79
+		/*
05ad79
+		 * The Hurd always gives st_rdev as 0, which causes this
05ad79
+		 * method to select the first terminal it finds.
05ad79
+		 */
05ad79
+		close(fd);
05ad79
+		goto fallback;
05ad79
 #endif
05ad79
 		DBG(dbgprint("trying device/fallback file descriptor"));
05ad79
 
05ad79
@@ -670,7 +718,7 @@ int detect_consoles(const char *device, int fallback, struct list_head *consoles
05ad79
 #ifdef __linux__
05ad79
 console:
05ad79
 	/*
05ad79
-	 * Detection of devices used for Linux system consolei using
05ad79
+	 * Detection of devices used for Linux system console using
05ad79
 	 * the /proc/consoles API with kernel 2.6.38 and higher.
05ad79
 	 */
05ad79
 	rc = detect_consoles_from_proc(consoles);
05ad79
@@ -754,8 +802,7 @@ int main(int argc, char *argv[])
05ad79
 {
05ad79
 	char *name = NULL;
05ad79
 	int fd, re;
05ad79
-	LIST_HEAD(consoles);
05ad79
-	struct list_head *p;
05ad79
+	struct list_head *p, consoles;
05ad79
 
05ad79
 	if (argc == 2) {
05ad79
 		name = argv[1];
05ad79
@@ -768,6 +815,7 @@ int main(int argc, char *argv[])
05ad79
 	if (!name)
05ad79
 		errx(EXIT_FAILURE, "usage: %s [<tty>]\n", program_invocation_short_name);
05ad79
 
05ad79
+	INIT_LIST_HEAD(&consoles);
05ad79
 	re = detect_consoles(name, fd, &consoles);
05ad79
 
05ad79
 	list_for_each(p, &consoles) {
05ad79
diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
05ad79
index dd73a1c50..4620ed2da 100644
05ad79
--- a/login-utils/sulogin.c
05ad79
+++ b/login-utils/sulogin.c
05ad79
@@ -56,8 +56,12 @@
05ad79
 
05ad79
 #include "c.h"
05ad79
 #include "closestream.h"
05ad79
+#include "env.h"
05ad79
 #include "nls.h"
05ad79
 #include "pathnames.h"
05ad79
+#ifdef USE_PLYMOUTH_SUPPORT
05ad79
+# include "plymouth-ctrl.h"
05ad79
+#endif
05ad79
 #include "strutils.h"
05ad79
 #include "ttyutils.h"
05ad79
 #include "sulogin-consoles.h"
05ad79
@@ -66,13 +70,12 @@
05ad79
 static unsigned int timeout;
05ad79
 static int profile;
05ad79
 static volatile uint32_t openfd;		/* Remember higher file descriptors */
05ad79
-static volatile uint32_t *usemask;
05ad79
 
05ad79
-struct sigaction saved_sigint;
05ad79
-struct sigaction saved_sigtstp;
05ad79
-struct sigaction saved_sigquit;
05ad79
-struct sigaction saved_sighup;
05ad79
-struct sigaction saved_sigchld;
05ad79
+static struct sigaction saved_sigint;
05ad79
+static struct sigaction saved_sigtstp;
05ad79
+static struct sigaction saved_sigquit;
05ad79
+static struct sigaction saved_sighup;
05ad79
+static struct sigaction saved_sigchld;
05ad79
 
05ad79
 static volatile sig_atomic_t alarm_rised;
05ad79
 static volatile sig_atomic_t sigchild;
05ad79
@@ -81,7 +84,12 @@ static volatile sig_atomic_t sigchild;
05ad79
 # define IUCLC		0
05ad79
 #endif
05ad79
 
05ad79
-static int locked_account_password(const char *passwd)
05ad79
+#ifndef WEXITED
05ad79
+# warning "WEXITED is missing, sulogin may not work as expected"
05ad79
+# define WEXITED 0
05ad79
+#endif
05ad79
+
05ad79
+static int locked_account_password(const char * const passwd)
05ad79
 {
05ad79
 	if (passwd && (*passwd == '*' || *passwd == '!'))
05ad79
 		return 1;
05ad79
@@ -93,10 +101,29 @@ static int locked_account_password(const char *passwd)
05ad79
  */
05ad79
 static void tcinit(struct console *con)
05ad79
 {
05ad79
-	int mode = 0, flags = 0;
05ad79
+	int flags = 0, mode = 0;
05ad79
 	struct termios *tio = &con->tio;
05ad79
-	int fd = con->fd;
05ad79
-
05ad79
+	const int fd = con->fd;
05ad79
+#ifdef USE_PLYMOUTH_SUPPORT
05ad79
+	struct termios lock;
05ad79
+	int i = (plymouth_command(MAGIC_PING)) ? PLYMOUTH_TERMIOS_FLAGS_DELAY : 0;
05ad79
+	if (i)
05ad79
+		plymouth_command(MAGIC_QUIT);
05ad79
+	while (i-- > 0) {
05ad79
+		/*
05ad79
+		 * With plymouth the termios flags become changed after this
05ad79
+		 * function had changed the termios.
05ad79
+		 */
05ad79
+		memset(&lock, 0, sizeof(struct termios));
05ad79
+		if (ioctl(fd, TIOCGLCKTRMIOS, &lock) < 0)
05ad79
+			break;
05ad79
+		if (!lock.c_iflag && !lock.c_oflag && !lock.c_cflag && !lock.c_lflag)
05ad79
+			break;
05ad79
+		sleep(1);
05ad79
+	}
05ad79
+	memset(&lock, 0, sizeof(struct termios));
05ad79
+	ioctl(fd, TIOCSLCKTRMIOS, &lock);
05ad79
+#endif
05ad79
 	errno = 0;
05ad79
 
05ad79
 	if (tcgetattr(fd, tio) < 0) {
05ad79
@@ -189,20 +216,23 @@ setattr:
05ad79
  */
05ad79
 static void tcfinal(struct console *con)
05ad79
 {
05ad79
-	struct termios *tio;
05ad79
-	int fd;
05ad79
+	struct termios *tio = &con->tio;
05ad79
+	const int fd = con->fd;
05ad79
 
05ad79
 	if ((con->flags & CON_SERIAL) == 0) {
05ad79
-		setenv("TERM", "linux", 1);
05ad79
+		xsetenv("TERM", "linux", 1);
05ad79
 		return;
05ad79
 	}
05ad79
-	if (con->flags & CON_NOTTY)
05ad79
+	if (con->flags & CON_NOTTY) {
05ad79
+		xsetenv("TERM", "dumb", 1);
05ad79
 		return;
05ad79
+	}
05ad79
 
05ad79
-	setenv("TERM", "vt102", 1);
05ad79
-	tio = &con->tio;
05ad79
-	fd = con->fd;
05ad79
-
05ad79
+#if defined (__s390__) || defined (__s390x__)
05ad79
+	xsetenv("TERM", "dumb", 1);
05ad79
+#else
05ad79
+	xsetenv("TERM", "vt102", 1);
05ad79
+#endif
05ad79
 	tio->c_iflag |= (IXON | IXOFF);
05ad79
 	tio->c_lflag |= (ICANON | ISIG | ECHO|ECHOE|ECHOK|ECHOKE);
05ad79
 	tio->c_oflag |= OPOST;
05ad79
@@ -237,11 +267,11 @@ static void tcfinal(struct console *con)
05ad79
 		break;
05ad79
 	case 1:				/* odd parity */
05ad79
 		tio->c_cflag |= PARODD;
05ad79
-		/* fall through */
05ad79
+		/* fallthrough */
05ad79
 	case 2:				/* even parity */
05ad79
 		tio->c_cflag |= PARENB;
05ad79
 		tio->c_iflag |= (INPCK | ISTRIP);
05ad79
-		/* fall through */
05ad79
+		/* fallthrough */
05ad79
 	case (1 | 2):			/* no parity bit */
05ad79
 		tio->c_cflag &= ~CSIZE;
05ad79
 		tio->c_cflag |= CS7;
05ad79
@@ -466,7 +496,7 @@ static struct passwd *getrootpwent(int try_manually)
05ad79
 		warnx(_("%s: no entry for root"), _PATH_SHADOW_PASSWD);
05ad79
 		*pwd.pw_passwd = '\0';
05ad79
 	}
05ad79
-	/* locked accont passwords are valid too */
05ad79
+	/* locked account passwords are valid too */
05ad79
 	if (!locked_account_password(pwd.pw_passwd) && !valid(pwd.pw_passwd)) {
05ad79
 		warnx(_("%s: root password garbled"), _PATH_SHADOW_PASSWD);
05ad79
 		*pwd.pw_passwd = '\0';
05ad79
@@ -524,22 +554,17 @@ err:
05ad79
  */
05ad79
 static void setup(struct console *con)
05ad79
 {
05ad79
-	pid_t pid, pgrp, ppgrp, ttypgrp;
05ad79
-	int fd;
05ad79
+	int fd = con->fd;
05ad79
+	const pid_t pid = getpid(), pgrp = getpgid(0), ppgrp =
05ad79
+	    getpgid(getppid()), ttypgrp = tcgetpgrp(fd);
05ad79
 
05ad79
 	if (con->flags & CON_NOTTY)
05ad79
 		return;
05ad79
-	fd = con->fd;
05ad79
 
05ad79
 	/*
05ad79
 	 * Only go through this trouble if the new
05ad79
 	 * tty doesn't fall in this process group.
05ad79
 	 */
05ad79
-	pid = getpid();
05ad79
-	pgrp = getpgid(0);
05ad79
-	ppgrp = getpgid(getppid());
05ad79
-	ttypgrp = tcgetpgrp(fd);
05ad79
-
05ad79
 	if (pgrp != ttypgrp && ppgrp != ttypgrp) {
05ad79
 		if (pid != getsid(0)) {
05ad79
 			if (pid == getpgid(0))
05ad79
@@ -575,21 +600,20 @@ static void setup(struct console *con)
05ad79
  * Ask for the password. Note that there is no default timeout as we normally
05ad79
  * skip this during boot.
05ad79
  */
05ad79
-static char *getpasswd(struct console *con)
05ad79
+static const char *getpasswd(struct console *con)
05ad79
 {
05ad79
 	struct sigaction sa;
05ad79
 	struct termios tty;
05ad79
 	static char pass[128], *ptr;
05ad79
 	struct chardata *cp;
05ad79
-	char *ret = pass;
05ad79
+	const char *ret = pass;
05ad79
 	unsigned char tc;
05ad79
 	char c, ascval;
05ad79
 	int eightbit;
05ad79
-	int fd;
05ad79
+	const int fd = con->fd;
05ad79
 
05ad79
 	if (con->flags & CON_NOTTY)
05ad79
 		goto out;
05ad79
-	fd = con->fd;
05ad79
 	cp = &con->cp;
05ad79
 	tty = con->tio;
05ad79
 
05ad79
@@ -597,6 +621,7 @@ static char *getpasswd(struct console *con)
05ad79
 	tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ISIG);
05ad79
 	tc = (tcsetattr(fd, TCSAFLUSH, &tty) == 0);
05ad79
 
05ad79
+	sigemptyset(&sa.sa_mask);
05ad79
 	sa.sa_handler = alrm_handler;
05ad79
 	sa.sa_flags = 0;
05ad79
 	sigaction(SIGALRM, &sa, NULL);
05ad79
@@ -615,7 +640,7 @@ static char *getpasswd(struct console *con)
05ad79
 					ret = NULL;
05ad79
 					goto quit;
05ad79
 				}
05ad79
-				usleep(1000);
05ad79
+				usleep(250000);
05ad79
 				continue;
05ad79
 			}
05ad79
 			ret = (char*)0;
05ad79
@@ -627,7 +652,7 @@ static char *getpasswd(struct console *con)
05ad79
 			case ENOENT:
05ad79
 				break;
05ad79
 			default:
05ad79
-				warn(_("%s: read failed"), con->tty);
05ad79
+				warn(_("cannot read %s"), con->tty);
05ad79
 				break;
05ad79
 			}
05ad79
 			goto quit;
05ad79
@@ -694,8 +719,8 @@ static void sushell(struct passwd *pwd)
05ad79
 {
05ad79
 	char shell[PATH_MAX];
05ad79
 	char home[PATH_MAX];
05ad79
-	char *p;
05ad79
-	char *su_shell;
05ad79
+	char const *p;
05ad79
+	char const *su_shell;
05ad79
 
05ad79
 	/*
05ad79
 	 * Set directory and shell.
05ad79
@@ -731,16 +756,16 @@ static void sushell(struct passwd *pwd)
05ad79
 	if (getcwd(home, sizeof(home)) == NULL)
05ad79
 		strcpy(home, "/");
05ad79
 
05ad79
-	setenv("HOME", home, 1);
05ad79
-	setenv("LOGNAME", "root", 1);
05ad79
-	setenv("USER", "root", 1);
05ad79
+	xsetenv("HOME", home, 1);
05ad79
+	xsetenv("LOGNAME", "root", 1);
05ad79
+	xsetenv("USER", "root", 1);
05ad79
 	if (!profile)
05ad79
-		setenv("SHLVL","0",1);
05ad79
+		xsetenv("SHLVL","0",1);
05ad79
 
05ad79
 	/*
05ad79
 	 * Try to execute a shell.
05ad79
 	 */
05ad79
-	setenv("SHELL", su_shell, 1);
05ad79
+	xsetenv("SHELL", su_shell, 1);
05ad79
 	unmask_signal(SIGINT, &saved_sigint);
05ad79
 	unmask_signal(SIGTSTP, &saved_sigtstp);
05ad79
 	unmask_signal(SIGQUIT, &saved_sigquit);
05ad79
@@ -765,17 +790,21 @@ static void sushell(struct passwd *pwd)
05ad79
 	execl(su_shell, shell, NULL);
05ad79
 	warn(_("failed to execute %s"), su_shell);
05ad79
 
05ad79
-	setenv("SHELL", "/bin/sh", 1);
05ad79
+	xsetenv("SHELL", "/bin/sh", 1);
05ad79
 	execl("/bin/sh", profile ? "-sh" : "sh", NULL);
05ad79
 	warn(_("failed to execute %s"), "/bin/sh");
05ad79
 }
05ad79
 
05ad79
-static void usage(FILE *out)
05ad79
+static void usage(void)
05ad79
 {
05ad79
+	FILE *out = stdout;
05ad79
 	fputs(USAGE_HEADER, out);
05ad79
 	fprintf(out, _(
05ad79
 		" %s [options] [tty device]\n"), program_invocation_short_name);
05ad79
 
05ad79
+	fputs(USAGE_SEPARATOR, out);
05ad79
+	fputs(_("Single-user login.\n"), out);
05ad79
+
05ad79
 	fputs(USAGE_OPTIONS, out);
05ad79
 	fputs(_(" -p, --login-shell        start a login shell\n"
05ad79
 		" -t, --timeout <seconds>  max time to wait for a password (default: no limit)\n"
05ad79
@@ -783,32 +812,35 @@ static void usage(FILE *out)
05ad79
 		out);
05ad79
 
05ad79
 	fputs(USAGE_SEPARATOR, out);
05ad79
-	fputs(USAGE_HELP, out);
05ad79
-	fputs(USAGE_VERSION, out);
05ad79
-	fprintf(out, USAGE_MAN_TAIL("sulogin(8)"));
05ad79
+	printf(USAGE_HELP_OPTIONS(26));
05ad79
+	printf(USAGE_MAN_TAIL("sulogin(8)"));
05ad79
 }
05ad79
 
05ad79
 int main(int argc, char **argv)
05ad79
 {
05ad79
-	LIST_HEAD(consoles);
05ad79
-	struct list_head *ptr;
05ad79
+	struct list_head *ptr, consoles;
05ad79
 	struct console *con;
05ad79
 	char *tty = NULL;
05ad79
 	struct passwd *pwd;
05ad79
-	int c, status = 0;
05ad79
-	int reconnect = 0;
05ad79
+	const struct timespec sigwait = { .tv_sec = 0, .tv_nsec = 50000000 };
05ad79
+	siginfo_t status = { 0 };
05ad79
+	sigset_t set;
05ad79
+	int c, reconnect = 0;
05ad79
 	int opt_e = 0;
05ad79
+	int wait = 0;
05ad79
 	pid_t pid;
05ad79
 
05ad79
 	static const struct option longopts[] = {
05ad79
-		{ "login-shell",  0, 0, 'p' },
05ad79
-		{ "timeout",      1, 0, 't' },
05ad79
-		{ "force",        0, 0, 'e' },
05ad79
-		{ "help",         0, 0, 'h' },
05ad79
-		{ "version",      0, 0, 'V' },
05ad79
-		{ NULL,           0, 0, 0 }
05ad79
+		{ "login-shell",  no_argument,       NULL, 'p' },
05ad79
+		{ "timeout",      required_argument, NULL, 't' },
05ad79
+		{ "force",        no_argument,       NULL, 'e' },
05ad79
+		{ "help",         no_argument,       NULL, 'h' },
05ad79
+		{ "version",      no_argument,       NULL, 'V' },
05ad79
+		{ NULL, 0, NULL, 0 }
05ad79
 	};
05ad79
 
05ad79
+	INIT_LIST_HEAD(&consoles);
05ad79
+
05ad79
 	/*
05ad79
 	 * If we are init we need to set up a own session.
05ad79
 	 */
05ad79
@@ -840,17 +872,16 @@ int main(int argc, char **argv)
05ad79
 			printf(UTIL_LINUX_VERSION);
05ad79
 			return EXIT_SUCCESS;
05ad79
 		case 'h':
05ad79
-			usage(stdout);
05ad79
+			usage();
05ad79
 			return EXIT_SUCCESS;
05ad79
 		default:
05ad79
-			usage(stderr);
05ad79
-			/* Do not exit! */
05ad79
+			/* Do not exit! getopt prints a warning. */
05ad79
 			break;
05ad79
 		}
05ad79
 	}
05ad79
 
05ad79
 	if (geteuid() != 0)
05ad79
-		errx(EXIT_FAILURE, _("only root can run this program."));
05ad79
+		errx(EXIT_FAILURE, _("only superuser can run this program"));
05ad79
 
05ad79
 	mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit);
05ad79
 	mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp);
05ad79
@@ -877,7 +908,7 @@ int main(int argc, char **argv)
05ad79
 	reconnect = detect_consoles(tty, STDIN_FILENO, &consoles);
05ad79
 
05ad79
 	/*
05ad79
-	 * If previous stdin was not the speified tty and therefore reconnected
05ad79
+	 * If previous stdin was not the specified tty and therefore reconnected
05ad79
 	 * to the specified tty also reconnect stdout and stderr.
05ad79
 	 */
05ad79
 	if (reconnect) {
05ad79
@@ -900,7 +931,7 @@ int main(int argc, char **argv)
05ad79
 	 * Get the root password.
05ad79
 	 */
05ad79
 	if ((pwd = getrootpwent(opt_e)) == NULL) {
05ad79
-		warnx(_("cannot open password database."));
05ad79
+		warnx(_("cannot open password database"));
05ad79
 		sleep(2);
05ad79
 		return EXIT_FAILURE;
05ad79
 	}
05ad79
@@ -923,9 +954,6 @@ int main(int argc, char **argv)
05ad79
 		tcinit(con);
05ad79
 	}
05ad79
 	ptr = (&consoles)->next;
05ad79
-	usemask = (uint32_t*) mmap(NULL, sizeof(uint32_t),
05ad79
-					PROT_READ|PROT_WRITE,
05ad79
-					MAP_ANONYMOUS|MAP_SHARED, -1, 0);
05ad79
 
05ad79
 	if (ptr->next == &consoles) {
05ad79
 		con = list_entry(ptr, struct console, entry);
05ad79
@@ -942,7 +970,6 @@ int main(int argc, char **argv)
05ad79
 		switch ((con->pid = fork())) {
05ad79
 		case 0:
05ad79
 			mask_signal(SIGCHLD, SIG_DFL, NULL);
05ad79
-			/* fall through */
05ad79
 		nofork:
05ad79
 			setup(con);
05ad79
 			while (1) {
05ad79
@@ -971,9 +998,7 @@ int main(int argc, char **argv)
05ad79
 				}
05ad79
 
05ad79
 				if (doshell) {
05ad79
-					*usemask |= (1<<con->id);
05ad79
 					sushell(pwd);
05ad79
-					*usemask &= ~(1<<con->id);
05ad79
 					failed++;
05ad79
 				}
05ad79
 
05ad79
@@ -982,7 +1007,7 @@ int main(int argc, char **argv)
05ad79
 				mask_signal(SIGINT,  SIG_IGN, &saved_sigint);
05ad79
 
05ad79
 				if (failed) {
05ad79
-					fprintf(stderr, _("Can not execute su shell\n\n"));
05ad79
+					fprintf(stderr, _("cannot execute su shell\n\n"));
05ad79
 					break;
05ad79
 				}
05ad79
 				fprintf(stderr, _("Login incorrect\n\n"));
05ad79
@@ -997,7 +1022,7 @@ int main(int argc, char **argv)
05ad79
 			exit(0);
05ad79
 		case -1:
05ad79
 			warn(_("fork failed"));
05ad79
-			/* fall through */
05ad79
+			/* fallthrough */
05ad79
 		default:
05ad79
 			break;
05ad79
 		}
05ad79
@@ -1006,28 +1031,80 @@ int main(int argc, char **argv)
05ad79
 
05ad79
 	} while (ptr != &consoles);
05ad79
 
05ad79
-	while ((pid = wait(&status))) {
05ad79
-		if (errno == ECHILD)
05ad79
+	do {
05ad79
+		int ret;
05ad79
+
05ad79
+		status.si_pid = 0;
05ad79
+		ret = waitid(P_ALL, 0, &status, WEXITED);
05ad79
+
05ad79
+		if (ret == 0)
05ad79
 			break;
05ad79
-		if (pid < 0)
05ad79
-			continue;
05ad79
-		list_for_each(ptr, &consoles) {
05ad79
-			con = list_entry(ptr, struct console, entry);
05ad79
-			if (con->pid == pid) {
05ad79
-				*usemask &= ~(1<<con->id);
05ad79
+		if (ret < 0) {
05ad79
+			if (errno == ECHILD)
05ad79
+				break;
05ad79
+			if (errno == EINTR)
05ad79
 				continue;
05ad79
-			}
05ad79
-			if (kill(con->pid, 0) < 0) {
05ad79
-				*usemask &= ~(1<<con->id);
05ad79
+		}
05ad79
+
05ad79
+		errx(EXIT_FAILURE, _("cannot wait on su shell\n\n"));
05ad79
+
05ad79
+	} while (1);
05ad79
+
05ad79
+	list_for_each(ptr, &consoles) {
05ad79
+		con = list_entry(ptr, struct console, entry);
05ad79
+
05ad79
+		if (con->fd < 0)
05ad79
+			continue;
05ad79
+		if (con->pid < 0)
05ad79
+			continue;
05ad79
+		if (con->pid == status.si_pid)
05ad79
+			con->pid = -1;
05ad79
+		else {
05ad79
+			kill(con->pid, SIGTERM);
05ad79
+			wait++;
05ad79
+		}
05ad79
+	}
05ad79
+
05ad79
+	sigemptyset(&set);
05ad79
+	sigaddset(&set, SIGCHLD);
05ad79
+
05ad79
+	do {
05ad79
+		int signum, ret;
05ad79
+
05ad79
+		if (!wait)
05ad79
+			break;
05ad79
+
05ad79
+		status.si_pid = 0;
05ad79
+		ret = waitid(P_ALL, 0, &status, WEXITED|WNOHANG);
05ad79
+
05ad79
+		if (ret < 0) {
05ad79
+			if (errno == ECHILD)
05ad79
+				break;
05ad79
+			if (errno == EINTR)
05ad79
 				continue;
05ad79
+		}
05ad79
+
05ad79
+		if (!ret && status.si_pid > 0) {
05ad79
+			list_for_each(ptr, &consoles) {
05ad79
+				con = list_entry(ptr, struct console, entry);
05ad79
+
05ad79
+				if (con->fd < 0)
05ad79
+					continue;
05ad79
+				if (con->pid < 0)
05ad79
+					continue;
05ad79
+				if (con->pid == status.si_pid) {
05ad79
+					con->pid = -1;
05ad79
+					wait--;
05ad79
+				}
05ad79
 			}
05ad79
-			if (*usemask & (1<<con->id))
05ad79
-				continue;
05ad79
-			kill(con->pid, SIGHUP);
05ad79
-			usleep(5000);
05ad79
-			kill(con->pid, SIGKILL);
05ad79
+			continue;
05ad79
 		}
05ad79
-	}
05ad79
+
05ad79
+		signum = sigtimedwait(&set, NULL, &sigwait);
05ad79
+		if (signum != SIGCHLD && signum < 0 && errno == EAGAIN)
05ad79
+			break;
05ad79
+
05ad79
+	} while (1);
05ad79
 
05ad79
 	mask_signal(SIGCHLD, SIG_DFL, NULL);
05ad79
 	return EXIT_SUCCESS;
05ad79
-- 
05ad79
2.14.4
05ad79