lxcpp: Initial implementation of the start API call 48/47548/22
authorLukasz Pawelczyk <l.pawelczyk@samsung.com>
Wed, 29 Jul 2015 11:27:25 +0000 (13:27 +0200)
committerLukasz Pawelczyk <l.pawelczyk@samsung.com>
Wed, 16 Sep 2015 14:08:20 +0000 (16:08 +0200)
[Feature]       Initial implementation of the start API call.
[Verification]  Build, install, run tests

Significant changes related to the start API call:
- Start command that daemonizes and execs guard binary
- Guard process implementation that execs the container's init

Additional changes in this commit supporting the Start API call:
- API extension to Channel class, support close on exec and be able
  to survive (as a FD) exec() call
- set close on exec in persistent file logger
- new logger helper to setup the logger
- add pid to the log format

Change-Id: I2d9648e2a861add2aa1bd1d66587bff2a109cc9c

26 files changed:
CMakeLists.txt
common/utils/channel.cpp
common/utils/channel.hpp
libs/logger/backend-persistent-file.hpp
libs/logger/formatter.cpp
libs/logger/level.hpp
libs/logger/logger.cpp
libs/logger/logger.hpp
libs/lxcpp/CMakeLists.txt
libs/lxcpp/commands/attach.cpp
libs/lxcpp/commands/start.cpp [new file with mode: 0644]
libs/lxcpp/commands/start.hpp [new file with mode: 0644]
libs/lxcpp/container-config.hpp [new file with mode: 0644]
libs/lxcpp/container-impl.cpp
libs/lxcpp/container-impl.hpp
libs/lxcpp/container.hpp
libs/lxcpp/exception.hpp
libs/lxcpp/guard/CMakeLists.txt [new file with mode: 0644]
libs/lxcpp/guard/guard.cpp [new file with mode: 0644]
libs/lxcpp/guard/guard.hpp [new file with mode: 0644]
libs/lxcpp/guard/main.cpp [new file with mode: 0644]
libs/lxcpp/logger-config.cpp [new file with mode: 0644]
libs/lxcpp/logger-config.hpp [new file with mode: 0644]
libs/lxcpp/utils.cpp [new file with mode: 0644]
libs/lxcpp/utils.hpp [new file with mode: 0644]
packaging/vasum.spec

index 5b98eee..0409167 100644 (file)
@@ -143,10 +143,10 @@ ENDIF(NOT DEFINED PYTHON_SITELIB)
 SET(COMMON_FOLDER ${PROJECT_SOURCE_DIR}/common)
 SET(LIBS_FOLDER ${PROJECT_SOURCE_DIR}/libs)
 SET(LOGGER_FOLDER ${PROJECT_SOURCE_DIR}/libs/logger)
-SET(LXCPP_FOLDER ${PROJECT_SOURCE_DIR}/libs/lxcpp)
 SET(DBUS_FOLDER ${PROJECT_SOURCE_DIR}/libs/dbus)
 SET(CONFIG_FOLDER ${PROJECT_SOURCE_DIR}/libs/config)
 SET(IPC_FOLDER ${PROJECT_SOURCE_DIR}/libs/ipc)
+SET(LXCPP_FOLDER ${PROJECT_SOURCE_DIR}/libs/lxcpp)
 SET(CLIENT_FOLDER ${PROJECT_SOURCE_DIR}/client)
 SET(SERVER_FOLDER ${PROJECT_SOURCE_DIR}/server)
 SET(ZONE_SUPPORT_FOLDER ${PROJECT_SOURCE_DIR}/zone-support)
@@ -180,6 +180,10 @@ IF(NOT DEFINED DATA_DIR)
     SET(DATA_DIR "${CMAKE_INSTALL_PREFIX}/share")
 ENDIF(NOT DEFINED DATA_DIR)
 
+IF(NOT DEFINED LIBEXEC_DIR)
+    SET(LIBEXEC_DIR "${CMAKE_INSTALL_PREFIX}/libexec")
+ENDIF(NOT DEFINED LIBEXEC_DIR)
+
 IF(NOT DEFINED RUN_DIR)
     SET(RUN_DIR "/var/run")
 ENDIF(NOT DEFINED RUN_DIR)
@@ -190,12 +194,12 @@ SET(VSM_UNIT_TESTS_IPC_SOCKET_PATH ${RUN_DIR}/vasum-ipc-unit-tests.socket)
 
 ADD_SUBDIRECTORY(${COMMON_FOLDER})
 ADD_SUBDIRECTORY(${LOGGER_FOLDER})
-ADD_SUBDIRECTORY(${LXCPP_FOLDER})
 IF(NOT WITHOUT_DBUS)
 ADD_SUBDIRECTORY(${DBUS_FOLDER})
 ENDIF(NOT WITHOUT_DBUS)
 ADD_SUBDIRECTORY(${CONFIG_FOLDER})
 ADD_SUBDIRECTORY(${IPC_FOLDER})
