summaryrefslogtreecommitdiffstats
path: root/sigchainlib
diff options
context:
space:
mode:
Diffstat (limited to 'sigchainlib')
-rw-r--r--sigchainlib/Android.mk29
-rw-r--r--sigchainlib/sigchain.cc183
-rw-r--r--sigchainlib/sigchain.h29
3 files changed, 241 insertions, 0 deletions
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk
new file mode 100644
index 0000000..cb1778d
--- /dev/null
+++ b/sigchainlib/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include art/build/Android.common.mk
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
+LOCAL_SRC_FILES := sigchain.cc
+LOCAL_MODULE:= libsigchain
+LOCAL_SHARED_LIBRARIES += liblog libdl
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+include $(BUILD_SHARED_LIBRARY)
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
new file mode 100644
index 0000000..26e7d31
--- /dev/null
+++ b/sigchainlib/sigchain.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace art {
+
+class SignalAction {
+ public:
+ SignalAction() : claimed_(false) {
+ }
+
+ // Claim the signal and keep the action specified.
+ void Claim(const struct sigaction& action) {
+ action_ = action;
+ claimed_ = true;
+ }
+
+ // Unclaim the signal and restore the old action.
+ void Unclaim(int signal) {
+ claimed_ = false;
+ sigaction(signal, &action_, NULL); // Restore old action.
+ }
+
+ // Get the action associated with this signal.
+ const struct sigaction& GetAction() const {
+ return action_;
+ }
+
+ // Is the signal claimed?
+ bool IsClaimed() const {
+ return claimed_;
+ }
+
+ // Change the recorded action to that specified.
+ void SetAction(const struct sigaction& action) {
+ action_ = action;
+ }
+
+ private:
+ struct sigaction action_; // Action to be performed.
+ bool claimed_; // Whether signal is claimed or not.
+};
+
+// User's signal handlers
+static SignalAction user_sigactions[_NSIG];
+
+static void log(const char* format, ...) {
+ char buf[256];
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(buf, sizeof(buf), format, ap);
+ __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf);
+ va_end(ap);
+}
+
+static void CheckSignalValid(int signal) {
+ if (signal <= 0 || signal >= _NSIG) {
+ log("Invalid signal %d", signal);
+ abort();
+ }
+}
+
+// Claim a signal chain for a particular signal.
+void ClaimSignalChain(int signal, struct sigaction* oldaction) {
+ CheckSignalValid(signal);
+ user_sigactions[signal].Claim(*oldaction);
+}
+
+void UnclaimSignalChain(int signal) {
+ CheckSignalValid(signal);
+
+ user_sigactions[signal].Unclaim(signal);
+}
+
+// Invoke the user's signal handler.
+void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) {
+ // Check the arguments.
+ CheckSignalValid(sig);
+
+ // The signal must have been claimed in order to get here. Check it.
+ if (!user_sigactions[sig].IsClaimed()) {
+ abort();
+ }
+
+ const struct sigaction& action = user_sigactions[sig].GetAction();
+
+ // Only deliver the signal if the signal was not masked out.
+ if (sigismember(&action.sa_mask, sig)) {
+ return;
+ }
+ if ((action.sa_flags & SA_SIGINFO) == 0) {
+ if (action.sa_handler != NULL) {
+ action.sa_handler(sig);
+ }
+ } else {
+ if (action.sa_sigaction != NULL) {
+ action.sa_sigaction(sig, info, context);
+ }
+ }
+}
+
+extern "C" {
+// These functions are C linkage since they replace the functions in libc.
+
+int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
+ // If this signal has been claimed as a signal chain, record the user's
+ // action but don't pass it on to the kernel.
+ // Note that we check that the signal number is in range here. An out of range signal
+ // number should behave exactly as the libc sigaction.
+ if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
+ if (old_action != NULL) {
+ *old_action = user_sigactions[signal].GetAction();
+ }
+ if (new_action != NULL) {
+ user_sigactions[signal].SetAction(*new_action);
+ }
+ return 0;
+ }
+
+ // Will only get here if the signal chain has not been claimed. We want
+ // to pass the sigaction on to the kernel via the real sigaction in libc.
+
+ void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
+ if (linked_sigaction_sym == nullptr) {
+ log("Unable to find next sigaction in signal chain");
+ abort();
+ }
+
+ typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
+ SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
+ return linked_sigaction(signal, new_action, old_action);
+}
+
+
+int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
+ const sigset_t* new_set_ptr = bionic_new_set;
+ sigset_t tmpset;
+ if (bionic_new_set != NULL) {
+ tmpset = *bionic_new_set;
+
+ if (how == SIG_BLOCK) {
+ // Don't allow claimed signals in the mask. If a signal chain has been claimed
+ // we can't allow the user to block that signal.
+ for (int i = 0 ; i < _NSIG; ++i) {
+ if (user_sigactions[i].IsClaimed() && sigismember(&tmpset, i)) {
+ sigdelset(&tmpset, i);
+ }
+ }
+ }
+ new_set_ptr = &tmpset;
+ }
+
+ void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
+ if (linked_sigprocmask_sym == nullptr) {
+ log("Unable to find next sigprocmask in signal chain");
+ abort();
+ }
+
+ typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
+ SigProcMask linked_sigprocmask= reinterpret_cast<SigProcMask>(linked_sigprocmask_sym);
+ return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
+}
+} // extern "C"
+} // namespace art
+
diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h
new file mode 100644
index 0000000..f6f2253
--- /dev/null
+++ b/sigchainlib/sigchain.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SIGCHAINLIB_SIGCHAIN_H_
+#define ART_SIGCHAINLIB_SIGCHAIN_H_
+
+#include <signal.h>
+namespace art {
+
+void ClaimSignalChain(int signal, struct sigaction* oldaction);
+void UnclaimSignalChain(int signal);
+void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context);
+
+} // namespace art
+
+#endif // ART_SIGCHAINLIB_SIGCHAIN_H_