/*
* Copyright (C) 2015 by Multi-Tech Systems
*
* This file is part of libmts.
*
* libmts is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* libmts 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 libmts. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
#include
#ifdef WIN32
#include
//WIN32: FILETIME structure has a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601.
static int64_t getEpochTimeMicros() {
const SYSTEMTIME EPOCH = {1970, 1, 4, 1, 0, 0, 0, 0};
FILETIME ft;
BOOL ok = SystemTimeToFileTime(&EPOCH, &ft);
assert(ok);
int64_t epochTimeMicros = ((static_cast(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) / 10;
return epochTimeMicros;
}
static int64_t getSystemTimeMicros() {
SYSTEMTIME st;
GetSystemTime(&st);
FILETIME ft;
BOOL ok = SystemTimeToFileTime(&st, &ft);
assert(ok);
int64_t systemTimeMicros = ((static_cast(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) / 10;
return systemTimeMicros;
}
static int64_t getClockFrequency() {
LARGE_INTEGER freq;
BOOL ok = QueryPerformanceFrequency(&freq);
assert(ok);
return freq.QuadPart;
}
static int64_t getClockValue() {
LARGE_INTEGER value;
BOOL ok = QueryPerformanceCounter(&value);
assert(ok);
return value.QuadPart;
}
#else
#include
#endif
using namespace MTS;
uint64_t System::timeMicros() {
int64_t micros = 0;
#ifdef WIN32
static const int64_t EPOCH_TIME_MICROS = getEpochTimeMicros();
micros = getSystemTimeMicros() - EPOCH_TIME_MICROS;
#else
timespec ts;
int result = clock_gettime(CLOCK_REALTIME, &ts);
if (result == 0) {
micros = (static_cast(ts.tv_sec) * 1000000)
+ (ts.tv_nsec / 1000);
}
#endif
return micros;
}
uint64_t System::precisionTimeMicros() {
int64_t micros = 0;
#ifdef WIN32
static const double TO_MICROS = 1000000.0 / getClockFrequency();
int64_t value = getClockValue();
micros = static_cast(value * TO_MICROS);
#else
micros = timeMicros();
#endif
return micros;
}
uint64_t System::monoTimeMicros() {
uint64_t micros = 0;
#ifdef WIN32
micros = static_cast(GetTickCount64()) * 1000;
#else
timespec ts;
int result = clock_gettime(CLOCK_MONOTONIC, &ts);
if (result == 0) {
micros = (static_cast(ts.tv_sec) * 1000000)
+ (ts.tv_nsec / 1000);
}
#endif
return micros;
}
bool System::isBigEndian() {
static union {
uint32_t i;
char c[4];
} endian = { 0x01020304 };
return endian.c[0] == 1;
}
void System::swapBytes(uint8_t* const pBuffer, const uint32_t iSize) {
if (iSize > 1 && pBuffer != 0) {
uint8_t cByte = 0;
uint32_t i;
uint32_t j;
for (i = 0, j = iSize - 1; i < j; i++, j--) {
cByte = pBuffer[i];
pBuffer[i] = pBuffer[j];
pBuffer[j] = cByte;
}
}
}
int32_t System::cmd(const std::string& cmd, std::string& result) {
std::string output;
FILE * stream;
const int max_buffer = 256;
char buffer[max_buffer];
int32_t code = -1;
stream = popen(cmd.c_str(), "r");
if (stream) {
while (!feof(stream))
if (fgets(buffer, max_buffer, stream) != NULL)
output.append(buffer);
code = pclose(stream);
}
result = output;
return code;
}
int32_t System::readFile(const std::string& path, std::string& result) {
std::ifstream infile(path.c_str());
std::stringstream ss;
if (!infile.is_open()) {
return -1;
}
ss << infile.rdbuf();
infile.close();
result = ss.str();
return 0;
}
int32_t System::execute(const std::string& cmd, const std::vector& argv, std::string& result) {
// Ported directly from System::cmd
std::string output;
const int max_buffer = 256;
char buffer[max_buffer];
int32_t code = -1;
ChildHandle child;
if (executeBackground(cmd, argv, PipeType::READ, child)) {
while (!feof(child.stream)) {
if (fgets(buffer, max_buffer, child.stream) != NULL) {
output.append(buffer);
}
}
code = closeBackground(child);
}
result = output;
return code;
}
int32_t System::executeEnv(const std::string& cmd, const std::vector& argv, std::string& result) {
// Ported directly from System::cmd
std::string output;
const int max_buffer = 256;
char buffer[max_buffer];
int32_t code = -1;
ChildHandle child;
if (executeBackgroundEnv(cmd, argv, PipeType::READ, child)) {
while (!feof(child.stream)) {
if (fgets(buffer, max_buffer, child.stream) != NULL) {
output.append(buffer);
}
}
code = closeBackground(child);
}
result = output;
return code;
}
bool System::executeBackground(const std::string& cmd, const std::vector& argv, System::PipeType type, System::ChildHandle& child) {
std::vector arguments = castArgVector(cmd, argv);
return executeBackground(cmd, arguments.data(), false, type, child);
}
bool System::executeBackgroundEnv(const std::string& cmd, const std::vector& argv, System::PipeType type, System::ChildHandle& child) {
std::vector arguments = castArgVector(cmd, argv);
return executeBackground(cmd, arguments.data(), true, type, child);
}
int System::closeBackground(System::ChildHandle& child) {
int pstat;
pid_t pid;
fclose(child.stream);
do {
pid = waitpid(child.pid, &pstat, 0);
} while (pid == -1 && errno == EINTR);
return pstat;
}
extern char **environ;
bool System::executeBackground(const std::string& cmd, char* const argv[], bool env, System::PipeType type, ChildHandle& child) {
pid_t childPid;
FILE* stream = NULL;
posix_spawn_file_actions_t actions;
int childTargetFd;
const char* parentMode = nullptr;
bool bActionsInitialized = false;
bool bSuccess = false;
do {
Pipe pipe;
if (initPipe(pipe, type) != 0) {
// failed to create pipe
break;
}
if (type == PipeType::READ) {
childTargetFd = STDOUT_FILENO;
parentMode = "r";
} else if (type == PipeType::WRITE) {
childTargetFd = STDIN_FILENO;
parentMode = "w";
} else {
break; // unreachable, unknown mode
}
// Define how child process should handle pipes
if (posix_spawn_file_actions_init(&actions) != 0) {
// failed to initialize file actions
break;
}
bActionsInitialized = true;
if (posix_spawn_file_actions_addclose(&actions, pipe.parent.get()) != 0) {
// failed to add action to close parent pipe fd
break;
}
if (posix_spawn_file_actions_adddup2(&actions, pipe.child.get(), childTargetFd) != 0) {
// failed to add an action to use child pipe fd for output
break;
}
if (posix_spawn_file_actions_addclose(&actions, pipe.child.get()) != 0) {
// failed to add an action to close dupped child pipe fd
break;
}
if (posix_spawnp(&childPid, cmd.c_str(), &actions, NULL, argv, env ? environ : NULL) != 0) {
// failed to spawn the process
break;
}
// Close child-specific file descriptor
pipe.child.reset();
// Convert parent fd to FILE pointer
stream = fdopen(pipe.parent.release(), parentMode);
// Save data about child
child.pid = childPid;
child.stream = stream;
bSuccess = true;
} while (false);
if (bActionsInitialized) {
posix_spawn_file_actions_destroy(&actions);
}
return bSuccess;
}
char* System::castArgument(const std::string& arg) {
// Casting const away is required only to be compatible with some C-style system functions.
// Arguments are not actually modified in such functions.
return const_cast(arg.c_str());
}
std::vector System::castArgVector(const std::string& cmd, const std::vector& argv) {
std::vector result;
// Append application name as argument zero
result.push_back(castArgument(cmd));
// Append all other arguments
for (const std::string& arg : argv) {
result.push_back(castArgument(arg));
}
// Terminate with NULL
result.push_back(NULL);
return result;
}
int System::initPipe(System::Pipe& pipe, System::PipeType type) {
const int READ_END_IDX = 0;
const int WRITE_END_IDX = 1;
int holder[2];
int parentIndex;
int childIndex;
int ret = -1;
do {
if (type == PipeType::READ) {
parentIndex = READ_END_IDX;
childIndex = WRITE_END_IDX;
} else if (type == PipeType::WRITE) {
parentIndex = WRITE_END_IDX;
childIndex = READ_END_IDX;
} else {
// unreachable, unknown mode
break; // error
}
ret = ::pipe(holder);
if (ret != 0) {
break;
}
pipe.parent.reset(holder[parentIndex]);
pipe.child.reset(holder[childIndex]);
} while (false);
return ret;
}
System::FdWrapper::FdWrapper(int fd)
: m_fd(fd)
{}
System::FdWrapper::~FdWrapper() {
reset();
}
int System::FdWrapper::get() const {
return m_fd;
}
int System::FdWrapper::release() {
int fd = m_fd;
m_fd = FD_NOT_READY;
return fd;
}
void System::FdWrapper::reset(int fd) {
if (FD_NOT_READY != m_fd) {
::close(m_fd);
}
m_fd = fd;
}