summaryrefslogtreecommitdiff
path: root/usr/src/cmd/su
diff options
context:
space:
mode:
authorMarek Pospisil <Marek.Pospisil@Sun.COM>2010-07-29 11:50:23 -0700
committerMarek Pospisil <Marek.Pospisil@Sun.COM>2010-07-29 11:50:23 -0700
commit06ccc4b827eae192ea88f88ab6dfc31a6f252ada (patch)
treef154f379042718f58c3e3f1353d9201398021e76 /usr/src/cmd/su
parentfc33347812f84907261f6fd501e2409da108b8d8 (diff)
downloadillumos-joyent-06ccc4b827eae192ea88f88ab6dfc31a6f252ada.tar.gz
6937808 su suspend hangs when auditing is enabled
Diffstat (limited to 'usr/src/cmd/su')
-rw-r--r--usr/src/cmd/su/su.c64
1 files changed, 58 insertions, 6 deletions
diff --git a/usr/src/cmd/su/su.c b/usr/src/cmd/su/su.c
index 13a8e7dd95..e878077368 100644
--- a/usr/src/cmd/su/su.c
+++ b/usr/src/cmd/su/su.c
@@ -19,10 +19,8 @@
* CDDL HEADER END
*/
/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
*/
-
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
@@ -828,7 +826,7 @@ audit_success(int pw_change, struct passwd *pwd)
* The preceeding code is a noop if audit isn't enabled,
* but, let's not make a new process when it's not necessary.
*/
- if (adt_audit_enabled()) {
+ if (adt_audit_state(AUC_AUDITING)) {
audit_logout(ah, event_id); /* fork to catch logout */
}
(void) adt_end_session(ah);
@@ -920,8 +918,62 @@ audit_logout(adt_session_data_t *ah, au_event_t event_id)
}
closefrom(0);
priv_freeset(priv);
- while (pid != waitpid(pid, &status, 0))
- continue;
+
+ for (;;) {
+ if (pid != waitpid(pid, &status, WUNTRACED)) {
+ if (errno == ECHILD) {
+ /*
+ * No existing child with the given pid. Lets
+ * audit the logout.
+ */
+ break;
+ }
+ continue;
+ }
+
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ /*
+ * The child shell exited or was terminated by
+ * a signal. Lets audit logout.
+ */
+ break;
+ } else if (WIFSTOPPED(status)) {
+ pid_t pgid;
+ int fd;
+ void (*sg_handler)();
+ /*
+ * The child shell has been stopped/suspended.
+ * We need to suspend here as well and pass down
+ * the control to the parent process.
+ */
+ sg_handler = signal(WSTOPSIG(status), SIG_DFL);
+ (void) sigsend(P_PGID, getpgrp(), WSTOPSIG(status));
+ /*
+ * We stop here. When resumed, mark the child
+ * shell group as foreground process group
+ * which gives the child shell a control over
+ * the controlling terminal.
+ */
+ (void) signal(WSTOPSIG(status), sg_handler);
+
+ pgid = getpgid(pid);
+ if ((fd = open("/dev/tty", O_RDWR)) != -1) {
+ /*
+ * Pass down the control over the controlling
+ * terminal iff we are in a foreground process
+ * group. Otherwise, we are in a background
+ * process group and the kernel will send
+ * SIGTTOU signal to stop us (by default).
+ */
+ if (tcgetpgrp(fd) == getpgrp()) {
+ (void) tcsetpgrp(fd, pgid);
+ }
+ (void) close(fd);
+ }
+ /* Wake up the child shell */
+ (void) sigsend(P_PGID, pgid, SIGCONT);
+ }
+ }
(void) adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS);
adt_free_event(event);