+ADD_SUBDIRECTORY(${LXCPP_FOLDER})
 ADD_SUBDIRECTORY(${CLIENT_FOLDER})
 ADD_SUBDIRECTORY(${SERVER_FOLDER})
 IF(NOT WITHOUT_DBUS)
index fbb110d..3a0dd31 100644 (file)
@@ -27,6 +27,8 @@
 
 #include "logger/logger.hpp"
 
+#include <fcntl.h>
+#include <unistd.h>
 #include <sys/socket.h>
 
 namespace {
@@ -36,10 +38,15 @@ const int RIGHT = 1;
 
 namespace utils {
 
-Channel::Channel()
+Channel::Channel(const bool closeOnExec)
     : mSocketIndex(-1)
 {
-    if (::socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, mSockets) < 0) {
+    int flags = SOCK_STREAM;
+    if (closeOnExec) {
+        flags |= SOCK_CLOEXEC;
+    };
+
+    if (::socketpair(AF_LOCAL, flags, 0, mSockets.data()) < 0) {
         const std::string msg = "socketpair() failed: " +
                                 utils::getSystemErrorMessage();
         LOGE(msg);
@@ -47,23 +54,36 @@ Channel::Channel()
     }
 }
 
+Channel::Channel(const int fd)
+    : mSocketIndex(LEFT),
+      mSockets{{fd, -1}}
+{
+    assert(fd >= 0);
+}
+
 Channel::~Channel()
 {
     closeSocket(LEFT);
     closeSocket(RIGHT);
 }
 
+/*
+ * This function has to be safe in regard to signal(7)
+ */
 void Channel::setLeft()
 {
     mSocketIndex = LEFT;
-    utils::close(mSockets[RIGHT]);
+    ::close(mSockets[RIGHT]);
     mSockets[RIGHT] = -1;
 }
 
+/*
+ * This function has to be safe in regard to signal(7)
+ */
 void Channel::setRight()
 {
     mSocketIndex = RIGHT;
-    utils::close(mSockets[LEFT]);
+    ::close(mSockets[LEFT]);
     mSockets[LEFT] = -1;
 }
 
@@ -73,6 +93,33 @@ void Channel::shutdown()
     closeSocket(mSocketIndex);
 }
 
+int Channel::getFD()
+{
+    assert(mSocketIndex != -1 && "Channel's end isn't set");
+    return mSockets[mSocketIndex];
+}
+
+int Channel::getLeftFD()
+{
+    return mSockets[LEFT];
+}
+
+int Channel::getRightFD()
+{
+    return mSockets[RIGHT];
+}
+
+void Channel::setCloseOnExec(const bool closeOnExec)
+{
+    const int fd = getFD();
+
+    if (closeOnExec) {
+        ::fcntl(fd, F_SETFD, ::fcntl(fd, F_GETFD) | FD_CLOEXEC);
+    } else {
+        ::fcntl(fd, F_SETFD, ::fcntl(fd, F_GETFD) & ~FD_CLOEXEC);
+    }
+}
+
 void Channel::closeSocket(int socketIndex)
 {
     utils::shutdown(mSockets[socketIndex]);
index f537121..3b60daa 100644 (file)
@@ -26,6 +26,8 @@
 #define COMMON_UTILS_CHANNEL_HPP
 
 #include "utils/fd-utils.hpp"
+
+#include <array>
 #include <cassert>
 
 namespace utils {
@@ -35,7 +37,8 @@ namespace utils {
  */
 class Channel {
 public:
-    Channel();
+    explicit Channel(const bool closeOnExec = true);
+    explicit Channel(const int fd);
     ~Channel();
 
     Channel(const Channel&) = delete;
@@ -72,12 +75,32 @@ public:
     template<typename Data>
     Data read();
 
+    /**
+     * Get an active file descriptor
+     */
+    int getFD();
+
+    /**
+     * Gen the left file descriptor
+     */
+    int getLeftFD();
+
+    /**
+     * Gen the right file descriptor
+     */
+    int getRightFD();
+
+    /**
+     * Sets close on exec on an active fd to either true or false
+     */
+    void setCloseOnExec(const bool closeOnExec);
+
 private:
 
     void closeSocket(int socketIndex);
 
     int mSocketIndex;
-    int mSockets[2];
+    std::array<int, 2> mSockets;
 };
 
 template<typename Data>
index 74d2def..5cbd714 100644 (file)
@@ -27,7 +27,9 @@
 
 #include "logger/backend.hpp"
 
+#include <fcntl.h>
 #include <fstream>
+#include <ext/stdio_filebuf.h>
 
 namespace logger {
 
@@ -35,7 +37,12 @@ class PersistentFileBackend : public LogBackend {
 public:
     PersistentFileBackend(const std::string& filePath) :
         mfilePath(filePath),
-        mOut(mfilePath, std::ios::app) {}
+        mOut(mfilePath, std::ios::app)
+    {
+        using filebufType = __gnu_cxx::stdio_filebuf<std::ofstream::char_type>;
+        const int fd = static_cast<filebufType*>(mOut.rdbuf())->fd();
+        ::fcntl(fd, F_SETFD, ::fcntl(fd, F_GETFD) | FD_CLOEXEC);
+    }
 
     void log(LogLevel logLevel,
              const std::string& file,
index 17bed53..06404f2 100644 (file)
@@ -26,6 +26,7 @@
 #include "logger/formatter.hpp"
 #include "utils/ccolor.hpp"
 
+#include <unistd.h>
 #include <sys/time.h>
 #include <cassert>
 #include <sstream>
@@ -39,7 +40,7 @@ namespace {
 
 const int TIME_COLUMN_LENGTH = 12;
 const int SEVERITY_COLUMN_LENGTH = 8;
-const int THREAD_COLUMN_LENGTH = 3;
+const int PROCESS_COLUMN_LENGTH = 8;
 const int FILE_COLUMN_LENGTH = 60;
 
 std::atomic<unsigned int> gNextThreadId(1);
@@ -122,7 +123,7 @@ std::string LogFormatter::getHeader(LogLevel logLevel,
     std::ostringstream logLine;
     logLine << getCurrentTime() << ' '
             << std::left << std::setw(SEVERITY_COLUMN_LENGTH) << '[' + toString(logLevel) + ']'
-            << std::right << std::setw(THREAD_COLUMN_LENGTH) << getCurrentThread() << ": "
+            << std::right << std::setw(PROCESS_COLUMN_LENGTH) << ::getpid() << "/" << getCurrentThread() << ": "
             << std::left << std::setw(FILE_COLUMN_LENGTH)
             << file + ':' + std::to_string(line) + ' ' + func + ':';
     return logLine.str();
index fd33343..99dc235 100644 (file)
@@ -33,7 +33,7 @@ namespace logger {
  * @brief Available log levels
  * @ingroup libLogger
  */
-enum class LogLevel {
+enum class LogLevel : int {
     TRACE, ///< Most detailed log level
     DEBUG, ///< Debug logs
     INFO,  ///< Information
index ec0855b..fbb85d6 100644 (file)
@@ -40,6 +40,44 @@ std::mutex gLogMutex;
 
 } // namespace
 
+void setupLogger(const LogType type,
+                 const LogLevel level,
+                 const std::string &arg)
+{
+    if (type == LogType::LOG_FILE || type == LogType::LOG_PERSISTENT_FILE) {
+        if (arg.empty()) {
+            throw std::runtime_error("Path needs to be specified in the agument");
+        }
+    }
+
+    switch(type) {
+    case LogType::LOG_NULL:
+        Logger::setLogBackend(new NullLogger());
+        break;
+#ifdef HAVE_SYSTEMD
+    case LogType::LOG_JOURNALD:
+        Logger::setLogBackend(new SystemdJournalBackend());
+        break;
+#endif
+    case LogType::LOG_FILE:
+        Logger::setLogBackend(new FileBackend(arg));
+        break;
+    case LogType::LOG_PERSISTENT_FILE:
+        Logger::setLogBackend(new PersistentFileBackend(arg));
+        break;
+    case LogType::LOG_SYSLOG:
+        Logger::setLogBackend(new SyslogBackend());
+        break;
+    case LogType::LOG_STDERR:
+        Logger::setLogBackend(new StderrBackend());
+        break;
+    default:
+        throw std::runtime_error("Bad logger type passed");
+    }
+
+    Logger::setLogLevel(level);
+}
+
 void Logger::logMessage(LogLevel logLevel,
                         const std::string& message,
                         const std::string& file,
index 0a1ebe6..92baee1 100644 (file)
 #define COMMON_LOGGER_LOGGER_HPP
 
 #include "logger/level.hpp"
+#include "logger/backend-null.hpp"
+#ifdef HAVE_SYSTEMD
+#include "logger/backend-journal.hpp"
+#endif
 #include "logger/backend-file.hpp"
-#include "logger/backend-stderr.hpp"
 #include "logger/backend-persistent-file.hpp"
+#include "logger/backend-syslog.hpp"
+#include "logger/backend-stderr.hpp"
 
 #include <sstream>
 #include <string>
 
 namespace logger {
 
+enum class LogType : int {
+    LOG_NULL,
+    LOG_JOURNALD,
+    LOG_FILE,
+    LOG_PERSISTENT_FILE,
+    LOG_SYSLOG,
+    LOG_STDERR
+};
+
+/**
+ * A helper function to easily and completely setup a new logger
+ *
+ * @param type      logger type to be set up
+ * @param level     maximum log level that will be logged
+ * @param arg       an argument used by some loggers, specific to them
+ *                  (e.g. file name for file loggers)
+ */
+void setupLogger(const LogType type,
+                 const LogLevel level,
+                 const std::string &arg = "");
+
 class LogBackend;
 
 class Logger {
index 203ddd3..8f391e8 100644 (file)
@@ -19,6 +19,9 @@
 
 PROJECT(lxcpp)
 
+SET(GUARD_CODENAME "${PROJECT_NAME}-guard")
+ADD_SUBDIRECTORY(guard)
+
 MESSAGE(STATUS "")
 MESSAGE(STATUS "Generating makefile for the liblxcpp...")
 FILE(GLOB HEADERS          *.hpp)
@@ -37,15 +40,18 @@ ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_COMMANDS})
 SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
     SOVERSION   ${_LIB_SOVERSION_}
     VERSION     ${_LIB_VERSION_}
+    COMPILE_DEFINITIONS GUARD_PATH="${LIBEXEC_DIR}/${GUARD_CODENAME}"
 )
 
 ADD_DEPENDENCIES(${PROJECT_NAME} Common Logger)
 
 ## Link libraries ##############################################################
 FIND_PACKAGE(Boost COMPONENTS system filesystem)
+PKG_CHECK_MODULES(LXCPP_DEPS REQUIRED glib-2.0)
+
 INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER})
-INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS})
-TARGET_LINK_LIBRARIES(${PROJECT_NAME} Common ${pkgs_LDFLAGS} ${Boost_LIBRARIES} Logger)
+INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS} ${LXCPP_DEPS_INCLUDE_DIRS} ${JSON_C_INCLUDE_DIRS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} Common ${pkgs_LDFLAGS} ${Boost_LIBRARIES} Logger Config)
 
 ## Generate the pc file ########################################################
 CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
index 01a811b..419f651 100644 (file)
@@ -167,6 +167,8 @@ void Attach::execute()
         parent(intermChannel, interPid);
         intermChannel.shutdown();
     } else {
+        // TODO: only safe functions mentioned in signal(7) should be used below.
+        // This is not the case now, needs fixing.
         intermChannel.setRight();
         interm(intermChannel, call);
         intermChannel.shutdown();
diff --git a/libs/lxcpp/commands/start.cpp b/libs/lxcpp/commands/start.cpp
new file mode 100644 (file)
index 0000000..f68654a
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   Implementation of starting a container
+ */
+
+#include "lxcpp/commands/start.hpp"
+#include "lxcpp/exception.hpp"
+#include "lxcpp/process.hpp"
+#include "lxcpp/utils.hpp"
+
+#include "logger/logger.hpp"
+#include "config/manager.hpp"
+
+#include <unistd.h>
+
+
+namespace lxcpp {
+
+
+Start::Start(ContainerConfig &config)
+    : mConfig(config),
+      mChannel(false),
+      mGuardPath(GUARD_PATH),
+      mChannelFD(std::to_string(mChannel.getRightFD()))
+{
+}
+
+Start::~Start()
+{
+}
+
+void Start::execute()
+{
+    LOGD("Forking daemonize and guard processes. Execing guard libexec binary.");
+    LOGD("Logging will cease now. It should be restored using some new facility in the guard process.");
+    const pid_t pid = lxcpp::fork();
+
+    if (pid > 0) {
+        mChannel.setLeft();
+        parent(pid);
+    } else {
+        // Below this point only safe functions mentioned in signal(7) are allowed.
+        mChannel.setRight();
+        daemonize();
+        ::_exit(EXIT_FAILURE);
+    }
+}
+
+void Start::parent(const pid_t pid)
+{
+    int status = lxcpp::waitpid(pid);
+    if (status != EXIT_SUCCESS) {
+        const std::string msg = "Problem with a daemonize process";
+        LOGE(msg);
+        throw ProcessSetupException(msg);
+    }
+
+    // Send the config to the guard process
+    config::saveToFD(mChannel.getFD(), mConfig);
+
+    // Read the pids from the guard process
+    mConfig.mGuardPid = mChannel.read<pid_t>();
+    mConfig.mInitPid = mChannel.read<pid_t>();
+
+    mChannel.shutdown();
+
+    if (mConfig.mGuardPid <= 0 || mConfig.mInitPid <= 0) {
+        const std::string msg = "Problem with receiving pids";
+        LOGE(msg);
+        throw ProcessSetupException(msg);
+    }
+
+    LOGD("Guard PID: " << mConfig.mGuardPid);
+    LOGD("Init PID: " << mConfig.mInitPid);
+}
+
+void Start::daemonize()
+{
+    // Double fork() with exit() to reattach the process under the host's init
+    pid_t pid = ::fork();
+    if (pid < 0) {
+        ::_exit(EXIT_FAILURE);
+    }
+
+    if (pid == 0) {
+        // Prepare a clean environment for a guard process:
+        // - chdir to / so it's independent on other directories
+        // - null std* fds so it's properly dettached from the terminal
+        // - set a new session so it cannot reacquire a terminal
+
+        if (::chdir("/") < 0) {
+            ::_exit(EXIT_FAILURE);
+        }
+        if (nullStdFDs() <0) {
+            ::_exit(EXIT_FAILURE);
+        }
+        if (::setsid() < 0) {
+            ::_exit(EXIT_FAILURE);
+        }
+
+        // Add name and path of the container to argv. They are not used, but will
+        // identify the container in the process list in case setProcTitle() fails
+        // and will guarantee we have enough argv memory to write the title we want.
+        const char *argv[] = {mGuardPath.c_str(),
+                              mChannelFD.c_str(),
+                              mConfig.mName.c_str(),
+                              mConfig.mRootPath.c_str(),
+                              NULL};
+        ::execve(argv[0], const_cast<char *const*>(argv), NULL);
+        ::_exit(EXIT_FAILURE);
+    }
+
+    ::_exit(EXIT_SUCCESS);
+}
+
+
+} // namespace lxcpp
diff --git a/libs/lxcpp/commands/start.hpp b/libs/lxcpp/commands/start.hpp
new file mode 100644 (file)
index 0000000..758b471
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   Headers for starting a container
+ */
+
+#ifndef LXCPP_COMMANDS_START_HPP
+#define LXCPP_COMMANDS_START_HPP
+
+#include "lxcpp/commands/command.hpp"
+#include "lxcpp/container-config.hpp"
+#include "utils/channel.hpp"
+
+#include <sys/types.h>
+
+
+namespace lxcpp {
+
+
+class Start final: Command {
+public:
+    /**
+     * Starts the container
+     *
+     * In more details it prepares an environment for a guard process,
+     * starts it, and passes it the configuration through a file descriptor.
+     *
+     * @param config container's config
+     */
+    Start(ContainerConfig &config);
+    ~Start();
+
+    void execute();
+
+private:
+    ContainerConfig &mConfig;
+    utils::Channel mChannel;
+    std::string mGuardPath;
+    std::string mChannelFD;
+
+    void parent(const pid_t pid);
+    void daemonize();
+};
+
+
+} // namespace lxcpp
+
+
+#endif // LXCPP_COMMANDS_START_HPP
diff --git a/libs/lxcpp/container-config.hpp b/libs/lxcpp/container-config.hpp
new file mode 100644 (file)
index 0000000..9caad96
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   A definition of a ContainerConfig struct
+ */
+
+#ifndef LXCPP_CONTAINER_CONFIG_HPP
+#define LXCPP_CONTAINER_CONFIG_HPP
+
+#include "lxcpp/logger-config.hpp"
+
+#include <config/config.hpp>
+#include <config/fields.hpp>
+
+#include <string>
+#include <vector>
+#include <sys/types.h>
+
+
+namespace lxcpp {
+
+
+struct ContainerConfig {
+    /**
+     * Name of the container.
+     *
+     * Set: by constructor, cannot be changed afterwards.
+     * Get: getName()
+     */
+    std::string mName;
+
+    /**
+     * Path of the root directory of the container.
+     *
+     * Set: by contstructor, cannot be changed afterwards.
+     * Get: getRootPath()
+     */
+    std::string mRootPath;
+
+    /**
+     * Pid of the guard process.
+     *
+     * Set: automatically by the guard process itself.
+     * Get: getGuardPid()
+     */
+    pid_t mGuardPid;
+
+    /**
+     * Pid of the container's init process.
+     *
+     * Set: automatically by the guard process.
+     * Get: getInitPid()
+     */
+    pid_t mInitPid;
+
+    /**
+     * Argv of the container's init process to be executed.
+     * The path has to be relative to the RootPath.
+     *
+     * Set: setInit()
+     * Get: getInit()
+     */
+    std::vector<std::string> mInit;
+
+    /**
+     * Logger to be configured inside the guard process. This logger
+     * reconfiguration is due to the fact that guard looses standard file
+     * descriptors and might loose access to other files by mount namespace
+     * usage. Hence an option to set some other logger that will work
+     * regardless. E.g. PersistentFile.
+     *
+     * Set: setLogger()
+     * Get: none
+     */
+    LoggerConfig mLogger;
+
+    ContainerConfig() : mGuardPid(-1), mInitPid(-1) {}
+
+    CONFIG_REGISTER
+    (
+        mName,
+        mRootPath,
+        mGuardPid,
+        mInitPid,
+        mInit,
+        mLogger
+    )
+};
+
+
+}
+
+
+#endif // LXCPP_CONTAINER_CONFIG_HPP
index 8249009..cee6fa4 100644 (file)
@@ -28,6 +28,7 @@
 #include "lxcpp/namespace.hpp"
 #include "lxcpp/capability.hpp"
 #include "lxcpp/commands/attach.hpp"
+#include "lxcpp/commands/start.hpp"
 
 #include "logger/logger.hpp"
 #include "utils/exception.hpp"
@@ -120,9 +121,18 @@ pid_t ContainerImpl::getInitPid() const
     return mConfig.mInitPid;
 }
 
+void ContainerImpl::setLogger(const logger::LogType type,
+                              const logger::LogLevel level,
+                              const std::string &arg)
+{
+    mConfig.mLogger.set(type, level, arg);
+}
+
 void ContainerImpl::start()
 {
-    throw NotImplementedException();
+    // TODO: check config consistency and completeness somehow
+    Start start(mConfig);
+    start.execute();
 }
 
 void ContainerImpl::stop()
index 5435d49..1bf44b7 100644 (file)
 #define LXCPP_CONTAINER_IMPL_HPP
 
 #include <sys/types.h>
-#include <config/config.hpp>
-#include <config/fields.hpp>
 #include <memory>
 
+#include "lxcpp/container-config.hpp"
 #include "lxcpp/container.hpp"
 #include "lxcpp/namespace.hpp"
 #include "lxcpp/network.hpp"
 
-#include "utils/channel.hpp"
-
 namespace lxcpp {
 
-struct ContainerConfig {
-    std::string mName;
-    std::string mRootPath;
-    pid_t mGuardPid;
-    pid_t mInitPid;
-    std::vector<std::string> mInit;
-
-    ContainerConfig() : mGuardPid(-1), mInitPid(-1) {}
-
-    CONFIG_REGISTER
-    (
-        mName,
-        mRootPath,
-        mGuardPid,
-        mInitPid,
-        mInit
-    )
-};
-
 
 class ContainerImpl : public virtual Container {
 public:
@@ -73,6 +51,10 @@ public:
     const std::vector<std::string>& getInit();
     void setInit(const std::vector<std::string> &init);
 
+    void setLogger(const logger::LogType type,
+                   const logger::LogLevel level,
+                   const std::string &arg);
+
     const std::vector<Namespace>& getNamespaces() const;
 
     // Execution actions
index 192f2df..88c3ff8 100644 (file)
@@ -25,6 +25,7 @@
 #define LXCPP_CONTAINER_HPP
 
 #include "lxcpp/network-config.hpp"
+#include "lxcpp/logger-config.hpp"
 #include <sys/types.h>
 
 #include <string>
@@ -60,6 +61,10 @@ public:
     virtual const std::vector<std::string>& getInit() = 0;
     virtual void setInit(const std::vector<std::string> &init) = 0;
 
+    virtual void setLogger(const logger::LogType type,
+                           const logger::LogLevel level,
+                           const std::string &arg) = 0;
+
     // Execution actions
     virtual void start() = 0;
     virtual void stop() = 0;
index 7764282..11a6a0e 100644 (file)
@@ -66,6 +66,11 @@ struct CapabilitySetupException: public Exception {
         : Exception(message) {}
 };
 
+struct UtilityException: public Exception {
+    explicit UtilityException(const std::string& message = "Error during an utility operation")
+        : Exception(message) {}
+};
+
 struct BadArgument: public Exception {
     explicit BadArgument(const std::string& message = "Bad argument passed")
         : Exception(message) {}
diff --git a/libs/lxcpp/guard/CMakeLists.txt b/libs/lxcpp/guard/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6c69300
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+#
+# @file   CMakeLists.txt
+# @author Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+#
+
+MESSAGE(STATUS "")
+MESSAGE(STATUS "Generating makefile for the Guard...")
+FILE(GLOB      guard_SRCS *.cpp *.hpp)
+
+
+## Setup target ################################################################
+ADD_EXECUTABLE(${GUARD_CODENAME} ${guard_SRCS})
+
+
+## Link libraries ##############################################################
+PKG_CHECK_MODULES(GUARD_DEPS REQUIRED glib-2.0)
+INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER})
+INCLUDE_DIRECTORIES(SYSTEM ${GUARD_DEPS_INCLUDE_DIRS} ${JSON_C_INCLUDE_DIRS})
+TARGET_LINK_LIBRARIES(${GUARD_CODENAME} lxcpp)
+
+
+## Install #####################################################################
+INSTALL(TARGETS ${GUARD_CODENAME} DESTINATION ${LIBEXEC_DIR})
diff --git a/libs/lxcpp/guard/guard.cpp b/libs/lxcpp/guard/guard.cpp
new file mode 100644 (file)
index 0000000..63c7263
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   LXCPP guard process implementation
+ */
+
+#include "lxcpp/utils.hpp"
+#include "lxcpp/guard/guard.hpp"
+#include "lxcpp/process.hpp"
+
+#include "config/manager.hpp"
+#include "logger/logger.hpp"
+
+#include <unistd.h>
+#include <sys/wait.h>
+
+
+namespace lxcpp {
+
+
+void startContainer(const ContainerConfig &cfg)
+{
+    std::vector<char const *> argv;
+    argv.reserve(cfg.mInit.size() + 1);
+    for (auto const & it : cfg.mInit) {
+        argv.push_back(it.c_str());
+    }
+    argv.push_back(nullptr);
+
+    LOGD("Executing container's init: " << argv[0]);
+    ::execve(argv[0], const_cast<char *const*>(argv.data()), NULL);
+    ::_exit(EXIT_FAILURE);
+}
+
+int startGuard(int channelFD)
+{
+    ContainerConfig cfg;
+    utils::Channel channel(channelFD);
+    channel.setCloseOnExec(true);
+    config::loadFromFD(channel.getFD(), cfg);
+
+    logger::setupLogger(cfg.mLogger.getType(),
+                        cfg.mLogger.getLevel(),
+                        cfg.mLogger.getArg());
+
+    LOGD("Guard started, config & logging restored");
+
+    try {
+        LOGD("Setting the guard process title");
+        const std::string title = "[LXCPP] " + cfg.mName + " " + cfg.mRootPath;
+        setProcTitle(title);
+    } catch (std::exception &e) {
+        // Ignore, this is optional
+        LOGW("Failed to set the guard process title");
+    }
+
+    // TODO: container preparation part 1
+
+    // TODO: switch to clone
+    LOGD("Forking container's init process");
+    pid_t pid = lxcpp::fork();
+
+    if (pid == 0) {
+        // TODO: container preparation part 2
+
+        startContainer(cfg);
+        ::_exit(EXIT_FAILURE);
+    }
+
+    cfg.mGuardPid = ::getpid();
+    cfg.mInitPid = pid;
+
+    channel.write(cfg.mGuardPid);
+    channel.write(cfg.mInitPid);
+    channel.shutdown();
+
+    int status = lxcpp::waitpid(pid);
+    LOGD("Init exited with status: " << status);
+    return status;
+}
+
+
+} // namespace lxcpp
diff --git a/libs/lxcpp/guard/guard.hpp b/libs/lxcpp/guard/guard.hpp
new file mode 100644 (file)
index 0000000..78457ae
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   LXCPP guard process header
+ */
+
+#ifndef LXCPP_GUARD_GUARD_HPP
+#define LXCPP_GUARD_GUARD_HPP
+
+
+#include "lxcpp/container-config.hpp"
+#include "utils/channel.hpp"
+
+namespace lxcpp {
+
+
+int startGuard(int channelFD);
+
+
+} // namespace lxcpp
+
+#endif // LXCPP_GUARD_HPP
diff --git a/libs/lxcpp/guard/main.cpp b/libs/lxcpp/guard/main.cpp
new file mode 100644 (file)
index 0000000..c8db30b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   Main file for the Guard process (libexec)
+ */
+
+#include "lxcpp/guard/guard.hpp"
+
+#include "utils/fd-utils.hpp"
+
+#include <iostream>
+#include <unistd.h>
+
+int main(int argc, char *argv[])
+{
+    if (argc == 1) {
+        std::cout << "This file should not be executed by hand" << std::endl;
+        ::_exit(EXIT_FAILURE);
+    }
+
+    // NOTE: this might not be required now, but I leave it here not to forget.
+    // We need to investigate this with vasum and think about possibility of
+    // poorly written software that leaks file descriptors and might use LXCPP.
+#if 0
+    for(int fd = 3; fd < ::sysconf(_SC_OPEN_MAX); ++fd) {
+        utils::close(fd);
+    }
+#endif
+
+    int fd = std::stoi(argv[1]);
+    return lxcpp::startGuard(fd);
+}
diff --git a/libs/lxcpp/logger-config.cpp b/libs/lxcpp/logger-config.cpp
new file mode 100644 (file)
index 0000000..3aed302
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsumg.com)
+ * @brief   Logger configuration
+ */
+
+#include "lxcpp/logger-config.hpp"
+#include "lxcpp/exception.hpp"
+
+namespace lxcpp {
+
+
+void LoggerConfig::set(const logger::LogType type,
+                       const logger::LogLevel level,
+                       const std::string &arg)
+{
+    if (type == logger::LogType::LOG_FILE || type == logger::LogType::LOG_PERSISTENT_FILE) {
+        if (arg.empty()) {
+            const std::string msg = "Path needs to be specified in the agument";
+            LOGE(msg);
+            throw BadArgument(msg);
+        }
+    }
+
+    mType = static_cast<int>(type);
+    mLevel = static_cast<int>(level);
+    mArg = arg;
+}
+
+
+} //namespace lxcpp
diff --git a/libs/lxcpp/logger-config.hpp b/libs/lxcpp/logger-config.hpp
new file mode 100644 (file)
index 0000000..8312623
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsumg.com)
+ * @brief   Logger configuration
+ */
+
+#ifndef LXCPP_LOGGER_CONFIG_HPP
+#define LXCPP_LOGGER_CONFIG_HPP
+
+#include "config/config.hpp"
+#include "config/fields.hpp"
+#include "logger/logger.hpp"
+
+
+namespace lxcpp {
+
+
+/**
+ * Logger configuration
+ */
+struct LoggerConfig {
+private:
+    int mType;
+    int mLevel;
+    std::string mArg;
+
+public:
+    void set(const logger::LogType type,
+             const logger::LogLevel level,
+             const std::string &arg = "");
+
+    logger::LogType getType() const {return static_cast<logger::LogType>(mType);}
+    logger::LogLevel getLevel() const {return static_cast<logger::LogLevel>(mLevel);}
+    std::string getArg() const {return mArg;}
+
+    CONFIG_REGISTER
+    (
+        mType,
+        mLevel,
+        mArg
+    )
+};
+
+
+} //namespace lxcpp
+
+
+#endif // LXCPP_LOGGER_CONFIG_HPP
diff --git a/libs/lxcpp/utils.cpp b/libs/lxcpp/utils.cpp
new file mode 100644 (file)
index 0000000..41e76b5
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   LXCPP utils implementation
+ */
+
+#include "lxcpp/exception.hpp"
+
+#include "logger/logger.hpp"
+#include "utils/fd-utils.hpp"
+#include "utils/exception.hpp"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <iostream>
+#include <iterator>
+#include <unistd.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+
+namespace lxcpp {
+
+
+/*
+ * This function has to be safe in regard to signal(7)
+ */
+int nullStdFDs()
+{
+    int ret = -1;
+
+    int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR | O_CLOEXEC));
+    if (fd == -1) {
+        goto err;
+    }
+
+    if (-1 == TEMP_FAILURE_RETRY(::dup2(fd, STDIN_FILENO))) {
+        goto err_close;
+    }
+
+    if (-1 == TEMP_FAILURE_RETRY(::dup2(fd, STDOUT_FILENO))) {
+        goto err_close;
+    }
+
+    if (-1 == TEMP_FAILURE_RETRY(::dup2(fd, STDERR_FILENO))) {
+        goto err_close;
+    }
+
+    if (-1 == TEMP_FAILURE_RETRY(::close(fd))) {
+        goto err_close;
+    }
+
+    return 0;
+
+err_close:
+    TEMP_FAILURE_RETRY(::close(fd));
+err:
+    return ret;
+}
+
+void setProcTitle(const std::string &title)
+{
+    std::ifstream f("/proc/self/stat");
+    auto it = std::istream_iterator<std::string>(f);
+
+    // Skip the first 47 fields, entries 48-49 are ARG_START and ARG_END.
+    std::advance(it, 47);
+    unsigned long argStart = std::stol(*it++);
+    unsigned long argEnd = std::stol(*it++);
+
+    f.close();
+
+    char *mem = reinterpret_cast<char*>(argStart);
+    ptrdiff_t oldLen = argEnd - argStart;
+
+    // Include the null byte here, because in the calculations below we want to have room for it.
+    size_t newLen = title.length() + 1;
+
+    // We shouldn't use more then we have available. Hopefully that should be enough.
+    if ((long)newLen > oldLen) {
+        newLen = oldLen;
+    } else {
+        argEnd = argStart + newLen;
+    }
+
+    // Sanity check
+    if (argEnd < newLen || argEnd < argStart) {
+        std::string msg = "setProcTitle() failed: " + utils::getSystemErrorMessage();
+        LOGE(msg);
+        throw UtilityException(msg);
+    }
+
+    // Let's try to set the memory range properly (this requires capabilities)
+    if (::prctl(PR_SET_MM, PR_SET_MM_ARG_END, argEnd, 0, 0) < 0) {
+        // If that failed let's fall back to the poor man's version, just zero the memory we already have.
+        ::bzero(mem, oldLen);
+    }
+
+    ::strcpy(mem, title.c_str());
+}
+
+
+} // namespace lxcpp
diff --git a/libs/lxcpp/utils.hpp b/libs/lxcpp/utils.hpp
new file mode 100644 (file)
index 0000000..2a59303
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License version 2.1 as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @author  Lukasz Pawelczyk (l.pawelczyk@samsung.com)
+ * @brief   LXCPP utils headers
+ */
+
+#ifndef LXCPP_UTILS_HPP
+#define LXCPP_UTILS_HPP
+
+#include <string>
+
+
+namespace lxcpp {
+
+/**
+ * Nullifies all standard file descriptors (stdin, stdout, stderr)
+ * replacing them with file descriptor to /dev/null. Used to
+ * as a part of a process to detach a process from a control terminal.
+ *
+ * This function has to be safe in regard to signal(7)
+ *
+ * @returns an error code in case of failure.
+ */
+int nullStdFDs();
+
+/**
+ * Changes the tittle of a current process title (e.g. visible in ps tool).
+ *
+ * @param title  A new tittle to be set
+ */
+void setProcTitle(const std::string &title);
+
+
+} // namespace lxcpp
+
+
+#endif // LXCPP_START_HPP
index 2cd5dd8..7709ab4 100644 (file)
@@ -487,6 +487,7 @@ The package provides liblxcpp library.
 %files -n liblxcpp
 %defattr(644,root,root,755)
 %{_libdir}/liblxcpp.so.0
+%attr(755,root,root) %{_libexecdir}/lxcpp-guard
 %attr(755,root,root) %{_libdir}/liblxcpp.so.%{version}
 
 %package -n liblxcpp-devel