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