2017-12-17 20:40:21 +00:00
|
|
|
#if defined(__unix__) || defined(__APPLE__)
|
2017-03-04 01:45:20 +00:00
|
|
|
#include "platform.h"
|
|
|
|
|
2017-03-25 20:15:00 +00:00
|
|
|
#include "utils.h"
|
2017-03-04 01:45:20 +00:00
|
|
|
|
2017-07-28 02:14:33 +00:00
|
|
|
#include <loguru.hpp>
|
|
|
|
|
2017-03-04 01:45:20 +00:00
|
|
|
#include <pthread.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <cassert>
|
2017-03-04 01:45:20 +00:00
|
|
|
#include <iostream>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <string>
|
|
|
|
|
2017-03-04 01:45:20 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <limits.h>
|
|
|
|
#include <semaphore.h>
|
2017-03-04 01:45:20 +00:00
|
|
|
#include <signal.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2017-04-18 17:21:53 +00:00
|
|
|
#include <dirent.h>
|
2017-04-20 16:47:24 +00:00
|
|
|
#include <sys/stat.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <sys/types.h> // required for stat.h
|
|
|
|
|
|
|
|
#include <errno.h>
|
2017-04-21 06:32:18 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2017-04-18 02:59:48 +00:00
|
|
|
|
2017-03-04 01:45:20 +00:00
|
|
|
#include <semaphore.h>
|
|
|
|
#include <sys/mman.h>
|
2017-09-22 01:14:57 +00:00
|
|
|
|
2017-12-17 20:40:21 +00:00
|
|
|
#if defined(__FreeBSD__)
|
|
|
|
# include <sys/param.h> // MAXPATHLEN
|
|
|
|
# include <sys/sysctl.h> // sysctl
|
|
|
|
#elif defined(__linux__)
|
|
|
|
# include <malloc.h>
|
|
|
|
# include <sys/prctl.h>
|
2017-10-31 22:43:07 +00:00
|
|
|
#endif
|
|
|
|
|
2017-11-30 18:40:42 +00:00
|
|
|
namespace {
|
|
|
|
|
2017-12-01 17:50:39 +00:00
|
|
|
// Returns the canonicalized absolute pathname, without expanding symbolic
|
|
|
|
// links. This is a variant of realpath(2), C++ rewrite of
|
2017-11-30 18:40:42 +00:00
|
|
|
// https://github.com/freebsd/freebsd/blob/master/lib/libc/stdlib/realpath.c
|
|
|
|
optional<std::string> RealPathNotExpandSymlink(std::string path) {
|
|
|
|
if (path.empty()) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return nullopt;
|
|
|
|
}
|
|
|
|
if (path[0] == '\0') {
|
|
|
|
errno = ENOENT;
|
|
|
|
return nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do not use PATH_MAX because it is tricky on Linux.
|
|
|
|
// See https://eklitzke.org/path-max-is-tricky
|
|
|
|
char tmp[1024];
|
|
|
|
std::string resolved;
|
|
|
|
size_t i = 0;
|
|
|
|
struct stat sb;
|
|
|
|
if (path[0] == '/') {
|
|
|
|
resolved = "/";
|
|
|
|
i = 1;
|
|
|
|
} else {
|
|
|
|
if (!getcwd(tmp, sizeof tmp))
|
|
|
|
return nullopt;
|
|
|
|
resolved = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (i < path.size()) {
|
|
|
|
auto j = path.find('/', i);
|
|
|
|
if (j == std::string::npos)
|
|
|
|
j = path.size();
|
|
|
|
auto next_token = path.substr(i, j - i);
|
|
|
|
i = j + 1;
|
|
|
|
if (resolved.back() != '/')
|
|
|
|
resolved += '/';
|
|
|
|
if (next_token.empty() || next_token == ".") {
|
|
|
|
// Handle consequential slashes and "."
|
|
|
|
continue;
|
|
|
|
} else if (next_token == "..") {
|
|
|
|
// Strip the last path component except when it is single "/"
|
|
|
|
if (resolved.size() > 1)
|
|
|
|
resolved.resize(resolved.rfind('/', resolved.size() - 2) + 1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Append the next path component.
|
|
|
|
// Here we differ from realpath(3), we use stat(2) instead of
|
|
|
|
// lstat(2) because we do not want to resolve symlinks.
|
|
|
|
resolved += next_token;
|
|
|
|
if (stat(resolved.c_str(), &sb) != 0)
|
|
|
|
return nullopt;
|
|
|
|
if (!S_ISDIR(sb.st_mode) && j < path.size()) {
|
|
|
|
errno = ENOTDIR;
|
|
|
|
return nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove trailing slash except when a single "/".
|
|
|
|
if (resolved.size() > 1 && resolved.back() == '/')
|
|
|
|
resolved.pop_back();
|
|
|
|
return resolved;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2017-03-04 01:45:20 +00:00
|
|
|
struct PlatformMutexLinux : public PlatformMutex {
|
|
|
|
sem_t* sem_ = nullptr;
|
|
|
|
|
|
|
|
PlatformMutexLinux(const std::string& name) {
|
2017-03-16 01:39:28 +00:00
|
|
|
std::cerr << "PlatformMutexLinux name=" << name << std::endl;
|
2017-03-19 22:08:36 +00:00
|
|
|
sem_ = sem_open(name.c_str(), O_CREAT, 0666 /*permission*/,
|
|
|
|
1 /*initial_value*/);
|
2017-03-04 01:45:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-19 22:08:36 +00:00
|
|
|
~PlatformMutexLinux() override { sem_close(sem_); }
|
2017-03-04 01:45:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct PlatformScopedMutexLockLinux : public PlatformScopedMutexLock {
|
|
|
|
sem_t* sem_ = nullptr;
|
|
|
|
|
2017-03-19 22:08:36 +00:00
|
|
|
PlatformScopedMutexLockLinux(sem_t* sem) : sem_(sem) { sem_wait(sem_); }
|
2017-03-04 01:45:20 +00:00
|
|
|
|
2017-03-19 22:08:36 +00:00
|
|
|
~PlatformScopedMutexLockLinux() override { sem_post(sem_); }
|
2017-03-04 01:45:20 +00:00
|
|
|
};
|
|
|
|
|
2017-03-16 01:39:28 +00:00
|
|
|
void* checked(void* result, const char* expr) {
|
|
|
|
if (!result) {
|
|
|
|
std::cerr << "FAIL errno=" << errno << " in |" << expr << "|" << std::endl;
|
2017-03-28 02:28:45 +00:00
|
|
|
std::cerr << "errno => " << strerror(errno) << std::endl;
|
2017-03-16 01:39:28 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int checked(int result, const char* expr) {
|
|
|
|
if (result == -1) {
|
|
|
|
std::cerr << "FAIL errno=" << errno << " in |" << expr << "|" << std::endl;
|
2017-03-28 02:28:45 +00:00
|
|
|
std::cerr << "errno => " << strerror(errno) << std::endl;
|
2017-03-16 01:39:28 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CHECKED(expr) checked(expr, #expr)
|
|
|
|
|
2017-03-04 01:45:20 +00:00
|
|
|
struct PlatformSharedMemoryLinux : public PlatformSharedMemory {
|
|
|
|
std::string name_;
|
2017-03-21 17:06:22 +00:00
|
|
|
size_t size_;
|
2017-03-04 01:45:20 +00:00
|
|
|
int fd_;
|
|
|
|
|
2017-03-21 17:06:22 +00:00
|
|
|
PlatformSharedMemoryLinux(const std::string& name, size_t size)
|
2017-09-22 01:14:57 +00:00
|
|
|
: name_(name), size_(size) {
|
|
|
|
std::cerr << "PlatformSharedMemoryLinux name=" << name << ", size=" << size
|
|
|
|
<< std::endl;
|
2017-03-16 01:39:28 +00:00
|
|
|
|
|
|
|
// Try to create shared memory but only if it does not already exist. Since
|
|
|
|
// we created the memory, we need to initialize it.
|
|
|
|
fd_ = shm_open(name_.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
|
|
|
if (fd_ >= 0) {
|
|
|
|
std::cerr << "Calling ftruncate fd_=" << fd_ << std::endl;
|
2017-03-21 17:06:22 +00:00
|
|
|
CHECKED(ftruncate(fd_, size));
|
2017-03-16 01:39:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, we just open existing shared memory. We don't need to
|
|
|
|
// create or initialize it.
|
|
|
|
else {
|
2017-03-19 22:08:36 +00:00
|
|
|
fd_ = CHECKED(shm_open(
|
|
|
|
name_.c_str(), O_RDWR, /* memory is read/write, create if needed */
|
|
|
|
S_IRUSR | S_IWUSR /* user read/write */));
|
2017-03-16 01:39:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Map the shared memory to an address.
|
2017-09-22 01:14:57 +00:00
|
|
|
data = CHECKED(mmap(nullptr /*kernel assigned starting address*/, size,
|
|
|
|
PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0 /*offset*/));
|
2017-03-21 17:06:22 +00:00
|
|
|
capacity = size;
|
2017-03-16 01:39:28 +00:00
|
|
|
|
2017-03-28 01:04:37 +00:00
|
|
|
std::cerr << "Open shared memory name=" << name << ", fd=" << fd_
|
2017-03-21 17:06:22 +00:00
|
|
|
<< ", shared=" << data << ", capacity=" << capacity << std::endl;
|
2017-03-04 01:45:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~PlatformSharedMemoryLinux() override {
|
2017-03-21 17:06:22 +00:00
|
|
|
CHECKED(munmap(data, size_));
|
2017-03-16 01:39:28 +00:00
|
|
|
CHECKED(shm_unlink(name_.c_str()));
|
|
|
|
|
2017-03-21 17:06:22 +00:00
|
|
|
data = nullptr;
|
2017-03-04 01:45:20 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<PlatformMutex> CreatePlatformMutex(const std::string& name) {
|
|
|
|
std::string name2 = "/" + name;
|
|
|
|
return MakeUnique<PlatformMutexLinux>(name2);
|
|
|
|
}
|
|
|
|
|
2017-03-19 22:08:36 +00:00
|
|
|
std::unique_ptr<PlatformScopedMutexLock> CreatePlatformScopedMutexLock(
|
|
|
|
PlatformMutex* mutex) {
|
|
|
|
return MakeUnique<PlatformScopedMutexLockLinux>(
|
|
|
|
static_cast<PlatformMutexLinux*>(mutex)->sem_);
|
2017-03-04 01:45:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-19 22:08:36 +00:00
|
|
|
std::unique_ptr<PlatformSharedMemory> CreatePlatformSharedMemory(
|
2017-09-22 01:14:57 +00:00
|
|
|
const std::string& name,
|
|
|
|
size_t size) {
|
2017-03-04 01:45:20 +00:00
|
|
|
std::string name2 = "/" + name;
|
2017-03-21 17:06:22 +00:00
|
|
|
return MakeUnique<PlatformSharedMemoryLinux>(name2, size);
|
2017-03-04 01:45:20 +00:00
|
|
|
}
|
2017-03-28 01:47:12 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
void PlatformInit() {}
|
2017-03-28 01:47:12 +00:00
|
|
|
|
2017-12-14 22:35:21 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
extern "C" int _NSGetExecutablePath(char* buf,uint32_t* bufsize);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// See https://stackoverflow.com/questions/143174/how-do-i-get-the-directory-that-a-program-is-running-from
|
|
|
|
std::string GetExecutablePath() {
|
2017-12-17 20:40:21 +00:00
|
|
|
#ifdef __APPLE__
|
2017-12-14 22:35:21 +00:00
|
|
|
uint32_t size = 0;
|
|
|
|
_NSGetExecutablePath(nullptr, &size);
|
|
|
|
char *buffer = new char[size];
|
|
|
|
_NSGetExecutablePath(buffer, &size);
|
|
|
|
std::string result(buffer);
|
|
|
|
delete[] buffer;
|
|
|
|
return result;
|
2017-12-17 20:40:21 +00:00
|
|
|
#elif defined(__FreeBSD__)
|
|
|
|
static const int name[] = {
|
|
|
|
CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
|
|
|
|
};
|
|
|
|
char path[MAXPATHLEN];
|
|
|
|
size_t len = sizeof(path);
|
|
|
|
path[0] = '\0';
|
|
|
|
(void)sysctl(name, 4, path, &len, NULL, 0);
|
|
|
|
return std::string(path);
|
|
|
|
#else
|
|
|
|
char buffer[PATH_MAX] = {0};
|
|
|
|
readlink("/proc/self/exe", buffer, PATH_MAX);
|
|
|
|
return std::string(buffer);
|
2017-12-14 22:35:21 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-10-25 02:11:11 +00:00
|
|
|
std::string GetWorkingDirectory() {
|
|
|
|
char result[FILENAME_MAX];
|
|
|
|
if (!getcwd(result, sizeof(result)))
|
|
|
|
return "";
|
|
|
|
std::string working_dir = std::string(result, strlen(result));
|
|
|
|
EnsureEndsInSlash(working_dir);
|
|
|
|
return working_dir;
|
|
|
|
}
|
|
|
|
|
2017-03-28 01:47:12 +00:00
|
|
|
std::string NormalizePath(const std::string& path) {
|
2017-11-30 18:40:42 +00:00
|
|
|
optional<std::string> resolved = RealPathNotExpandSymlink(path);
|
|
|
|
return resolved ? *resolved : path;
|
2017-03-28 01:47:12 +00:00
|
|
|
}
|
|
|
|
|
2017-04-18 02:59:48 +00:00
|
|
|
bool TryMakeDirectory(const std::string& absolute_path) {
|
2017-09-22 01:14:57 +00:00
|
|
|
const mode_t kMode = 0777; // UNIX style permissions
|
2017-04-18 02:59:48 +00:00
|
|
|
if (mkdir(absolute_path.c_str(), kMode) == -1) {
|
|
|
|
// Success if the directory exists.
|
|
|
|
return errno == EEXIST;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-16 22:48:54 +00:00
|
|
|
void SetCurrentThreadName(const std::string& thread_name) {
|
2017-07-28 02:14:33 +00:00
|
|
|
loguru::set_thread_name(thread_name.c_str());
|
2017-12-17 20:40:21 +00:00
|
|
|
#ifdef __linux__
|
2017-04-16 22:48:54 +00:00
|
|
|
prctl(PR_SET_NAME, thread_name.c_str(), 0, 0, 0);
|
2017-04-18 17:21:53 +00:00
|
|
|
#endif
|
2017-04-16 22:48:54 +00:00
|
|
|
}
|
|
|
|
|
2017-08-16 05:39:50 +00:00
|
|
|
optional<int64_t> GetLastModificationTime(const std::string& absolute_path) {
|
2017-04-20 16:47:24 +00:00
|
|
|
struct stat buf;
|
|
|
|
if (stat(absolute_path.c_str(), &buf) != 0) {
|
|
|
|
switch (errno) {
|
|
|
|
case ENOENT:
|
2017-09-22 01:14:57 +00:00
|
|
|
// std::cerr << "GetLastModificationTime: unable to find file " <<
|
|
|
|
// absolute_path << std::endl;
|
2017-08-16 05:39:50 +00:00
|
|
|
return nullopt;
|
2017-04-20 16:47:24 +00:00
|
|
|
case EINVAL:
|
2017-09-22 01:14:57 +00:00
|
|
|
// std::cerr << "GetLastModificationTime: invalid param to _stat for
|
|
|
|
// file file " << absolute_path << std::endl;
|
2017-08-16 05:39:50 +00:00
|
|
|
return nullopt;
|
2017-04-20 16:47:24 +00:00
|
|
|
default:
|
2017-09-22 01:14:57 +00:00
|
|
|
// std::cerr << "GetLastModificationTime: unhandled for " <<
|
|
|
|
// absolute_path << std::endl; exit(1);
|
2017-08-16 05:39:50 +00:00
|
|
|
return nullopt;
|
2017-04-20 16:47:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.st_mtime;
|
|
|
|
}
|
|
|
|
|
2017-07-29 00:08:18 +00:00
|
|
|
void MoveFileTo(const std::string& dest, const std::string& source) {
|
|
|
|
// TODO/FIXME - do a real move.
|
|
|
|
CopyFileTo(dest, source);
|
|
|
|
}
|
|
|
|
|
2017-04-21 06:32:18 +00:00
|
|
|
// See http://stackoverflow.com/q/13198627
|
|
|
|
void CopyFileTo(const std::string& dest, const std::string& source) {
|
|
|
|
int fd_from = open(source.c_str(), O_RDONLY);
|
|
|
|
if (fd_from < 0)
|
|
|
|
return;
|
|
|
|
|
2017-05-20 00:41:27 +00:00
|
|
|
int fd_to = open(dest.c_str(), O_WRONLY | O_CREAT, 0666);
|
2017-04-21 06:32:18 +00:00
|
|
|
if (fd_to < 0)
|
|
|
|
goto out_error;
|
|
|
|
|
2017-04-21 17:00:36 +00:00
|
|
|
char buf[4096];
|
|
|
|
ssize_t nread;
|
2017-04-21 06:32:18 +00:00
|
|
|
while (nread = read(fd_from, buf, sizeof buf), nread > 0) {
|
2017-09-22 01:14:57 +00:00
|
|
|
char* out_ptr = buf;
|
2017-04-21 06:32:18 +00:00
|
|
|
ssize_t nwritten;
|
|
|
|
|
|
|
|
do {
|
|
|
|
nwritten = write(fd_to, out_ptr, nread);
|
|
|
|
|
|
|
|
if (nwritten >= 0) {
|
|
|
|
nread -= nwritten;
|
|
|
|
out_ptr += nwritten;
|
2017-09-22 01:14:57 +00:00
|
|
|
} else if (errno != EINTR)
|
2017-04-21 06:32:18 +00:00
|
|
|
goto out_error;
|
|
|
|
} while (nread > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nread == 0) {
|
|
|
|
if (close(fd_to) < 0) {
|
|
|
|
fd_to = -1;
|
|
|
|
goto out_error;
|
|
|
|
}
|
|
|
|
close(fd_from);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
out_error:
|
|
|
|
close(fd_from);
|
|
|
|
if (fd_to >= 0)
|
|
|
|
close(fd_to);
|
|
|
|
}
|
|
|
|
|
2017-05-23 07:24:14 +00:00
|
|
|
bool IsSymLink(const std::string& path) {
|
|
|
|
struct stat buf;
|
|
|
|
int result;
|
|
|
|
result = lstat(path.c_str(), &buf);
|
2017-12-16 16:32:10 +00:00
|
|
|
return S_ISLNK(buf.st_mode);
|
2017-05-23 07:24:14 +00:00
|
|
|
}
|
|
|
|
|
2017-03-29 06:33:38 +00:00
|
|
|
std::vector<std::string> GetPlatformClangArguments() {
|
2017-04-26 04:13:04 +00:00
|
|
|
return {};
|
2017-03-29 06:33:38 +00:00
|
|
|
}
|
2017-03-28 01:47:12 +00:00
|
|
|
#undef CHECKED
|
|
|
|
|
2017-08-17 18:02:47 +00:00
|
|
|
void FreeUnusedMemory() {
|
2017-10-31 22:43:07 +00:00
|
|
|
#if defined(__linux__)
|
2017-08-17 18:02:47 +00:00
|
|
|
malloc_trim(0);
|
2017-10-31 22:43:07 +00:00
|
|
|
#endif
|
2017-08-17 18:02:47 +00:00
|
|
|
}
|
|
|
|
|
2017-03-21 17:06:22 +00:00
|
|
|
#endif
|