diff options
author | Karel Zak | 2017-09-07 14:19:20 +0200 |
---|---|---|
committer | Karel Zak | 2017-09-18 11:49:11 +0200 |
commit | e9fde3e9004d1661b5229a476cf0cacfb8d0ef4f (patch) | |
tree | bdc015649838b25e4596cb4756c7b7721c589b0c | |
parent | su: (pty) save child status (diff) | |
download | kernel-qcow2-util-linux-e9fde3e9004d1661b5229a476cf0cacfb8d0ef4f.tar.gz kernel-qcow2-util-linux-e9fde3e9004d1661b5229a476cf0cacfb8d0ef4f.tar.xz kernel-qcow2-util-linux-e9fde3e9004d1661b5229a476cf0cacfb8d0ef4f.zip |
su: (pty) improve SIGSTOP/SIGCONT semantic
We want to use waitpid() only when child is terminated or stopped to
pick up child status, otherwise PTY proxy has to be active. This is
difference between "su" and "su --pty". For "su" we keep parent all
time in waitpid().
It would be possible to use separate code based on signalfd_siginfo,
but it seems better to keep all this stuff on one place -- it means
wait_for_child().
Signed-off-by: Karel Zak <kzak@redhat.com>
-rw-r--r-- | login-utils/su-common.c | 26 |
1 files changed, 24 insertions, 2 deletions
diff --git a/login-utils/su-common.c b/login-utils/su-common.c index 925cef983..b9deeeab1 100644 --- a/login-utils/su-common.c +++ b/login-utils/su-common.c @@ -196,14 +196,28 @@ static int wait_for_child(struct su_context *su) return su->childstatus; if (su->child != (pid_t) -1) { + /* + * The "su" parent process spends all time here in waitpid(), + * but "su --pty" uses pty_proxy_master() and waitpid() is only + * called to pick up child status or to react to SIGSTOP. + */ DBG(SIG, ul_debug("waiting for child [%d]...", su->child)); for (;;) { pid = waitpid(su->child, &status, WUNTRACED); if (pid != (pid_t) - 1 && WIFSTOPPED(status)) { + DBG(SIG, ul_debug(" child got SIGSTOP -- stop all session")); kill(getpid(), SIGSTOP); /* once we get here, we must have resumed */ kill(pid, SIGCONT); + DBG(SIG, ul_debug(" session resumed -- continue")); +#ifdef USE_PTY + /* Let's go back to pty_proxy_master() */ + if (su->pty_sigfd != -1) { + DBG(SIG, ul_debug(" leaving on child SIGSTOP")); + return 0; + } +#endif } else break; } @@ -421,8 +435,14 @@ static int pty_handle_signal(struct su_context *su, int fd) switch (info.ssi_signo) { case SIGCHLD: DBG(SIG, ul_debug(" get signal SIGCHLD")); - wait_for_child(su); - su->poll_timeout = 10; + + /* The child terminated or stopped. Note that we ignore SIGCONT + * here, because stop/cont semantic is handled by wait_for_child() */ + if (info.ssi_code == CLD_EXITED || info.ssi_status == SIGSTOP) + wait_for_child(su); + /* The child is dead, force poll() timeout. */ + if (su->child == (pid_t) -1) + su->poll_timeout = 10; return 0; case SIGWINCH: DBG(SIG, ul_debug(" get signal SIGWINCH")); @@ -555,6 +575,8 @@ static void pty_proxy_master(struct su_context *su) } } + close(su->pty_sigfd); + su->pty_sigfd = -1; DBG(PTY, ul_debug("poll() done [signal=%d, rc=%d]", caught_signal, rc)); } #endif /* USE_PTY */ |