Integration fixes & features 68/58768/18
authorKrzysztof Dynowski <k.dynowski@samsung.com>
Tue, 2 Feb 2016 12:35:32 +0000 (13:35 +0100)
committerKrzysztof Dynowski <k.dynowski@samsung.com>
Thu, 25 Feb 2016 10:44:40 +0000 (11:44 +0100)
[Feature]       removeDir, backtrace
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, install, run tests.

Change-Id: Ie91a6c3b50ff5628c40b70d37685f3022ef7915b

common/utils/channel.hpp
common/utils/exception.cpp
common/utils/exception.hpp
common/utils/execute.cpp
common/utils/fs.cpp
common/utils/fs.hpp
common/utils/scoped-dir.cpp
libs/cargo-ipc/internals/socket.cpp
libs/cargo-validator/internals/validator-visitor.hpp
libs/cargo/libcargo.pc.in
tests/unit_tests/epoll/ut-event-poll.cpp

index 3b60daac238c7fb6b8f091dba969858ec38da263..361cf266d7d59ec947775b0a4251f32d7adf6d00 100644 (file)
@@ -73,7 +73,7 @@ public:
      * Receive data of a given type (size)
      */
     template<typename Data>
-    Data read();
+    Data read(unsigned timeoutMS = 5000);
 
     /**
      * Get an active file descriptor
@@ -112,12 +112,12 @@ void Channel::write(const Data& data)
 }
 
 template<typename Data>
-Data Channel::read()
+Data Channel::read(unsigned timeoutMS)
 {
     assert(mSocketIndex != -1 && "Channel's end isn't set");
 
     Data data;
-    utils::read(mSockets[mSocketIndex], &data, sizeof(Data));
+    utils::read(mSockets[mSocketIndex], &data, sizeof(Data), timeoutMS);
     return data;
 }
 
index 7d2a6ac6fe19586f82ad450b0e044cd0d59acee8..239fa75882189046296d2ae8045062ae76259043 100644 (file)
 #include <string>
 #include <cstring>
 #include <cerrno>
+#include <memory>
+
+#include <execinfo.h>
+#include <cxxabi.h>
+
+namespace {
+
+std::string demangle(const std::string& name)
+{
+    int status;
+    std::string ret;
+    char *realname = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+
+    if (status == 0) {
+        ret.append(realname);
+        free(realname);
+    } else {
+        ret.append(name);
+    }
+
+    return ret;
+}
+
+}
 
 namespace utils {
 
 const int ERROR_MESSAGE_BUFFER_CAPACITY = 256;
+const int STACK_FETCH_DEPTH = 50;
 
 std::string getSystemErrorMessage()
 {
@@ -45,4 +70,44 @@ std::string getSystemErrorMessage(int err)
     return strerror_r(err, buf, sizeof(buf));
 }
 
+void fillInStackTrace(std::vector<std::string>& bt)
+{
+    void *frames[STACK_FETCH_DEPTH];
+    size_t size;
+    size = ::backtrace(frames, sizeof(frames)/sizeof(*frames));
+    char** symbollist = ::backtrace_symbols(frames, size);
+
+    // skip 2 stack frames:
+    //   [0] = fillInStackTrace
+    //   [1] = Exception constructor
+    //   ---------------------------
+    //   [2] = location where Exception was created
+    for (size_t i = 2; i < size; i++) {
+        int namePos = 0; // offsets to tokens found in symbol
+        int offsPos = 0;
+        int tailPos = 0;
+
+        for (char *p = symbollist[i]; *p != 0; ++p) {
+            if (*p == '(') {
+                namePos = p - symbollist[i] + 1;
+            } else if (*p == '+') {
+                offsPos = p - symbollist[i] + 1;
+            } else if (*p == ')' && offsPos) {
+                tailPos = p - symbollist[i] + 1;
+                break;
+            }
+        }
+
+        const std::string& item = symbollist[i];
+        if (namePos && tailPos && namePos < offsPos) {
+            std::string demangled = demangle(item.substr(namePos, offsPos - namePos - 1));
+            bt.push_back(item.substr(0, namePos - 1) + ":" + demangled + "+" + item.substr(offsPos));
+        } else {
+            // add whole line as is.
+            bt.push_back(item);
+        }
+    }
+    ::free(symbollist);
+}
+
 } // namespace utils
index 18a87cabee42af88d7ccde564cdbe248adcdb400..4bda68b34a4af5c3b6c5b4485ef448b1b101ac73 100644 (file)
@@ -27,6 +27,7 @@
 #define COMMON_UTILS_EXCEPTION_HPP
 
 #include "logger/logger.hpp"
+#include <vector>
 #include <stdexcept>
 
 namespace utils {
@@ -114,6 +115,8 @@ struct EventFDException: public UtilsException {
 };
 
 
+void fillInStackTrace(std::vector<std::string>& bt);
+
 } // namespace utils
 
 #endif // COMMON_UTILS_EXCEPTION_HPP
index 014ee812fc681f00633b2026151f18ad527134b4..bd022fb2a1adb3071a810c87de1d2db668fbb26a 100644 (file)
@@ -28,6 +28,8 @@
 #include "utils/execute.hpp"
 #include "logger/logger.hpp"
 
+#include <iostream>
+
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/wait.h>
@@ -98,20 +100,21 @@ bool executeAndWait(const std::function<void()>& func)
 
 bool executeAndWait(const uid_t uid, const char* fname, const char* const* argv, int& status)
 {
-    LOGD("Execute " << fname << argv);
+    LOGD("Execute " << (uid == UNSPEC_UID ? "" : "as " + std::to_string(uid) + " ") << fname << argv);
 
     pid_t pid = ::fork();
     if (pid == -1) {
         LOGE("Fork failed: " << getSystemErrorMessage());
         return false;
     }
+
     if (pid == 0) {
         if (uid != UNSPEC_UID && ::setuid(uid) < 0) {
             LOGW("Failed to become uid(" << uid << "): " << getSystemErrorMessage());
             ::_exit(EXIT_FAILURE);
         }
         ::execv(fname, const_cast<char* const*>(argv));
-        LOGE("execv failed: " << getSystemErrorMessage());
+        LOGE("execv(" << fname << ") failed: " << getSystemErrorMessage());
         ::_exit(EXIT_FAILURE);
     }
     return waitPid(pid, status);
@@ -119,7 +122,7 @@ bool executeAndWait(const uid_t uid, const char* fname, const char* const* argv,
 
 bool executeAndWait(const char* fname, const char* const* argv, int& status)
 {
-    return executeAndWait(-1, fname, argv, status);
+    return executeAndWait(UNSPEC_UID, fname, argv, status);
 }
 
 bool executeAndWait(const char* fname, const char* const* argv)
@@ -133,6 +136,8 @@ bool executeAndWait(const char* fname, const char* const* argv)
 
 bool waitPid(pid_t pid, int& status)
 {
+    LOGD("Wait pid " << pid);
+
     while (::waitpid(pid, &status, 0) == -1) {
         if (errno != EINTR) {
             LOGE("waitpid() failed: " << getSystemErrorMessage());
index dcd970a8079be06620a82e3ffb7568aa87a4f098..14469607a4480be805bc04e859306fe778ccb544 100644 (file)
@@ -51,6 +51,17 @@ namespace fs = boost::filesystem;
 
 namespace utils {
 
+namespace {
+
+class ScopedDirStruct final {
+public:
+    ScopedDirStruct(DIR *d) : dir(d) { }
+    ~ScopedDirStruct() { closedir(dir); }
+private:
+    DIR *dir;
+};
+
+}
 
 // ------------------- syscalls -------------------
 bool remove(const std::string& path)
@@ -112,17 +123,19 @@ void mount(const std::string& source,
                       mountflags,
                       data.c_str());
     if (ret == -1) {
-        THROW_UTILS_EXCEPTION_ERRNO_E("Mount operation failure: source path: "
+        THROW_UTILS_EXCEPTION_ERRNO_E("Mount failed: source='"
              << source
-             << ", target path: "
+             << "' target='"
              << target
-             << ", filesystemtype: "
+             << "' filesystemtype='"
              << filesystemtype
-             << ", mountflags: "
+             << "' mountflags="
              << std::to_string(mountflags)
-             << ", data: "
-             << data, errno);
+             << " data='"
+             << data
+             << "'", errno);
     }
+    LOGD("mounted " << source << " on " << target << " " << filesystemtype << " (" << mountflags << ")");
 }
 
 void umount(const std::string& path, int flags)
@@ -292,33 +305,93 @@ void readFirstLineOfFile(const std::string& path, std::string& ret)
     }
 }
 
-bool exists(const std::string& path, int inodeType)
+bool removeDir(const std::string& path)
 {
+    //1. try rmdir, in case it is already empty or does not exist
     try {
-        assertExists(path, inodeType);
-        return true;
-    } catch(const UtilsException &) {
-        return false;
+        return utils::rmdir(path);
+    } catch (const UtilsException& e) {
+        if (e.mErrno != ENOTEMPTY && e.mErrno != EBUSY) {
+            throw;
+        }
+    }
+
+    //2. not empty, do recursion
+    struct dirent storeEntry;
+    struct dirent *entry;
+    DIR *dir = ::opendir(path.c_str());
+
+    if (dir == NULL) {
+        if (errno == ENOENT) {
+            LOGD(path << ": was removed by other process.");
+            return false;
+        }
+        THROW_UTILS_EXCEPTION_E(path);
+    }
+
+    { //bracket to call scope destructor
+        ScopedDirStruct scopedir(dir);
+        while (::readdir_r(dir, &storeEntry, &entry) == 0 && entry != NULL) {
+            if (::strcmp(entry->d_name, ".") == 0 || ::strcmp(entry->d_name, "..") == 0) {
+                 continue;
+            }
+
+            std::string newpath = path + "/" + entry->d_name;
+            if (entry->d_type == DT_DIR) {
+                removeDir(newpath);
+            } else {
+                try {
+                    utils::remove(newpath.c_str());
+                } catch (const UtilsException&) {
+                    // Ignore any errors on file deletion
+                    // Error from rmdir on parent directory (in next step) will be returned anyway
+                    // Note: rmdir can be successful even if directory is not empty (like for cgroup filesystem)
+                    //       but first all child directories must be removed
+                }
+            }
+        }
     }
+
+    utils::rmdir(path);
+    LOGD(path << ": successfuly removed.");
+    return true;
 }
 
 void assertExists(const std::string& path, int inodeType)
+{
+    if (!utils::exists(path, inodeType)) {
+        THROW_UTILS_EXCEPTION_E(path + ": not exists");
+    }
+}
+
+bool exists(const std::string& path, int inodeType)
 {
     if (path.empty()) {
         THROW_UTILS_EXCEPTION_E("Empty path");
     }
 
-    struct stat s = utils::stat(path);
+    struct ::stat s;
+    try {
+        s = utils::stat(path);
+    } catch (const UtilsException& e) {
+        if (e.mErrno == ENOENT) {
+            return false;
+        }
+        throw;
+    }
+
     if (inodeType != 0) {
         if (!(s.st_mode & inodeType)) {
-            THROW_UTILS_EXCEPTION_E(path << ": not an expected inode type, expected: " << std::to_string(inodeType) <<
-                                  ", while actual: " << std::to_string(s.st_mode));
+            LOGE(path << ": wrong inodeType, expected: " << std::to_string(inodeType) <<
+                                  ", actual: " << std::to_string(s.st_mode));
+            return false;
         }
 
         if (inodeType == S_IFDIR && !utils::access(path, X_OK)) {
             THROW_UTILS_EXCEPTION_ERRNO_E(path << ": not a traversable directory", errno);
         }
     }
+    return true;
 }
 
 bool isCharDevice(const std::string& path)
@@ -501,11 +574,6 @@ bool copyDirContentsRec(const boost::filesystem::path& src, const boost::filesys
     return false;
 }
 
-boost::filesystem::perms getPerms(const mode_t& mode)
-{
-    return static_cast<boost::filesystem::perms>(mode);
-}
-
 } // namespace
 
 void copyDirContents(const std::string& src, const std::string& dst)
@@ -548,31 +616,62 @@ void createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem:
 
 void createDirs(const std::string& path, mode_t mode)
 {
-    const boost::filesystem::perms perms = getPerms(mode);
-    std::vector<fs::path> dirsCreated;
-    fs::path prefix;
-    const fs::path dirPath = fs::path(path);
-    for (const auto& dirSegment : dirPath) {
-        prefix /= dirSegment;
-        if (!fs::exists(prefix)) {
-            try {
-                createDir(prefix.string(), -1, -1, perms);
-                dirsCreated.push_back(prefix);
-            } catch(...) {
-                LOGE("Failed to create dir: " << prefix);
-                // undo
-                for (auto iter = dirsCreated.rbegin(); iter != dirsCreated.rend(); ++iter) {
-                    boost::system::error_code errorCode;
-                    fs::remove(*iter, errorCode);
-                    if (errorCode) {
-                        LOGE("Error during cleaning: dir: " << *iter
-                             << ", msg: " << errorCode.message());
-                    }
-                }
-                throw;
+    std::vector<std::string> created;
+
+    try {
+        std::string prefix = utils::beginsWith(path, "/") ? "" : ".";
+        std::vector<std::string> segments = utils::split(path, "/");
+
+        for (const auto& seg : segments) {
+            if (seg.empty()) {
+                continue;
+            }
+
+            prefix += "/" + seg;
+            if (utils::mkdir(prefix, mode)) {
+                created.push_back(prefix);
+                LOGI("dir created: " << prefix);
+            }
+        }
+    } catch (...) {
+        try {
+            for (auto iter = created.rbegin(); iter != created.rend(); ++iter) {
+                utils::rmdir(*iter);
+            }
+        } catch(...) {
+            LOGE("Failed to undo created dirs after an error");
+        }
+    }
+}
+
+void chownDir(const std::string& path, uid_t uid, uid_t gid)
+{
+    struct dirent storeEntry;
+    struct dirent *entry;
+    DIR *dir = ::opendir(path.c_str());
+
+    if (dir == NULL) {
+        THROW_UTILS_EXCEPTION_ERRNO_E(path, errno);
+    }
+
+    { //bracket to call scope destructor
+        ScopedDirStruct scopedir(dir);
+        while (::readdir_r(dir, &storeEntry, &entry) == 0 && entry != NULL) {
+            if (::strcmp(entry->d_name, ".") == 0 || ::strcmp(entry->d_name, "..") == 0) {
+                 continue;
+            }
+
+            std::string newpath = path + "/" + entry->d_name;
+            if (entry->d_type == DT_DIR) {
+                chownDir(newpath, uid, gid);
+            } else {
+                utils::lchown(newpath, uid, gid);
             }
         }
     }
+
+    utils::lchown(path, uid, gid);
+    LOGI(path << ": successfuly chown.");
 }
 
 void createEmptyDir(const std::string& path)
index f08f3c8e4c56f30b38b8b6717e290833b3d3ec29..8ff8dfc9c5e804ae9204de0d8036a74ff5c44053 100644 (file)
@@ -91,6 +91,14 @@ void saveFileContent(const std::string& path, const std::string& content);
  */
 void readFirstLineOfFile(const std::string& path, std::string& ret);
 
