Restore default signal handling in child processes
In particular, this fixes Ctrl-C in nix-shell sessions.
This commit is contained in:
		
							parent
							
								
									583ff4ec46
								
							
						
					
					
						commit
						7a65b2470e
					
				
					 7 changed files with 33 additions and 35 deletions
				
			
		|  | @ -30,6 +30,7 @@ static std::pair<FdSink, FdSource> connect(const string & conn) | ||||||
|             throw SysError("dupping stdin"); |             throw SysError("dupping stdin"); | ||||||
|         if (dup2(from.writeSide, STDOUT_FILENO) == -1) |         if (dup2(from.writeSide, STDOUT_FILENO) == -1) | ||||||
|             throw SysError("dupping stdout"); |             throw SysError("dupping stdout"); | ||||||
|  |         restoreSignals(); | ||||||
|         execlp("ssh", "ssh", "-x", "-T", conn.c_str(), "nix-store --serve", NULL); |         execlp("ssh", "ssh", "-x", "-T", conn.c_str(), "nix-store --serve", NULL); | ||||||
|         throw SysError("executing ssh"); |         throw SysError("executing ssh"); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  | @ -119,15 +119,9 @@ void initNix() | ||||||
| 
 | 
 | ||||||
|     startSignalHandlerThread(); |     startSignalHandlerThread(); | ||||||
| 
 | 
 | ||||||
|     /* Ignore SIGPIPE. */ |     /* Reset SIGCHLD to its default. */ | ||||||
|     struct sigaction act; |     struct sigaction act; | ||||||
|     sigemptyset(&act.sa_mask); |     sigemptyset(&act.sa_mask); | ||||||
|     act.sa_handler = SIG_IGN; |  | ||||||
|     act.sa_flags = 0; |  | ||||||
|     if (sigaction(SIGPIPE, &act, 0)) |  | ||||||
|         throw SysError("ignoring SIGPIPE"); |  | ||||||
| 
 |  | ||||||
|     /* Reset SIGCHLD to its default. */ |  | ||||||
|     act.sa_handler = SIG_DFL; |     act.sa_handler = SIG_DFL; | ||||||
|     act.sa_flags = 0; |     act.sa_flags = 0; | ||||||
|     if (sigaction(SIGCHLD, &act, 0)) |     if (sigaction(SIGCHLD, &act, 0)) | ||||||
|  | @ -252,7 +246,7 @@ void printVersion(const string & programName) | ||||||
| 
 | 
 | ||||||
| void showManPage(const string & name) | void showManPage(const string & name) | ||||||
| { | { | ||||||
|     restoreSIGPIPE(); |     restoreSignals(); | ||||||
|     execlp("man", "man", name.c_str(), NULL); |     execlp("man", "man", name.c_str(), NULL); | ||||||
|     throw SysError(format("command ‘man %1%’ failed") % name.c_str()); |     throw SysError(format("command ‘man %1%’ failed") % name.c_str()); | ||||||
| } | } | ||||||
|  | @ -305,16 +299,6 @@ RunPager::RunPager() | ||||||
|     if (!pager) pager = getenv("PAGER"); |     if (!pager) pager = getenv("PAGER"); | ||||||
|     if (pager && ((string) pager == "" || (string) pager == "cat")) return; |     if (pager && ((string) pager == "" || (string) pager == "cat")) return; | ||||||
| 
 | 
 | ||||||
|     /* Ignore SIGINT. The pager will handle it (and we'll get
 |  | ||||||
|        SIGPIPE). */ |  | ||||||
|     struct sigaction act; |  | ||||||
|     act.sa_handler = SIG_IGN; |  | ||||||
|     act.sa_flags = 0; |  | ||||||
|     sigemptyset(&act.sa_mask); |  | ||||||
|     if (sigaction(SIGINT, &act, 0)) throw SysError("ignoring SIGINT"); |  | ||||||
| 
 |  | ||||||
|     restoreSIGPIPE(); |  | ||||||
| 
 |  | ||||||
|     Pipe toPager; |     Pipe toPager; | ||||||
|     toPager.create(); |     toPager.create(); | ||||||
| 
 | 
 | ||||||
