summaryrefslogtreecommitdiffstats
path: root/login-utils/login.c
diff options
context:
space:
mode:
authorKarel Zak2007-03-08 21:57:48 +0100
committerKarel Zak2007-03-08 21:57:48 +0100
commitb2452358860526b454ba5f6c71183300223e0e78 (patch)
treeadba1e2a78f963af58cfc95ef897fb41eefe4f87 /login-utils/login.c
parenttodo: add item about ipcs (diff)
downloadkernel-qcow2-util-linux-b2452358860526b454ba5f6c71183300223e0e78.tar.gz
kernel-qcow2-util-linux-b2452358860526b454ba5f6c71183300223e0e78.tar.xz
kernel-qcow2-util-linux-b2452358860526b454ba5f6c71183300223e0e78.zip
login: improve work with signals
The login cannot ignore signals, because: * SIGHUP is only way how inform session leader that controlling tty goes away. The leader has to inform others processes in same process group about the signal. * SIGHUP/SIGTERM cannot kill wait(2)-ing login, we have to wait as long as any child process exists. The PAM session has to be closed correctly. * The child process (before setsid()) has to call exit() if a controlling tty goes away. This patch is inspired by patch from Red Hat that is very well tested for last 4 years in all Red Hat distros. Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'login-utils/login.c')
-rw-r--r--login-utils/login.c81
1 files changed, 69 insertions, 12 deletions
diff --git a/login-utils/login.c b/login-utils/login.c
index a9a06b1e1..5895b8efa 100644
--- a/login-utils/login.c
+++ b/login-utils/login.c
@@ -285,6 +285,30 @@ logbtmp(const char *line, const char *username, const char *hostname) {
updwtmp(_PATH_BTMP, &ut);
#endif
}
+
+
+static int child_pid = 0;
+static volatile int got_sig = 0;
+
+/*
+ * This handler allows to inform a shell about signals to login. If you have
+ * (root) permissions you can kill all login childrent by one signal to login
+ * process.
+ *
+ * Also, parent who is session leader is able (before setsid() in child) to
+ * inform child when controlling tty goes away (e.g. modem hangup, SIGHUP).
+ */
+static void
+sig_handler(int signal)
+{
+ if(child_pid)
+ kill(-child_pid, signal);
+ else
+ got_sig = 1;
+ if(signal == SIGTERM)
+ kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */
+}
+
#endif /* HAVE_SECURITY_PAM_MISC_H */
int
@@ -307,7 +331,7 @@ main(int argc, char **argv)
int retcode;
pam_handle_t *pamh = NULL;
struct pam_conv conv = { misc_conv, NULL };
- pid_t childPid;
+ struct sigaction sa, oldsa_hup, oldsa_term;
#else
char *salt, *pp;
#endif
@@ -1021,13 +1045,37 @@ Michael Riepe <michael@stud.uni-hannover.de>
signal(SIGTSTP, SIG_IGN);
#ifdef HAVE_SECURITY_PAM_MISC_H
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGINT, &sa, NULL);
+
+ sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */
+
+ /*
+ * detach the controlling tty
+ * -- we needn't the tty in parent who waits for child only.
+ * The child calls setsid() that detach from the tty as well.
+ */
+ ioctl(0, TIOCNOTTY, NULL);
+
+ /*
+ * We have care about SIGTERM, because leave PAM session without
+ * pam_close_session() is pretty bad thing.
+ */
+ sa.sa_handler = sig_handler;
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGTERM, &sa, &oldsa_term);
+
+ closelog();
+
/*
* We must fork before setuid() because we need to call
* pam_close_session() as root.
*/
- childPid = fork();
- if (childPid < 0) {
+ child_pid = fork();
+ if (child_pid < 0) {
int errsv = errno;
/* error in fork() */
fprintf(stderr, _("login: failure forking: %s"), strerror(errsv));
@@ -1035,21 +1083,30 @@ Michael Riepe <michael@stud.uni-hannover.de>
exit(0);
}
- if (childPid) {
+ if (child_pid) {
/* parent - wait for child to finish, then cleanup session */
- signal(SIGHUP, SIG_IGN);
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- signal(SIGTSTP, SIG_IGN);
- signal(SIGTTIN, SIG_IGN);
- signal(SIGTTOU, SIG_IGN);
-
- wait(NULL);
+ close(0);
+ close(1);
+ close(2);
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ /* wait as long as any child is there */
+ while(wait(NULL) == -1 && errno == EINTR)
+ ;
PAM_END;
exit(0);
}
/* child */
+
+ /* restore to old state */
+ sigaction(SIGHUP, &oldsa_hup, NULL);
+ sigaction(SIGTERM, &oldsa_term, NULL);
+ if(got_sig)
+ exit(1);
+
/*
* Problem: if the user's shell is a shell like ash that doesnt do
* setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every