/* * 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; }