|  | @ -323,6 +307,7 @@ RunPager::RunPager() | ||||||
|             throw SysError("dupping stdin"); |             throw SysError("dupping stdin"); | ||||||
|         if (!getenv("LESS")) |         if (!getenv("LESS")) | ||||||
|             setenv("LESS", "FRSXMK", 1); |             setenv("LESS", "FRSXMK", 1); | ||||||
|  |         restoreSignals(); | ||||||
|         if (pager) |         if (pager) | ||||||
|             execl("/bin/sh", "sh", "-c", pager, NULL); |             execl("/bin/sh", "sh", "-c", pager, NULL); | ||||||
|         execlp("pager", "pager", NULL); |         execlp("pager", "pager", NULL); | ||||||
|  | @ -331,6 +316,8 @@ RunPager::RunPager() | ||||||
|         throw SysError(format("executing ‘%1%’") % pager); |         throw SysError(format("executing ‘%1%’") % pager); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     pid.setKillSignal(SIGINT); | ||||||
|  | 
 | ||||||
|     if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1) |     if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1) | ||||||
|         throw SysError("dupping stdout"); |         throw SysError("dupping stdout"); | ||||||
| } | } | ||||||
|  | @ -345,7 +332,11 @@ RunPager::~RunPager() | ||||||
|             pid.wait(); |             pid.wait(); | ||||||
|         } |         } | ||||||
|     } catch (...) { |     } catch (...) { | ||||||
|         ignoreException(); |         try { | ||||||
|  |             pid.kill(true); | ||||||
|  |         } catch (...) { | ||||||
|  |             ignoreException(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -400,6 +400,8 @@ void Goal::trace(const format & f) | ||||||
| /* Common initialisation performed in child processes. */ | /* Common initialisation performed in child processes. */ | ||||||
| static void commonChildInit(Pipe & logPipe) | static void commonChildInit(Pipe & logPipe) | ||||||
| { | { | ||||||
|  |     restoreSignals(); | ||||||
|  | 
 | ||||||
|     /* Put the child in a separate session (and thus a separate
 |     /* Put the child in a separate session (and thus a separate
 | ||||||
|        process group) so that it has no controlling terminal (meaning |        process group) so that it has no controlling terminal (meaning | ||||||
|        that e.g. ssh cannot open /dev/tty) and it doesn't receive |        that e.g. ssh cannot open /dev/tty) and it doesn't receive | ||||||
|  | @ -2662,8 +2664,6 @@ void DerivationGoal::runChild() | ||||||
|         for (auto & i : drv->args) |         for (auto & i : drv->args) | ||||||
|             args.push_back(rewriteStrings(i, inputRewrites)); |             args.push_back(rewriteStrings(i, inputRewrites)); | ||||||
| 
 | 
 | ||||||
|         restoreSIGPIPE(); |  | ||||||
| 
 |  | ||||||
|         /* Indicate that we managed to set up the build environment. */ |         /* Indicate that we managed to set up the build environment. */ | ||||||
|         writeFull(STDERR_FILENO, string("\1\n")); |         writeFull(STDERR_FILENO, string("\1\n")); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -91,6 +91,7 @@ ref<RemoteStore::Connection> SSHStore::openConnection() | ||||||
| { | { | ||||||
|     if ((pid_t) sshMaster == -1) { |     if ((pid_t) sshMaster == -1) { | ||||||
|         sshMaster = startProcess([&]() { |         sshMaster = startProcess([&]() { | ||||||
|  |             restoreSignals(); | ||||||
|             if (key.empty()) |             if (key.empty()) | ||||||
|                 execlp("ssh", "ssh", "-N", "-M", "-S", socketPath.c_str(), uri.c_str(), NULL); |                 execlp("ssh", "ssh", "-N", "-M", "-S", socketPath.c_str(), uri.c_str(), NULL); | ||||||
|             else |             else | ||||||
|  |  | ||||||
|  | @ -860,6 +860,8 @@ string runProgram(Path program, bool searchPath, const Strings & args, | ||||||
|         Strings args_(args); |         Strings args_(args); | ||||||
|         args_.push_front(program); |         args_.push_front(program); | ||||||
| 
 | 
 | ||||||
|  |         restoreSignals(); | ||||||
|  | 
 | ||||||
|         if (searchPath) |         if (searchPath) | ||||||
|             execvp(program.c_str(), stringsToCharPtrs(args_).data()); |             execvp(program.c_str(), stringsToCharPtrs(args_).data()); | ||||||
|         else |         else | ||||||
|  | @ -909,16 +911,6 @@ void closeOnExec(int fd) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void restoreSIGPIPE() |  | ||||||
| { |  | ||||||
|     struct sigaction act; |  | ||||||
|     act.sa_handler = SIG_DFL; |  | ||||||
|     act.sa_flags = 0; |  | ||||||
|     sigemptyset(&act.sa_mask); |  | ||||||
|     if (sigaction(SIGPIPE, &act, 0)) throw SysError("resetting SIGPIPE"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| //////////////////////////////////////////////////////////////////////
 | //////////////////////////////////////////////////////////////////////
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -1218,19 +1210,31 @@ void triggerInterrupt() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static sigset_t savedSignalMask; | ||||||
|  | 
 | ||||||
| void startSignalHandlerThread() | void startSignalHandlerThread() | ||||||
| { | { | ||||||
|  |     if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask)) | ||||||
|  |         throw SysError("quering signal mask"); | ||||||
|  | 
 | ||||||
|     sigset_t set; |     sigset_t set; | ||||||
|     sigemptyset(&set); |     sigemptyset(&set); | ||||||
|     sigaddset(&set, SIGINT); |     sigaddset(&set, SIGINT); | ||||||
|     sigaddset(&set, SIGTERM); |     sigaddset(&set, SIGTERM); | ||||||
|     sigaddset(&set, SIGHUP); |     sigaddset(&set, SIGHUP); | ||||||
|  |     sigaddset(&set, SIGPIPE); | ||||||
|     if (pthread_sigmask(SIG_BLOCK, &set, nullptr)) |     if (pthread_sigmask(SIG_BLOCK, &set, nullptr)) | ||||||
|         throw SysError("blocking signals"); |         throw SysError("blocking signals"); | ||||||
| 
 | 
 | ||||||
|     std::thread(signalHandlerThread, set).detach(); |     std::thread(signalHandlerThread, set).detach(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void restoreSignals() | ||||||
|  | { | ||||||
|  |     if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr)) | ||||||
|  |         throw SysError("restoring signals"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* RAII helper to automatically deregister a callback. */ | /* RAII helper to automatically deregister a callback. */ | ||||||
| struct InterruptCallbackImpl : InterruptCallback | struct InterruptCallbackImpl : InterruptCallback | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -256,10 +256,6 @@ void closeMostFDs(const set<int> & exceptions); | ||||||
| /* Set the close-on-exec flag for the given file descriptor. */ | /* Set the close-on-exec flag for the given file descriptor. */ | ||||||
| void closeOnExec(int fd); | void closeOnExec(int fd); | ||||||
| 
 | 
 | ||||||
| /* Restore default handling of SIGPIPE, otherwise some programs will
 |  | ||||||
|    randomly say "Broken pipe". */ |  | ||||||
| void restoreSIGPIPE(); |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| /* User interruption. */ | /* User interruption. */ | ||||||
| 
 | 
 | ||||||
|  | @ -423,6 +419,9 @@ void callSuccess( | ||||||
|    on the current thread (and thus any threads created by it). */ |    on the current thread (and thus any threads created by it). */ | ||||||
| void startSignalHandlerThread(); | void startSignalHandlerThread(); | ||||||
| 
 | 
 | ||||||
|  | /* Restore default signal handling. */ | ||||||
|  | void restoreSignals(); | ||||||
|  | 
 | ||||||
| struct InterruptCallback | struct InterruptCallback | ||||||
| { | { | ||||||
|     virtual ~InterruptCallback() { }; |     virtual ~InterruptCallback() { }; | ||||||
|  |  | ||||||
|  | @ -452,6 +452,8 @@ int main(int argc, char ** argv) | ||||||
| 
 | 
 | ||||||
|                 auto argPtrs = stringsToCharPtrs(args); |                 auto argPtrs = stringsToCharPtrs(args); | ||||||
| 
 | 
 | ||||||
|  |                 restoreSignals(); | ||||||
|  | 
 | ||||||
|                 execvp(getEnv("NIX_BUILD_SHELL", "bash").c_str(), argPtrs.data()); |                 execvp(getEnv("NIX_BUILD_SHELL", "bash").c_str(), argPtrs.data()); | ||||||
| 
 | 
 | ||||||
|                 throw SysError("executing shell"); |                 throw SysError("executing shell"); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue