diff options
author | Karel Zak | 2007-03-08 21:57:48 +0100 |
---|---|---|
committer | Karel Zak | 2007-03-08 21:57:48 +0100 |
commit | b2452358860526b454ba5f6c71183300223e0e78 (patch) | |
tree | adba1e2a78f963af58cfc95ef897fb41eefe4f87 /login-utils/login.c | |
parent | todo: add item about ipcs (diff) | |
download | kernel-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.c | 81 |
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 |