* Receive data of a given type (size)
*/
template<typename Data>
- Data read();
+ Data read(unsigned timeoutMS = 5000);
/**
* Get an active file descriptor
}
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;
}
#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()
{
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
#define COMMON_UTILS_EXCEPTION_HPP
#include "logger/logger.hpp"
+#include <vector>
#include <stdexcept>
namespace utils {
};
+void fillInStackTrace(std::vector<std::string>& bt);
+
} // namespace utils
#endif // COMMON_UTILS_EXCEPTION_HPP
#include "utils/execute.hpp"
#include "logger/logger.hpp"
+#include <iostream>
+
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
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);
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)
bool waitPid(pid_t pid, int& status)
{
+ LOGD("Wait pid " << pid);
+
while (::waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
LOGE("waitpid() failed: " << getSystemErrorMessage());
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)
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)
}
}
-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)
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)
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)
*/
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
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.
#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()
{
}
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);
}
}
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)
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());
}
}
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
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);