commit 836feec54d58f145d34b14baee7cb0ff677cbd26
parent c282965520a0d8bcc2b8c2f010fd96ad063a25c3
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date:   Thu,  7 Aug 2014 14:52:19 +0200
Avoid race conditions between fork and wait
If some signal arrives to the parent between these two points
then it is possible to have dirty entries in utmp.
Diffstat:
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/utmp.c b/utmp.c
@@ -6,6 +6,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <signal.h>
 
 #include <sys/types.h>
 #include <unistd.h>
@@ -39,6 +40,7 @@ main(int argc, char *argv[])
 {
 	int status;
 	uid_t uid;
+	sigset_t set;
 	extern void addutmp(void), delutmp(void);
 
 	egid = getegid();
@@ -54,8 +56,12 @@ main(int argc, char *argv[])
 	setenv("SHELL", pass->pw_shell, 0);
 	setenv("HOME", pass->pw_dir, 0);
 
+	sigfillset(&set);
+	sigprocmask(SIG_BLOCK, &set, NULL);
+
 	switch (fork()) {
 	case 0:
+		sigprocmask(SIG_UNBLOCK, &set, NULL);
 		argv[0] = getenv("SHELL");
 		execv(argv[0], argv);
 		die("error executing shell:%s", strerror(errno));
@@ -63,10 +69,13 @@ main(int argc, char *argv[])
 		die("error spawning child:%s", strerror(errno));
 	default:
 		addutmp();
-		if (wait(&status) == -1) {
-			fprintf(stderr, "error waiting child:%s\n",
-				strerror(errno));
-		}
+		signal(SIGINT, SIG_IGN);
+		signal(SIGTERM, SIG_IGN);
+		signal(SIGHUP, SIG_IGN);
+		sigprocmask(SIG_UNBLOCK, &set, NULL);
+
+		if (wait(&status) == -1)
+			perror("error waiting child");
 		delutmp();
 	}
 	return 0;