Handling signals with signalfd 87/43387/9
authorJan Olszak <j.olszak@samsung.com>
Tue, 7 Jul 2015 11:13:45 +0000 (13:13 +0200)
committerJan Olszak <j.olszak@samsung.com>
Fri, 10 Jul 2015 11:00:28 +0000 (13:00 +0200)
[Feature]       Synchronous signal handling
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, install, run tests

Change-Id: I5a31fcb9929a3d8d36c1ea275c089412687478d5

common/utils/signal.cpp
common/utils/signal.hpp
common/utils/signalfd.cpp [new file with mode: 0644]
common/utils/signalfd.hpp [new file with mode: 0644]
libs/ipc/epoll/event-poll.hpp
server/main.cpp
server/server.cpp
server/server.hpp
zone-daemon/CMakeLists.txt

index 2daa766..17ec003 100644 (file)
 
 namespace utils {
 
-void signalBlock(const int signalToBlock)
+namespace {
+
+void setSignalMask(int how, const ::sigset_t& set)
 {
+    int ret = ::pthread_sigmask(how, &set, nullptr /*&oldSet*/);
+    if(ret != 0) {
+        const std::string msg = getSystemErrorMessage(ret);
+        LOGE("Error in pthread_sigmask: " << msg);
+        throw UtilsException("Error in pthread_sigmask: " + msg);
+    }
+}
+
+void changeSignal(int how, const int sigNum) {
     ::sigset_t set;
-    if (-1 == ::sigemptyset(&set)) {
+    if(-1 == ::sigemptyset(&set)) {
         const std::string msg = getSystemErrorMessage();
-        LOGE("Error in sigemptyset: " << msg);
-        throw UtilsException("Error in sigemptyset: " + msg);
+        LOGE("Error in sigfillset: " << msg);
+        throw UtilsException("Error in sigfillset: " + msg);
     }
 
-    if (-1 ==::sigaddset(&set, signalToBlock)) {
+    if(-1 ==::sigaddset(&set, sigNum)) {
         const std::string msg = getSystemErrorMessage();
-        LOGE("Error in sigaddset: " << msg);
-        throw UtilsException("Error in sigaddset: " + msg);
+        LOGE("Error in sigdelset: " << msg);
+        throw UtilsException("Error in sigdelset: " + msg);
     }
 
-    int ret = ::pthread_sigmask(SIG_BLOCK, &set, nullptr /*&oldSet*/);
-    if (ret != 0) {
-        LOGE("Error in pthread_sigmask: " << std::to_string(ret));
-        throw UtilsException("Error in pthread_sigmask: " + std::to_string(ret));
+    setSignalMask(how, set);
+}
+
+}// namespace
+
+::sigset_t getSignalMask()
+{
+    ::sigset_t set;
+    int ret = ::pthread_sigmask(0 /*ignored*/, nullptr /*get the oldset*/, &set);
+    if(ret != 0) {
+        const std::string msg = getSystemErrorMessage(ret);
+        LOGE("Error in pthread_sigmask: " << msg);
+        throw UtilsException("Error in pthread_sigmask: " + msg);
+    }
+    return set;
+}
+
+bool isSignalBlocked(const int sigNum)
+{
+    ::sigset_t set = getSignalMask();
+
+    int ret = ::sigismember(&set, sigNum);
+    if(-1 == ret) {
+        const std::string msg = getSystemErrorMessage();
+        LOGE("Error in sigismember: " << msg);
+        throw UtilsException("Error in sigismember: " + msg);
     }
+
+    return ret == 1;
+}
+
+void signalBlock(const int sigNum)
+{
+    changeSignal(SIG_BLOCK, sigNum);
+}
+
+void signalBlockAll()
+{
+    ::sigset_t set;
+    if(-1 == ::sigfillset(&set)) {
+        const std::string msg = getSystemErrorMessage();
+        LOGE("Error in sigfillset: " << msg);
+        throw UtilsException("Error in sigfillset: " + msg);
+    }
+
+    setSignalMask(SIG_BLOCK, set);
+}
+
+void signalUnblock(const int sigNum)
+{
+    changeSignal(SIG_UNBLOCK, sigNum);
 }
 
 } // namespace utils
index c466688..4cd5a5a 100644 (file)
 #ifndef COMMON_UTILS_SIGNAL_HPP
 #define COMMON_UTILS_SIGNAL_HPP
 
+#include <csignal>
+
 namespace utils {
 
-void signalBlock(const int signalsToBlock);
+::sigset_t getSignalMask();
+bool isSignalBlocked(const int sigNum);
+void signalBlockAll();
+void signalBlock(const int sigNum);
+void signalUnblock(const int sigNum);
 
 } // namespace utils
 
diff --git a/common/utils/signalfd.cpp b/common/utils/signalfd.cpp
new file mode 100644 (file)
index 0000000..0b3250f
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Eventfd wrapper
+ */
+
+#include "utils/signalfd.hpp"
+#include "utils/signal.hpp"
+#include "utils/fd-utils.hpp"
+#include "utils/exception.hpp"
+#include "logger/logger.hpp"
+
+#include <functional>
+
+namespace utils {
+
+SignalFD::SignalFD(ipc::epoll::EventPoll& eventPoll)
+    :mEventPoll(eventPoll)
+{
+    ::sigset_t set = getSignalMask();
+
+    mFD = ::signalfd(-1, &set, SFD_CLOEXEC);
+    if (mFD == -1) {
+        const std::string msg = getSystemErrorMessage();
+        LOGE("Error in signalfd: " << msg);
+        throw UtilsException("Error in signalfd: " + msg);
+    }
+
+    mEventPoll.addFD(mFD, EPOLLIN, std::bind(&SignalFD::handleInternal, this));
+}
+
+SignalFD::~SignalFD()
+{
+    mEventPoll.removeFD(mFD);
+    utils::close(mFD);
+}
+
+int SignalFD::getFD() const
+{
+    return mFD;
+}
+
+void SignalFD::setHandler(const int sigNum, const Callback&& callback)
+{
+    Lock lock(mMutex);
+
+    bool isBlocked = isSignalBlocked(sigNum);
+
+    ::sigset_t set = getSignalMask();
+    if(!isBlocked) {
+        signalBlock(sigNum);
+    }
+
+    int error = ::signalfd(mFD, &set, SFD_CLOEXEC);
+    if (error != mFD) {
+        const std::string msg = getSystemErrorMessage();
+        LOGE("Error in signalfd: " << msg);
+        if(!isBlocked) {
+            signalUnblock(sigNum);
+        }
+        throw UtilsException("Error in signalfd: " + msg);
+    }
+
+    mCallbacks.insert({sigNum, callback});
+}
+
+void SignalFD::handleInternal()
+{
+    signalfd_siginfo sigInfo;
+    utils::read(mFD, &sigInfo, sizeof(sigInfo));
+
+    LOGD("Got signal: " << sigInfo.ssi_signo);
+
+    {
+        Lock lock(mMutex);
+        auto it = mCallbacks.find(sigInfo.ssi_signo);
+        if (it == mCallbacks.end()) {
+            // Meantime the callback was deleted
+            LOGE("No callback for signal: " << sigInfo.ssi_signo);
+            return;
+        }
+
+        it->second(sigInfo.ssi_signo);
+    }
+}
+
+} // namespace utils
diff --git a/common/utils/signalfd.hpp b/common/utils/signalfd.hpp
new file mode 100644 (file)
index 0000000..5410a34
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+*  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+*
+*  Contact: Jan Olszak <j.olszak@samsung.com>
+*
+*  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
+*/
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Eventfd wrapper
+ */
+
+#ifndef COMMON_UTILS_SIGNALFD_HPP
+#define COMMON_UTILS_SIGNALFD_HPP
+
+#include "ipc/epoll/event-poll.hpp"
+
+#include <csignal>
+#include <sys/signalfd.h>
+
+#include <functional>
+#include <mutex>
+#include <unordered_map>
+#include <memory>
+
+namespace utils {
+
+/**
+ * SignalFD takes control over handling signals
+ * sent to the thread.
+ *
+ * It should be the only place where signal masks are modified.
+ */
+class SignalFD {
+public:
+    typedef std::function<void(const int sigNum)> Callback;
+
+    SignalFD(ipc::epoll::EventPoll& eventPoll);
+    ~SignalFD();
+
+    SignalFD(const SignalFD& signalfd) = delete;
+    SignalFD& operator=(const SignalFD&) = delete;
+
+    /**
+     * Add a callback for a specified signal
+     *
+     * @param sigNum number of the signal
+     * @param callback handler callback
+     */
+    void setHandler(const int sigNum, const Callback&& callback);
+
+    /**
+     * @return signal file descriptor
+     */
+    int getFD() const;
+
+private:
+    typedef std::unique_lock<std::mutex> Lock;
+
+    int mFD;
+    std::mutex mMutex;
+    ipc::epoll::EventPoll& mEventPoll;
+    std::unordered_map<int, Callback> mCallbacks;
+
+    void handleInternal();
+};
+
+} // namespace utils
+
+#endif // COMMON_UTILS_SIGNALFD_HPP
index 5e84ee3..ba82497 100644 (file)
@@ -49,7 +49,7 @@ public:
     void removeFD(const int fd);
 
     /**
-     * Dispatch at most one signalled FD
+     * Dispatch at most one signaled FD
      * @param timeoutMs how long should wait in case of no pending events
      *        (0 - return immediately, -1 - wait forever)
      * @return false on timeout
index 55a795e..513a4f3 100644 (file)
@@ -37,6 +37,7 @@
 #include "logger/backend-stderr.hpp"
 #include "logger/backend-journal.hpp"
 #include "utils/typeinfo.hpp"
+#include "utils/signal.hpp"
 
 #include <boost/program_options.hpp>
 #include <iostream>
@@ -121,6 +122,10 @@ int main(int argc, char* argv[])
     }
 
     try {
+        // Block all signals
+        // Server will unblock handled signals
+        utils::signalBlockAll();
+
         Server server(CONFIG_PATH);
         server.run(runAsRoot);
         server.reloadIfRequired(argv);
index 5abd430..c8413c7 100644 (file)
@@ -41,7 +41,6 @@
 #include <cerrno>
 #include <string>
 #include <cstring>
-#include <atomic>
 #include <unistd.h>
 #include <pwd.h>
 #include <sys/stat.h>
@@ -78,68 +77,55 @@ using namespace utils;
 
 namespace vasum {
 
-
 Server::Server(const std::string& configPath)
-    : mConfigPath(configPath)
+    : mIsUpdate(false),
+      mConfigPath(configPath),
+      mSignalFD(mDispatcher.getPoll())
 {
-}
-
-
-namespace {
-
-std::atomic_bool gUpdateTriggered(false);
-utils::Latch gSignalLatch;
+    mSignalFD.setHandler(SIGINT, [this](int) {
+        mStopLatch.set();
+    });
 
-void signalHandler(const int sig)
-{
-    LOGI("Got signal " << sig);
+    mSignalFD.setHandler(SIGTERM, [this] (int) {
+        mStopLatch.set();
+    });
 
-    if (sig == SIGUSR1) {
+    mSignalFD.setHandler(SIGUSR1, [this] (int) {
         LOGD("Received SIGUSR1 - triggering update.");
-        gUpdateTriggered = true;
-    }
-
-    gSignalLatch.set();
+        mIsUpdate = true;
+        mStopLatch.set();
+    });
 }
 
-} // namespace
-
 void Server::run(bool asRoot)
 {
     if (!prepareEnvironment(mConfigPath, asRoot)) {
         throw ServerException("Environment setup failed");
     }
 
-    signal(SIGINT,  signalHandler);
-    signal(SIGTERM, signalHandler);
-    signal(SIGUSR1, signalHandler);
-    utils::signalBlock(SIGPIPE);
-
     LOGI("Starting daemon...");
     {
         utils::ScopedGlibLoop loop;
         ZonesManager manager(mDispatcher.getPoll(), mConfigPath);
 
         // Do not restore zones state at Vasum start
-        // manager.restoreAll();
         LOGI("Daemon started");
 
-        gSignalLatch.wait();
+        mStopLatch.wait();
 
         // Detach zones if we triggered an update
-        if (gUpdateTriggered) {
+        if (mIsUpdate) {
             manager.setZonesDetachOnExit();
         }
 
         LOGI("Stopping daemon...");
-        // manager.shutdownAll() will be called in destructor
     }
     LOGI("Daemon stopped");
 }
 
 void Server::reloadIfRequired(char* argv[])
 {
-    if (gUpdateTriggered) {
+    if (mIsUpdate) {
         execve(argv[0], argv, environ);
         LOGE("Failed to reload " << argv[0] << ": " << getSystemErrorMessage());
     }
@@ -148,7 +134,7 @@ void Server::reloadIfRequired(char* argv[])
 void Server::terminate()
 {
     LOGI("Terminating server");
-    gSignalLatch.set();
+    mStopLatch.set();
 }
 
 bool Server::checkEnvironment()
@@ -267,10 +253,11 @@ bool Server::prepareEnvironment(const std::string& configPath, bool runAsRoot)
     // directory or symlink
     // CAP_SETUID is needed to launch specific funtions as root (see environment.cpp)
     return (runAsRoot || utils::dropRoot(uid, gid, {CAP_SYS_ADMIN,
-                                                    CAP_MAC_OVERRIDE,
-                                                    CAP_SYS_TTY_CONFIG,
-                                                    CAP_CHOWN,
-                                                    CAP_SETUID}));
+                                         CAP_MAC_OVERRIDE,
+                                         CAP_SYS_TTY_CONFIG,
+                                         CAP_CHOWN,
+                                         CAP_SETUID
+                                                   }));
 }
 
 
index bf2519b..0d10396 100644 (file)
 #define SERVER_SERVER_HPP
 
 #include "utils/latch.hpp"
+#include "utils/signalfd.hpp"
 #include "ipc/epoll/thread-dispatcher.hpp"
 
+#include <atomic>
 #include <string>
 
 
@@ -61,9 +63,11 @@ public:
     static bool checkEnvironment();
 
 private:
+    std::atomic_bool mIsUpdate;
     std::string mConfigPath;
+    utils::Latch mStopLatch;
     ipc::epoll::ThreadDispatcher mDispatcher;
-
+    utils::SignalFD mSignalFD;
     /**
      * Set needed caps, groups and drop root privileges.
      */
index 4944dfb..7171eee 100644 (file)
@@ -43,7 +43,7 @@ SET_TARGET_PROPERTIES(${ZONE_DAEMON_CODENAME} PROPERTIES
 )
 
 TARGET_LINK_LIBRARIES(${ZONE_DAEMON_CODENAME} ${ZONE_DAEMON_DEPS_LIBRARIES}
-                      ${Boost_LIBRARIES} Logger SimpleDbus)
+                      ${Boost_LIBRARIES} Logger SimpleDbus Ipc)
 
 
 ## Install #####################################################################