summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-09-05 12:46:09 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-09-05 12:46:09 +0000
commit0610a859d979db77aa6b1e3591d13c0d8a8a63c0 (patch)
treeb295dd0d65b25028c26b3ff4d8f18b9e6300de46
parent01689aa9b16a2d78b21c5d037beeda4eb201d1f6 (diff)
* process.c (disable_child_handler_before_fork): New function.
(disable_child_handler_fork_parent): Ditto. (disable_child_handler_fork_child): Ditto. (retry_fork_async_signal_safe): Call above functions to disable signal handlers in child process. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47412 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog8
-rw-r--r--process.c81
2 files changed, 87 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 69ee94f9df..70bbe99399 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Fri Sep 5 21:45:33 2014 Tanaka Akira <akr@fsij.org>
+
+ * process.c (disable_child_handler_before_fork): New function.
+ (disable_child_handler_fork_parent): Ditto.
+ (disable_child_handler_fork_child): Ditto.
+ (retry_fork_async_signal_safe): Call above functions to disable
+ signal handlers in child process.
+
Fri Sep 5 21:02:54 2014 Tanaka Akira <akr@fsij.org>
* process.c (handle_fork_error): Make try_gc_p argument volatile to
diff --git a/process.c b/process.c
index 5dd505b559..46547025eb 100644
--- a/process.c
+++ b/process.c
@@ -3384,6 +3384,77 @@ has_privilege(void)
return 0;
}
+static void
+disable_child_handler_before_fork(sigset_t *oldset)
+{
+ int ret;
+ sigset_t all;
+
+ ret = sigfillset(&all);
+ if (ret == -1)
+ rb_sys_fail("sigfillset");
+
+ ret = pthread_sigmask(SIG_SETMASK, &all, oldset); /* not async-signal-safe */
+ if (ret != 0) {
+ errno = ret;
+ rb_sys_fail("pthread_sigmask");
+ }
+}
+
+static void
+disable_child_handler_fork_parent(sigset_t *oldset)
+{
+ int ret;
+
+ ret = pthread_sigmask(SIG_SETMASK, oldset, NULL); /* not async-signal-safe */
+ if (ret != 0) {
+ errno = ret;
+ rb_sys_fail("pthread_sigmask");
+ }
+}
+
+/* This function should be async-signal-safe. Actually it is. */
+static int
+disable_child_handler_fork_child(sigset_t *oldset, char *errmsg, size_t errmsg_buflen)
+{
+ int sig;
+ int ret;
+ struct sigaction act, oact;
+
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = 0;
+ ret = sigemptyset(&act.sa_mask); /* async-signal-safe */
+ if (ret == -1) {
+ ERRMSG("sigemptyset");
+ return -1;
+ }
+
+ for (sig = 1; sig < NSIG; sig++) {
+ ret = sigaction(sig, NULL, &oact); /* async-signal-safe */
+ if (ret == -1 && errno == EINVAL) {
+ continue; /* Ignore invalid signal number. */
+ }
+ if (ret == -1) {
+ ERRMSG("sigaction to obtain old action");
+ return -1;
+ }
+ if ((oact.sa_flags & SA_SIGINFO) || (oact.sa_handler != SIG_IGN && oact.sa_handler != SIG_DFL)) {
+ ret = sigaction(sig, &act, NULL); /* async-signal-safe */
+ if (ret == -1) {
+ ERRMSG("sigaction to set default action");
+ return -1;
+ }
+ }
+ }
+
+ ret = sigprocmask(SIG_SETMASK, oldset, NULL); /* async-signal-safe */
+ if (ret != 0) {
+ ERRMSG("sigprocmask");
+ return -1;
+ }
+ return 0;
+}
+
static rb_pid_t
retry_fork_async_signal_safe(int *status, int *ep,
int (*chfunc)(void*, char *, size_t), void *charg,
@@ -3391,9 +3462,11 @@ retry_fork_async_signal_safe(int *status, int *ep,
{
rb_pid_t pid;
volatile int try_gc = 1;
+ sigset_t oldsigmask;
while (1) {
prefork();
+ disable_child_handler_before_fork(&oldsigmask);
#ifdef HAVE_WORKING_VFORK
if (!has_privilege())
pid = vfork();
@@ -3405,8 +3478,11 @@ retry_fork_async_signal_safe(int *status, int *ep,
if (pid == 0) {/* fork succeed, child process */
int ret;
close(ep[0]);
- ret = chfunc(charg, errmsg, errmsg_buflen);
- if (!ret) _exit(EXIT_SUCCESS);
+ ret = disable_child_handler_fork_child(&oldsigmask, errmsg, errmsg_buflen); /* async-signal-safe */
+ if (ret == 0) {
+ ret = chfunc(charg, errmsg, errmsg_buflen);
+ if (!ret) _exit(EXIT_SUCCESS);
+ }
send_child_error(ep[1], errmsg, errmsg_buflen);
#if EXIT_SUCCESS == 127
_exit(EXIT_FAILURE);
@@ -3414,6 +3490,7 @@ retry_fork_async_signal_safe(int *status, int *ep,
_exit(127);
#endif
}
+ preserving_errno(disable_child_handler_fork_parent(&oldsigmask));
if (0 < pid) /* fork succeed, parent process */
return pid;
/* fork failed */