+/**
+ * Remove directory and its content. Throws exception on error
+ * @return:
+ *    true if directory sucessfuly removed
+ *    false if directory does not exist
+ */
+bool removeDir(const std::string& path);
+
 /**
  * Checks if a path exists and points to an expected item type.
  * @return: true if exists and is a directory, false otherwise
@@ -191,10 +199,15 @@ void copyDirContents(const std::string& src, const std::string& dst);
 void createDir(const std::string& path, uid_t uid, uid_t gid, boost::filesystem::perms mode);
 
 /**
- * Recursively creates a directory with specific permissions set.
+ * Creates a path directories with specific permissions set.
  * Throws exception on error
  */
-void createDirs(const std::string& path, mode_t mode);
+void createDirs(const std::string& path, mode_t mode = 0755);
+
+/**
+ * Recursively do lchown on directory
+ */
+void chownDir(const std::string& path, uid_t uid, uid_t gid);
 
 /**
  * Creates an empty directory, ready to serve as mount point.
index ddec9b6f4442be02d72608848d9f07eccb7c558e..d1f4a2e1d5b57d442afd34da19108c479fb79bb3 100644 (file)
 #include "config.hpp"
 
 #include "utils/scoped-dir.hpp"
-
-#include <boost/filesystem.hpp>
-
+#include "utils/fs.hpp"
+#include "utils/exception.hpp"
+#include "logger/logger.hpp"
 
 namespace utils {
 
-namespace fs = boost::filesystem;
-
 ScopedDir::ScopedDir()
 {
 }
@@ -52,16 +50,20 @@ void ScopedDir::create(const std::string& path)
     remove();
     if (!path.empty()) {
         mPath = path;
-        fs::remove_all(path);
-        fs::create_directories(path);
+        remove();
+        utils::createDirs(path);
     }
 }
 
 void ScopedDir::remove()
 {
-    if (!mPath.empty()) {
-        fs::remove_all(mPath);
-        mPath.clear();
+    if (mPath.empty()) {
+        return ;
+    }
+    try {
+        utils::removeDir(mPath);
+    } catch (const UtilsException&) {
+        LOGE("ScopedDir: can't remove " + mPath);
     }
 }
 
index f5f356156d416c61dec174953c6622d380420918..7980ea9642d0456bdfe1c79618927aad55709df0 100644 (file)
@@ -50,7 +50,7 @@ namespace internals {
 
 namespace {
 const int MAX_QUEUE_LENGTH = 1000;
-const int RETRY_CONNECT_STEP_MS = 10;
+const int RETRY_CONNECT_STEP_MS = 100;
 const int UNIX_SOCKET_PROTOCOL = 0;
 
 void setFdOptions(const int fd)
index f4d053feecbe7d8332c065d6081735fa37af8a24..25d4b9f8e12ab256c3c15e754cfe36dd105eb076 100644 (file)
@@ -43,11 +43,15 @@ public:
                const std::string &field_name,
                const T& value)
     {
-        if (!func(value)) {
-            const std::string msg = "validation failed on field: " +
-                                    field_name +
-                                    "(" + std::string(typeid(value).name()) + ")";
-            throw VerificationException(msg);
+        try {
+            if (!func(value)) {
+                const std::string msg = "validation failed on field: " +
+                                        field_name +
+                                        "(" + std::string(typeid(value).name()) + ")";
+                throw VerificationException(msg);
+            }
+        } catch (const std::exception& e) {
+            throw VerificationException(e.what());
         }
     }
 
index 8b92fd3e8778ab6b66c156827ba0067dd3f9c50a..35de8b20f0f40dade1a491b1a0bb9a28e610904e 100644 (file)
@@ -9,4 +9,4 @@ Name: lib@PROJECT_NAME@
 Description: cargo library
 Version: @_LIB_VERSION_@
 Libs:
-Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@
+Cflags: -I${includedir} -I${includedir}/@PROJECT_NAME@  -I${includedir}/@PROJECT_NAME@-utils
index e3f8b3e9f861a06f245afc225b55f6857f1ad322..10c4f9b33ef2f7625e8528921fe8971ae6b30b35 100644 (file)
@@ -154,7 +154,7 @@ void doSocketTest(EventPoll& poll)
                 std::string msg(RESPONSE.size(), 'x');
                 socket.read(&msg.front(), msg.size());
                 response.set(msg);
-            } catch (UtilsException&) {
+            } catch (const UtilsException&) {
                 response.set(std::string());
             }
             poll.modifyFD(socket.getFD(), EPOLLRDHUP);