Add signal handler 98/308198/2
authorHwankyu Jhun <h.jhun@samsung.com>
Tue, 19 Mar 2024 11:02:16 +0000 (20:02 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Wed, 20 Mar 2024 11:35:08 +0000 (20:35 +0900)
To handle specific signals, tizen core adds signal handlers.

Change-Id: Iad1b54601128a93a09e13585afa40fef0c1c2357
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
src/tizen-core/stub.cc
src/tizen-core/unix_signal.cc [new file with mode: 0644]
src/tizen-core/unix_signal.h [new file with mode: 0644]

index ea2701ac9382b19311313fc5e8ba18a97c9712bf..849b248fdd57d092352b048d09eab30bd232fbad 100644 (file)
@@ -24,6 +24,7 @@
 #include "tizen-core/log_private.h"
 #include "tizen-core/task.h"
 #include "tizen-core/source.h"
+#include "tizen-core/unix_signal.h"
 
 #undef EXPORT
 #define EXPORT __attribute__ ((visibility("default")))
@@ -103,14 +104,18 @@ class SourceExt : public tizen_core::Source {
 
 API void tizen_core_init(void) {
   ++tizen_core_ref;
+  if (tizen_core_ref == 1)
+    tizen_core::UnixSignal::Init();
 }
 
 API void tizen_core_shutdown(void) {
   if (tizen_core_ref <= 0)
     return;
 
-  if (tizen_core_ref == 1)
+  if (tizen_core_ref == 1) {
+    tizen_core::UnixSignal::Shutdown();
     tizen_core::ContextManager::GetInst().DisposeAll();
+  }
 
   --tizen_core_ref;
 }
diff --git a/src/tizen-core/unix_signal.cc b/src/tizen-core/unix_signal.cc
new file mode 100644 (file)
index 0000000..58521bd
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "tizen-core/unix_signal.h"
+
+#include <glib.h>
+#include <glib-unix.h>
+#include <unistd.h>
+
+#include <memory>
+#include <unordered_map>
+
+#include "tizen-core/context_manager.h"
+#include "tizen-core/log_private.h"
+
+namespace tizen_core {
+namespace {
+
+class UnixSignalHandler {
+ public:
+  class IEvent {
+   public:
+    virtual ~IEvent() = default;
+    virtual void OnUnixSignal(int signo) = 0;
+  };
+
+  UnixSignalHandler(int signo, IEvent* listener);
+  ~UnixSignalHandler();
+
+  int SendSigInfo(siginfo_t* info);
+  int ReceiveSigInfo(siginfo_t* info);
+  void InvokePreviousSigAction(int signo, siginfo_t* info, void* context);
+
+ private:
+  static gboolean UnixFdSourceCb(gint fd, GIOCondition condition,
+                                 gpointer user_data);
+
+ private:
+  guint source_ = 0;
+  int pipe_fd_[2] = { -1, -1 };
+  int signo_;
+  IEvent* listener_;
+  struct sigaction prev_action_;
+};
+
+class UnixSignalImpl : public UnixSignalHandler::IEvent {
+ public:
+  UnixSignalImpl() {
+    signals_ = {SIGPIPE, SIGALRM, SIGCHLD, SIGUSR1, SIGUSR2,
+                SIGHUP,  SIGQUIT, SIGINT,  SIGTERM};
+    sigemptyset(&oldset_);
+  }
+
+  virtual ~UnixSignalImpl() { Shutdown(); }
+
+  void Init() {
+    sigset_t newset;
+    sigemptyset(&newset);
+    for (int signo : signals_) {
+      try {
+        signal_handlers_[signo] =
+            std::make_unique<UnixSignalHandler>(signo, this);
+      } catch (const std::runtime_error& e) {
+        _E("Exception occurs. %s", e.what());
+      }
+
+      sigaddset(&newset, signo);
+    }
+
+    pthread_atfork(OnPrepareAtfork, OnParentAtfork, OnChildAtfork);
+    pthread_sigmask(SIG_UNBLOCK, &newset, nullptr);
+    disposed_ = false;
+  }
+
+  void Shutdown() {
+    if (disposed_) return;
+    sigset_t newset;
+    sigemptyset(&newset);
+    for (auto signo : signals_) sigaddset(&newset, signo);
+    pthread_sigmask(SIG_BLOCK, &newset, nullptr);
+    ClearSignalHandlers();
+    disposed_ = true;
+  }
+
+  void BlockSignals() {
+    sigset_t newset;
+    sigemptyset(&newset);
+    for (auto signo : signals_) sigaddset(&newset, signo);
+
+    if (sigprocmask(SIG_BLOCK, &newset, &oldset_) != 0)
+      _E("sigprocmask() is failed. errno=%d", errno);
+  }
+
+  void UnblockSignals() {
+    if (sigprocmask(SIG_SETMASK, &oldset_, nullptr) != 0)
+      _E("sigprocmask() is failed. errno=%d", errno);
+  }
+
+  void ClearSignalHandlers() { signal_handlers_.clear(); }
+
+  UnixSignalHandler* GetSignalHandler(int signo) {
+    return signal_handlers_[signo].get();
+  }
+
+  bool IsDisposed() const { return disposed_; }
+
+ private:
+  void OnUnixSignal(int signo) override {
+    _E("signo=%d", signo);
+    if (signo == SIGCHLD)
+      HandleSigchld();
+    else if (signo == SIGUSR1 || signo == SIGUSR2 || signo == SIGHUP ||
+               signo == SIGQUIT || signo == SIGINT || signo == SIGTERM)
+      QuitMainLoop();
+  }
+
+  void HandleSigchld() {
+    pid_t pid;
+    int status;
+    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+      _E("Child process %d exited with status %d", pid, status);
+    }
+  }
+
+  void QuitMainLoop() {
+    auto context = ContextManager::GetInst().FindFromThisThread();
+    if (context != nullptr) {
+      auto loop = context->GetLoop();
+      if (loop) {
+        loop->Quit();
+        return;
+      }
+    }
+
+    _E("exit()");
+    exit(-1);
+  }
+
+  static void OnPrepareAtfork();
+  static void OnParentAtfork();
+  static void OnChildAtfork();
+
+ private:
+  bool disposed_ = true;
+  std::vector<int> signals_;
+  std::unordered_map<int, std::unique_ptr<UnixSignalHandler>> signal_handlers_;
+  sigset_t oldset_;
+};
+
+UnixSignalImpl impl;
+
+void SignalHandler(int signo, siginfo_t* info, void* context) {
+  if (impl.IsDisposed()) return;
+
+  auto* handler = impl.GetSignalHandler(signo);
+  handler->SendSigInfo(info);
+  handler->InvokePreviousSigAction(signo, info, context);
+}
+
+UnixSignalHandler::UnixSignalHandler(int signo,
+                                     UnixSignalHandler::IEvent* listener)
+    : signo_(signo), listener_(listener) {
+  if (pipe2(pipe_fd_, O_CLOEXEC | O_NONBLOCK) != 0) {
+    _E("pipe2() is failed. errno=%d", errno);
+    throw std::runtime_error("pipe2() is failed.");
+  }
+
+  source_ = g_unix_fd_add(pipe_fd_[0], G_IO_IN, UnixFdSourceCb, this);
+  if (source_ == 0) {
+    _E("g_unix_fd_add() is failed. errno=%d", errno);
+    close(pipe_fd_[1]);
+    close(pipe_fd_[0]);
+    throw std::runtime_error("g_unix_fd_add() is failed.");
+  }
+
+  struct sigaction action;
+  action.sa_sigaction = SignalHandler;
+  action.sa_flags = SA_RESTART | SA_SIGINFO;
+  sigemptyset(&action.sa_mask);
+  sigaction(signo_, &action, &prev_action_);
+}
+
+UnixSignalHandler::~UnixSignalHandler() {
+  struct sigaction action;
+  action.sa_handler = SIG_DFL;
+  action.sa_flags = 0;
+  sigemptyset(&action.sa_mask);
+  sigaction(signo_, &action, nullptr);
+
+  if (source_ != 0)
+    g_source_remove(source_);
+
+  close(pipe_fd_[0]);
+  close(pipe_fd_[1]);
+}
+
+int UnixSignalHandler::SendSigInfo(siginfo_t* info) {
+  unsigned char* buffer = reinterpret_cast<unsigned char*>(info);
+  ssize_t len = sizeof(siginfo_t);
+  while (len) {
+    ssize_t bytes = write(pipe_fd_[1], buffer, len);
+    if (bytes < 0) {
+      if (errno == EINTR) continue;
+
+      _E("write() is failed. pipe_fd=%d, errno=%d", pipe_fd_[1], errno);
+      return -1;
+    }
+
+    len -= bytes;
+    buffer += bytes;
+  }
+
+  return 0;
+}
+
+int UnixSignalHandler::ReceiveSigInfo(siginfo_t* info) {
+  unsigned char* buffer = reinterpret_cast<unsigned char*>(info);
+  ssize_t len = sizeof(siginfo_t);
+  while (len) {
+    ssize_t bytes = read(pipe_fd_[0], buffer, len);
+    if (bytes == 0) {
+      _E("EOF. pipe_fd=%d", pipe_fd_[0]);
+      return -1;
+    }
+
+    if (bytes < 0) {
+      if (errno == EINTR) continue;
+
+      _E("read() failed. pipe_fd=%d, errno=%d", pipe_fd_[0], errno);
+      return -1;
+    }
+
+    len -= bytes;
+    buffer += bytes;
+  }
+
+  return 0;
+}
+
+void UnixSignalHandler::InvokePreviousSigAction(int signo, siginfo_t* info,
+                                                void* context) {
+  if ((prev_action_.sa_flags & SA_SIGINFO) == 0 &&
+      prev_action_.sa_handler == SIG_DFL)
+    return;
+
+  if ((prev_action_.sa_flags & SA_SIGINFO) == 0 &&
+      prev_action_.sa_handler == SIG_IGN)
+    return;
+
+  if (prev_action_.sa_flags & SA_SIGINFO) {
+    _E("sa_sigaction(%d, %p, %p)", signo, info, context);
+    if (prev_action_.sa_sigaction != nullptr)
+      prev_action_.sa_sigaction(signo, info, context);
+  } else {
+    _E("sa_handler(%d)", signo);
+    if (prev_action_.sa_handler!= nullptr)
+      prev_action_.sa_handler(signo);
+  }
+}
+
+gboolean UnixSignalHandler::UnixFdSourceCb(gint fd, GIOCondition condtion,
+                                           gpointer user_data) {
+  siginfo_t info;
+  auto* handler = static_cast<UnixSignalHandler*>(user_data);
+  if (handler->ReceiveSigInfo(&info) != 0) {
+    handler->source_ = 0;
+    return G_SOURCE_REMOVE;
+  }
+
+  auto* listener = handler->listener_;
+  listener->OnUnixSignal(info.si_signo);
+  return G_SOURCE_CONTINUE;
+}
+
+void UnixSignalImpl::OnPrepareAtfork() { impl.BlockSignals(); }
+
+void UnixSignalImpl::OnParentAtfork() { impl.UnblockSignals(); }
+
+void UnixSignalImpl::OnChildAtfork() { impl.ClearSignalHandlers(); }
+
+}  // namespace
+
+void UnixSignal::Init() { impl.Init(); }
+
+void UnixSignal::Shutdown() { impl.Shutdown(); }
+
+}  // namespace tizen_core
diff --git a/src/tizen-core/unix_signal.h b/src/tizen-core/unix_signal.h
new file mode 100644 (file)
index 0000000..3116124
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 TIZEN_CORE_UNIX_SIGNAL_H_
+#define TIZEN_CORE_UnIX_SIGNAL_H_
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#undef EXPORT_API
+#define EXPORT_API __attribute__((visibility("default")))
+
+namespace tizen_core {
+
+class EXPORT_API UnixSignal {
+ public:
+  static void Init();
+  static void Shutdown();
+};
+
+}  // namespace tizen_core
+
+#endif  // TIZEN_CORE_UNIX_SIGNAL_H_