1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

pow-testgiver support

This commit is contained in:
ton 2020-07-06 17:07:20 +03:00
parent dbde9c1c40
commit f064b1047a
257 changed files with 6665 additions and 2608 deletions

View file

@ -19,17 +19,55 @@
#include "td/utils/port/Clocks.h"
#include <chrono>
#include <ctime>
namespace td {
ClocksDefault::Duration ClocksDefault::monotonic() {
double Clocks::monotonic() {
// TODO write system specific functions, because std::chrono::steady_clock is steady only under Windows
auto duration = std::chrono::steady_clock::now().time_since_epoch();
return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
}
ClocksDefault::Duration ClocksDefault::system() {
double Clocks::system() {
auto duration = std::chrono::system_clock::now().time_since_epoch();
return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
}
int Clocks::tz_offset() {
// not thread-safe on POSIX, so calculate the offset only once
static int offset = [] {
auto now = std::time(nullptr);
auto time_ptr = std::localtime(&now);
if (time_ptr == nullptr) {
return 0;
}
auto local_time = *time_ptr;
time_ptr = std::gmtime(&now);
if (time_ptr == nullptr) {
return 0;
}
auto utc_time = *time_ptr;
int minute_offset = local_time.tm_min - utc_time.tm_min;
int hour_offset = local_time.tm_hour - utc_time.tm_hour;
int day_offset = local_time.tm_mday - utc_time.tm_mday;
if (day_offset >= 20) {
day_offset = -1;
} else if (day_offset <= -20) {
day_offset = 1;
}
int sec_offset = day_offset * 86400 + hour_offset * 3600 + minute_offset * 60;
if (sec_offset >= 15 * 3600 || sec_offset <= -15 * 3600) {
return 0;
}
return sec_offset / 900 * 900; // round to 900 just in case
}();
return offset;
}
static int init_tz_offset = Clocks::tz_offset();
} // namespace td

View file

@ -20,21 +20,12 @@
namespace td {
class ClocksBase {
public:
using Duration = double;
struct Clocks {
static double monotonic();
static double system();
static int tz_offset();
};
// TODO: (maybe) write system specific functions.
class ClocksDefault {
public:
using Duration = ClocksBase::Duration;
static Duration monotonic();
static Duration system();
};
using Clocks = ClocksDefault;
} // namespace td

View file

@ -18,20 +18,27 @@
*/
#pragma once
#pragma managed(push, off)
#include "td/utils/port/config.h"
#pragma managed(pop)
#include "td/utils/common.h"
#undef small
#if TD_WINRT
#pragma managed(push, off)
#include "td/utils/port/wstring_convert.h"
#pragma managed(pop)
#include "collection.h"
#pragma managed(push, off)
#include <cstdint>
#include <map>
#include <mutex>
#pragma managed(pop)
#undef small
#define REF_NEW ref new
#define CLRCALL
@ -102,6 +109,8 @@ inline String^ string_from_unmanaged(const std::string &from) {
#elif TD_CLI
#undef small
#define REF_NEW gcnew
#define CLRCALL __clrcall
#define DEPRECATED_ATTRIBUTE(message) System::ObsoleteAttribute(message)

View file

@ -23,9 +23,11 @@
#include "td/utils/port/wstring_convert.h"
#endif
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/detail/skip_eintr.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/port/sleep.h"
#include "td/utils/ScopeGuard.h"
@ -34,8 +36,11 @@
#include <cstring>
#include <mutex>
#include <unordered_set>
#include <utility>
#if TD_PORT_POSIX
#include <cerrno>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/types.h>
@ -43,6 +48,10 @@
#include <unistd.h>
#endif
#if TD_PORT_WINDOWS && defined(WIN32_LEAN_AND_MEAN)
#include <winioctl.h>
#endif
namespace td {
namespace {
@ -218,6 +227,12 @@ Result<FileFd> FileFd::open(CSlice filepath, int32 flags, int32 mode) {
if (handle == INVALID_HANDLE_VALUE) {
return OS_ERROR(PSLICE() << "File \"" << filepath << "\" can't be " << PrintFlags{flags});
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
if (flags & Write) {
DWORD bytes_returned = 0;
DeviceIoControl(handle, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &bytes_returned, nullptr);
}
#endif
auto native_fd = NativeFd(handle);
if (flags & Append) {
LARGE_INTEGER offset;
@ -492,18 +507,58 @@ NativeFd FileFd::move_as_native_fd() {
return res;
}
Result<int64> FileFd::get_size() const {
TRY_RESULT(s, stat());
return s.size_;
}
#if TD_PORT_WINDOWS
static uint64 filetime_to_unix_time_nsec(LONGLONG filetime) {
namespace {
uint64 filetime_to_unix_time_nsec(LONGLONG filetime) {
const auto FILETIME_UNIX_TIME_DIFF = 116444736000000000ll;
return static_cast<uint64>((filetime - FILETIME_UNIX_TIME_DIFF) * 100);
}
struct FileSize {
int64 size_;
int64 real_size_;
};
Result<FileSize> get_file_size(const FileFd &file_fd) {
FILE_STANDARD_INFO standard_info;
if (!GetFileInformationByHandleEx(file_fd.get_native_fd().fd(), FileStandardInfo, &standard_info,
sizeof(standard_info))) {
return OS_ERROR("Get FileStandardInfo failed");
}
FileSize res;
res.size_ = standard_info.EndOfFile.QuadPart;
res.real_size_ = standard_info.AllocationSize.QuadPart;
if (res.size_ > 0 && res.real_size_ <= 0) { // just in case
LOG(ERROR) << "Fix real file size from " << res.real_size_ << " to " << res.size_;
res.real_size_ = res.size_;
}
return res;
}
} // namespace
#endif
Result<int64> FileFd::get_size() const {
#if TD_PORT_POSIX
TRY_RESULT(s, stat());
#elif TD_PORT_WINDOWS
TRY_RESULT(s, get_file_size(*this));
#endif
return s.size_;
}
Result<int64> FileFd::get_real_size() const {
#if TD_PORT_POSIX
TRY_RESULT(s, stat());
#elif TD_PORT_WINDOWS
TRY_RESULT(s, get_file_size(*this));
#endif
return s.real_size_;
}
Result<Stat> FileFd::stat() const {
CHECK(!empty());
#if TD_PORT_POSIX
@ -521,12 +576,9 @@ Result<Stat> FileFd::stat() const {
res.is_dir_ = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
res.is_reg_ = !res.is_dir_; // TODO this is still wrong
FILE_STANDARD_INFO standard_info;
status = GetFileInformationByHandleEx(get_native_fd().fd(), FileStandardInfo, &standard_info, sizeof(standard_info));
if (!status) {
return OS_ERROR("Get FileStandardInfo failed");
}
res.size_ = standard_info.EndOfFile.QuadPart;
TRY_RESULT(file_size, get_file_size(*this));
res.size_ = file_size.size_;
res.real_size_ = file_size.real_size_;
return res;
#endif

View file

@ -62,11 +62,15 @@ class FileFd {
PollableFdInfo &get_poll_info();
const PollableFdInfo &get_poll_info() const;
void close();
bool empty() const;
Result<int64> get_size() const;
Result<int64> get_real_size() const;
Result<Stat> stat() const;
Status sync() TD_WARN_UNUSED_RESULT;

View file

@ -279,11 +279,11 @@ uint32 IPAddress::get_ipv4() const {
return htonl(ipv4_addr_.sin_addr.s_addr);
}
Slice IPAddress::get_ipv6() const {
string IPAddress::get_ipv6() const {
static_assert(sizeof(ipv6_addr_.sin6_addr) == 16, "ipv6 size == 16");
CHECK(is_valid());
CHECK(!is_ipv4());
return Slice(ipv6_addr_.sin6_addr.s6_addr, 16);
return Slice(ipv6_addr_.sin6_addr.s6_addr, 16).str();
}
IPAddress IPAddress::get_any_addr() const {
@ -320,7 +320,12 @@ void IPAddress::init_ipv6_any() {
Status IPAddress::init_ipv6_port(CSlice ipv6, int port) {
is_valid_ = false;
if (port <= 0 || port >= (1 << 16)) {
return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
return Status::Error(PSLICE() << "Invalid [IPv6 address port=" << port << "]");
}
string ipv6_plain;
if (ipv6.size() > 2 && ipv6[0] == '[' && ipv6.back() == ']') {
ipv6_plain.assign(ipv6.begin() + 1, ipv6.size() - 2);
ipv6 = ipv6_plain;
}
std::memset(&ipv6_addr_, 0, sizeof(ipv6_addr_));
ipv6_addr_.sin6_family = AF_INET6;
@ -342,7 +347,7 @@ Status IPAddress::init_ipv6_as_ipv4_port(CSlice ipv4, int port) {
Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
is_valid_ = false;
if (port <= 0 || port >= (1 << 16)) {
return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
return Status::Error(PSLICE() << "Invalid [IPv4 address port=" << port << "]");
}
std::memset(&ipv4_addr_, 0, sizeof(ipv4_addr_));
ipv4_addr_.sin_family = AF_INET;
@ -357,6 +362,18 @@ Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
return Status::OK();
}
Result<IPAddress> IPAddress::get_ip_address(CSlice host) {
auto r_address = get_ipv4_address(host);
if (r_address.is_ok()) {
return r_address.move_as_ok();
}
r_address = get_ipv6_address(host);
if (r_address.is_ok()) {
return r_address.move_as_ok();
}
return Status::Error("Not a valid IP address");
}
Result<IPAddress> IPAddress::get_ipv4_address(CSlice host) {
// sometimes inet_addr allows much more valid IPv4 hosts than inet_pton,
// like 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001
@ -384,6 +401,10 @@ Result<IPAddress> IPAddress::get_ipv6_address(CSlice host) {
}
Status IPAddress::init_host_port(CSlice host, int port, bool prefer_ipv6) {
if (host.size() > 2 && host[0] == '[' && host.back() == ']') {
return init_ipv6_port(host, port == 0 ? 1 : port);
}
return init_host_port(host, PSLICE() << port, prefer_ipv6);
}
@ -398,7 +419,12 @@ Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) {
}
#endif
TRY_RESULT(ascii_host, idn_to_ascii(host));
host = ascii_host;
host = ascii_host; // assign string to CSlice
if (host[0] == '[' && host.back() == ']') {
auto port_int = to_integer<int>(port);
return init_ipv6_port(host, port_int == 0 ? 1 : port_int);
}
// some getaddrinfo implementations use inet_pton instead of inet_aton and support only decimal-dotted IPv4 form,
// and so doesn't recognize 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001 as valid IPv4 addresses
@ -413,7 +439,7 @@ Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) {
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
LOG(DEBUG + 10) << "Try to init IP address of " << host << " with port " << port;
LOG(DEBUG + 10) << "Trying to init IP address of " << host << " with port " << port;
auto err = getaddrinfo(host.c_str(), port.c_str(), &hints, &info);
if (err != 0) {
#if TD_WINDOWS
@ -506,19 +532,19 @@ Status IPAddress::init_peer_address(const SocketFd &socket_fd) {
return Status::OK();
}
CSlice IPAddress::ipv4_to_str(uint32 ipv4) {
string IPAddress::ipv4_to_str(uint32 ipv4) {
ipv4 = ntohl(ipv4);
return ::td::get_ip_str(AF_INET, &ipv4);
return ::td::get_ip_str(AF_INET, &ipv4).str();
}
CSlice IPAddress::ipv6_to_str(Slice ipv6) {
string IPAddress::ipv6_to_str(Slice ipv6) {
CHECK(ipv6.size() == 16);
return ::td::get_ip_str(AF_INET6, ipv6.ubegin());
return ::td::get_ip_str(AF_INET6, ipv6.ubegin()).str();
}
Slice IPAddress::get_ip_str() const {
CSlice IPAddress::get_ip_str() const {
if (!is_valid()) {
return Slice("0.0.0.0");
return CSlice("0.0.0.0");
}
switch (get_address_family()) {
@ -528,7 +554,23 @@ Slice IPAddress::get_ip_str() const {
return ::td::get_ip_str(AF_INET, &ipv4_addr_.sin_addr);
default:
UNREACHABLE();
return Slice();
return CSlice();
}
}
string IPAddress::get_ip_host() const {
if (!is_valid()) {
return "0.0.0.0";
}
switch (get_address_family()) {
case AF_INET6:
return PSTRING() << '[' << ::td::get_ip_str(AF_INET6, &ipv6_addr_.sin6_addr) << ']';
case AF_INET:
return ::td::get_ip_str(AF_INET, &ipv4_addr_.sin_addr).str();
default:
UNREACHABLE();
return string();
}
}
@ -611,12 +653,7 @@ StringBuilder &operator<<(StringBuilder &builder, const IPAddress &address) {
if (!address.is_valid()) {
return builder << "[invalid]";
}
if (address.get_address_family() == AF_INET) {
return builder << "[" << address.get_ip_str() << ":" << address.get_port() << "]";
} else {
CHECK(address.get_address_family() == AF_INET6);
return builder << "[[" << address.get_ip_str() << "]:" << address.get_port() << "]";
}
return builder << "[" << address.get_ip_host() << ":" << address.get_port() << "]";
}
} // namespace td

View file

@ -51,11 +51,20 @@ class IPAddress {
void set_port(int port);
uint32 get_ipv4() const;
Slice get_ipv6() const;
Slice get_ip_str() const;
string get_ipv6() const;
// returns result in a static thread-local buffer, which may be overwritten by any subsequent method call
CSlice get_ip_str() const;
// returns IP address as a host, i.e. IPv4 or [IPv6]
string get_ip_host() const;
static string ipv4_to_str(uint32 ipv4);
static string ipv6_to_str(Slice ipv6);
IPAddress get_any_addr() const;
static Result<IPAddress> get_ip_address(CSlice host); // host must be any IPv4 or IPv6
static Result<IPAddress> get_ipv4_address(CSlice host);
static Result<IPAddress> get_ipv6_address(CSlice host);
@ -75,8 +84,6 @@ class IPAddress {
const sockaddr *get_sockaddr() const;
size_t get_sockaddr_len() const;
int get_address_family() const;
static CSlice ipv4_to_str(uint32 ipv4);
static CSlice ipv6_to_str(Slice ipv6);
Status init_sockaddr(sockaddr *addr);
Status init_sockaddr(sockaddr *addr, socklen_t len) TD_WARN_UNUSED_RESULT;

View file

@ -22,10 +22,12 @@
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/detail/skip_eintr.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/port/PollFlags.h"
#if TD_PORT_POSIX
#include <cerrno>
#include <arpa/inet.h>
#include <fcntl.h>
@ -34,7 +36,6 @@
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#endif
#if TD_PORT_WINDOWS

View file

@ -22,6 +22,7 @@
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/detail/skip_eintr.h"
#include "td/utils/port/PollFlags.h"
#if TD_PORT_WINDOWS
@ -32,6 +33,8 @@
#endif
#if TD_PORT_POSIX
#include <cerrno>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
@ -227,11 +230,11 @@ class SocketFdImpl : private Iocp::Callback {
return;
}
std::memset(&write_overlapped_, 0, sizeof(write_overlapped_));
constexpr size_t buf_size = 20;
WSABUF buf[buf_size];
constexpr size_t BUF_SIZE = 20;
WSABUF buf[BUF_SIZE];
auto it = output_reader_.clone();
size_t buf_i;
for (buf_i = 0; buf_i < buf_size; buf_i++) {
for (buf_i = 0; buf_i < BUF_SIZE; buf_i++) {
auto src = it.prepare_read();
if (src.empty()) {
break;

View file

@ -18,7 +18,6 @@
*/
#include "td/utils/port/Stat.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/FileFd.h"
#if TD_PORT_POSIX
@ -27,6 +26,7 @@
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/Clocks.h"
#include "td/utils/port/detail/skip_eintr.h"
#include "td/utils/ScopeGuard.h"
#include <utility>
@ -50,8 +50,20 @@
#include <sys/syscall.h>
#endif
#elif TD_PORT_WINDOWS
#include "td/utils/port/thread.h"
#ifndef PSAPI_VERSION
#define PSAPI_VERSION 1
#endif
#include <psapi.h>
#endif
namespace td {
#if TD_PORT_POSIX
namespace detail {
template <class...>
@ -118,6 +130,7 @@ Stat from_native_stat(const struct ::stat &buf) {
res.atime_nsec_ = static_cast<uint64>(buf.st_atime) * 1000000000 + time_nsec.first;
res.mtime_nsec_ = static_cast<uint64>(buf.st_mtime) * 1000000000 + time_nsec.second / 1000 * 1000;
res.size_ = buf.st_size;
res.real_size_ = buf.st_blocks * 512;
res.is_dir_ = (buf.st_mode & S_IFMT) == S_IFDIR;
res.is_reg_ = (buf.st_mode & S_IFMT) == S_IFREG;
return res;
@ -187,14 +200,20 @@ Status update_atime(CSlice path) {
};
return detail::update_atime(file.get_native_fd().fd());
}
#endif
Result<Stat> stat(CSlice path) {
#if TD_PORT_POSIX
struct ::stat buf;
int err = detail::skip_eintr([&] { return ::stat(path.c_str(), &buf); });
if (err < 0) {
return OS_ERROR(PSLICE() << "Stat for file \"" << path << "\" failed");
}
return detail::from_native_stat(buf);
#elif TD_PORT_WINDOWS
TRY_RESULT(fd, FileFd::open(path, FileFd::Flags::Read | FileFd::PrivateFlags::WinStat));
return fd.stat();
#endif
}
Result<MemStat> mem_stat() {
@ -204,7 +223,7 @@ Result<MemStat> mem_stat() {
if (KERN_SUCCESS !=
task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&t_info), &t_info_count)) {
return Status::Error("task_info failed");
return Status::Error("Call to task_info failed");
}
MemStat res;
res.resident_size_ = t_info.resident_size;
@ -262,7 +281,7 @@ Result<MemStat> mem_stat() {
LOG(ERROR) << "Failed to parse memory stats " << tag("name", name) << tag("value", value);
*x = static_cast<uint64>(-1);
} else {
*x = r_mem.ok() * 1024; // memory is in kB
*x = r_mem.ok() * 1024; // memory is in KB
}
}
if (*s == 0) {
@ -271,6 +290,19 @@ Result<MemStat> mem_stat() {
s++;
}
return res;
#elif TD_WINDOWS
PROCESS_MEMORY_COUNTERS_EX counters;
if (!GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PROCESS_MEMORY_COUNTERS *>(&counters),
sizeof(counters))) {
return Status::Error("Call to GetProcessMemoryInfo failed");
}
MemStat res;
res.resident_size_ = counters.WorkingSetSize;
res.resident_size_peak_ = counters.PeakWorkingSetSize;
res.virtual_size_ = counters.PrivateUsage;
res.virtual_size_peak_ = counters.PeakPagefileUsage;
return res;
#else
return Status::Error("Not supported");
@ -287,7 +319,9 @@ Status cpu_stat_self(CpuStat &stat) {
constexpr int TMEM_SIZE = 10000;
char mem[TMEM_SIZE];
TRY_RESULT(size, fd.read(MutableSlice(mem, TMEM_SIZE - 1)));
CHECK(size < TMEM_SIZE - 1);
if (size >= TMEM_SIZE - 1) {
return Status::Error("Failed for read /proc/self/stat");
}
mem[size] = 0;
char *s = mem;
@ -296,10 +330,10 @@ Status cpu_stat_self(CpuStat &stat) {
while (pass_cnt < 15) {
if (pass_cnt == 13) {
stat.process_user_ticks = to_integer<uint64>(Slice(s, t));
stat.process_user_ticks_ = to_integer<uint64>(Slice(s, t));
}
if (pass_cnt == 14) {
stat.process_system_ticks = to_integer<uint64>(Slice(s, t));
stat.process_system_ticks_ = to_integer<uint64>(Slice(s, t));
}
while (*s && *s != ' ') {
s++;
@ -308,11 +342,12 @@ Status cpu_stat_self(CpuStat &stat) {
s++;
pass_cnt++;
} else {
return Status::Error("unexpected end of proc file");
return Status::Error("Unexpected end of proc file");
}
}
return Status::OK();
}
Status cpu_stat_total(CpuStat &stat) {
TRY_RESULT(fd, FileFd::open("/proc/stat", FileFd::Read));
SCOPE_EXIT {
@ -322,14 +357,17 @@ Status cpu_stat_total(CpuStat &stat) {
constexpr int TMEM_SIZE = 10000;
char mem[TMEM_SIZE];
TRY_RESULT(size, fd.read(MutableSlice(mem, TMEM_SIZE - 1)));
CHECK(size < TMEM_SIZE - 1);
if (size >= TMEM_SIZE - 1) {
return Status::Error("Failed for read /proc/stat");
}
mem[size] = 0;
uint64 sum = 0, cur = 0;
uint64 sum = 0;
uint64 cur = 0;
for (size_t i = 0; i < size; i++) {
int c = mem[i];
char c = mem[i];
if (c >= '0' && c <= '9') {
cur = cur * 10 + (uint64)c - '0';
cur = cur * 10 + static_cast<uint64>(c) - '0';
} else {
sum += cur;
cur = 0;
@ -339,7 +377,7 @@ Status cpu_stat_total(CpuStat &stat) {
}
}
stat.total_ticks = sum;
stat.total_ticks_ = sum;
return Status::OK();
}
#endif
@ -349,25 +387,28 @@ Result<CpuStat> cpu_stat() {
CpuStat stat;
TRY_STATUS(cpu_stat_self(stat));
TRY_STATUS(cpu_stat_total(stat));
return stat;
#elif TD_WINDOWS
CpuStat stat;
stat.total_ticks_ = static_cast<uint64>(GetTickCount64()) * 10000;
auto hardware_concurrency = thread::hardware_concurrency();
if (hardware_concurrency != 0) {
stat.total_ticks_ *= hardware_concurrency;
}
FILETIME ignored_time;
FILETIME kernel_time;
FILETIME user_time;
if (!GetProcessTimes(GetCurrentProcess(), &ignored_time, &ignored_time, &kernel_time, &user_time)) {
return Status::Error("Failed to call GetProcessTimes");
}
stat.process_system_ticks_ = kernel_time.dwLowDateTime + (static_cast<uint64>(kernel_time.dwHighDateTime) << 32);
stat.process_user_ticks_ = user_time.dwLowDateTime + (static_cast<uint64>(user_time.dwHighDateTime) << 32);
return stat;
#else
return Status::Error("Not supported");
#endif
}
} // namespace td
#endif
#if TD_PORT_WINDOWS
namespace td {
Result<Stat> stat(CSlice path) {
TRY_RESULT(fd, FileFd::open(path, FileFd::Flags::Read | FileFd::PrivateFlags::WinStat));
return fd.stat();
}
Result<CpuStat> cpu_stat() {
return Status::Error("Not supported");
}
} // namespace td
#endif

View file

@ -30,6 +30,7 @@ struct Stat {
bool is_dir_;
bool is_reg_;
int64 size_;
int64 real_size_;
uint64 atime_nsec_;
uint64 mtime_nsec_;
};
@ -37,20 +38,13 @@ struct Stat {
Result<Stat> stat(CSlice path) TD_WARN_UNUSED_RESULT;
struct CpuStat {
uint64 total_ticks{0};
uint64 process_user_ticks{0};
uint64 process_system_ticks{0};
uint64 total_ticks_{0};
uint64 process_user_ticks_{0};
uint64 process_system_ticks_{0};
};
Result<CpuStat> cpu_stat() TD_WARN_UNUSED_RESULT;
#if TD_PORT_POSIX
namespace detail {
Result<Stat> fstat(int native_fd);
} // namespace detail
Status update_atime(CSlice path) TD_WARN_UNUSED_RESULT;
struct MemStat {
uint64 resident_size_ = 0;
uint64 resident_size_peak_ = 0;
@ -60,6 +54,14 @@ struct MemStat {
Result<MemStat> mem_stat() TD_WARN_UNUSED_RESULT;
#if TD_PORT_POSIX
namespace detail {
Result<Stat> fstat(int native_fd);
} // namespace detail
Status update_atime(CSlice path) TD_WARN_UNUSED_RESULT;
#endif
} // namespace td

View file

@ -35,8 +35,7 @@ namespace td {
template <int id>
static FileFd &get_file_fd() {
static FileFd result = FileFd::from_native_fd(NativeFd(id, true));
static auto guard = td::ScopeExit() + [&] { result.move_as_native_fd().release(); };
assert(!result.empty());
static auto guard = ScopeExit() + [&] { result.move_as_native_fd().release(); };
return result;
}
@ -56,7 +55,7 @@ static FileFd &get_file_fd() {
static auto handle = GetStdHandle(id);
LOG_IF(FATAL, handle == INVALID_HANDLE_VALUE) << "Failed to GetStdHandle " << id;
static FileFd result = FileFd::from_native_fd(NativeFd(handle, true));
static auto guard = td::ScopeExit() + [&] { result.move_as_native_fd().release(); };
static auto guard = ScopeExit() + [&] { result.move_as_native_fd().release(); };
#else
static FileFd result;
#endif
@ -81,7 +80,7 @@ class BufferedStdinImpl : public Iocp::Callback {
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
BufferedStdinImpl() : info_(NativeFd(GetStdHandle(STD_INPUT_HANDLE), true)) {
iocp_ref_ = Iocp::get()->get_ref();
read_thread_ = td::thread([this] { this->read_loop(); });
read_thread_ = thread([this] { this->read_loop(); });
}
#else
BufferedStdinImpl() {
@ -121,7 +120,7 @@ class BufferedStdinImpl : public Iocp::Callback {
PollableFdInfo info_;
ChainBufferWriter writer_;
ChainBufferReader reader_ = writer_.extract_reader();
td::thread read_thread_;
thread read_thread_;
std::atomic<bool> close_flag_{false};
IocpRef iocp_ref_;
std::atomic<int> refcnt_{1};
@ -144,7 +143,7 @@ class BufferedStdinImpl : public Iocp::Callback {
}
}
void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) override {
info_.add_flags_from_poll(td::PollFlags::Read());
info_.add_flags_from_poll(PollFlags::Read());
dec_refcnt();
}

View file

@ -22,6 +22,7 @@
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/detail/skip_eintr.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/VectorQueue.h"
@ -32,6 +33,8 @@
#endif
#if TD_PORT_POSIX
#include <cerrno>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
@ -73,7 +76,7 @@ class UdpSocketReceiveHelper {
message.error = Status::OK();
if ((message_header.dwFlags & (MSG_TRUNC | MSG_CTRUNC)) != 0) {
message.error = Status::Error(501, "message too long");
message.error = Status::Error(501, "Message too long");
message.data = BufferSlice();
return;
}
@ -179,7 +182,8 @@ class UdpSocketFdImpl : private Iocp::Callback {
UdpMessage to_receive_;
WSAMSG receive_message_;
UdpSocketReceiveHelper receive_helper_;
enum : size_t { MAX_PACKET_SIZE = 2048, RESERVED_SIZE = MAX_PACKET_SIZE * 8 };
static constexpr size_t MAX_PACKET_SIZE = 2048;
static constexpr size_t RESERVED_SIZE = MAX_PACKET_SIZE * 8;
BufferSlice receive_buffer_;
UdpMessage to_send_;
@ -434,7 +438,7 @@ class UdpSocketReceiveHelper {
}
if (message_header.msg_flags & MSG_TRUNC) {
if (message.error) {
*message.error = Status::Error(501, "message too long");
*message.error = Status::Error(501, "Message too long");
}
message.data.truncate(0);
return;
@ -822,11 +826,11 @@ static Result<uint32> maximize_buffer(int socket_fd, int optname, uint32 max) {
}
Result<uint32> UdpSocketFd::maximize_snd_buffer(uint32 max) {
return maximize_buffer(get_native_fd().fd(), SO_SNDBUF, max == 0 ? default_udp_max_snd_buffer_size : max);
return maximize_buffer(get_native_fd().fd(), SO_SNDBUF, max == 0 ? DEFAULT_UDP_MAX_SND_BUFFER_SIZE : max);
}
Result<uint32> UdpSocketFd::maximize_rcv_buffer(uint32 max) {
return maximize_buffer(get_native_fd().fd(), SO_RCVBUF, max == 0 ? default_udp_max_rcv_buffer_size : max);
return maximize_buffer(get_native_fd().fd(), SO_RCVBUF, max == 0 ? DEFAULT_UDP_MAX_RCV_BUFFER_SIZE : max);
}
#else
Result<uint32> UdpSocketFd::maximize_snd_buffer(uint32 max) {

View file

@ -96,8 +96,8 @@ class UdpSocketFd {
#endif
private:
static constexpr uint32 default_udp_max_snd_buffer_size = (1 << 24);
static constexpr uint32 default_udp_max_rcv_buffer_size = (1 << 24);
static constexpr uint32 DEFAULT_UDP_MAX_SND_BUFFER_SIZE = (1 << 24);
static constexpr uint32 DEFAULT_UDP_MAX_RCV_BUFFER_SIZE = (1 << 24);
std::unique_ptr<detail::UdpSocketFdImpl, detail::UdpSocketFdImplDeleter> impl_;
explicit UdpSocketFd(unique_ptr<detail::UdpSocketFdImpl> impl);
};

View file

@ -26,6 +26,8 @@ char disable_linker_warning_about_empty_file_epoll_cpp TD_UNUSED;
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include <cerrno>
#include <unistd.h>
namespace td {
@ -82,7 +84,8 @@ void Epoll::unsubscribe(PollableFdRef fd_ref) {
int err = epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_DEL, native_fd, nullptr);
auto epoll_ctl_errno = errno;
LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl DEL failed")
<< ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd << fd.native_fd().validate();
<< ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd
<< ", status = " << fd.native_fd().validate();
}
void Epoll::unsubscribe_before_close(PollableFdRef fd) {

View file

@ -28,6 +28,8 @@ char disable_linker_warning_about_empty_file_event_fd_bsd_cpp TD_UNUSED;
#include "td/utils/port/SocketFd.h"
#include "td/utils/Slice.h"
#include <cerrno>
#include <fcntl.h>
#include <poll.h>
#include <sys/socket.h>

View file

@ -25,10 +25,13 @@ char disable_linker_warning_about_empty_file_event_fd_linux_cpp TD_UNUSED;
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/detail/NativeFd.h"
#include "td/utils/port/detail/skip_eintr.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/Slice.h"
#include <cerrno>
#include <poll.h>
#include <sys/eventfd.h>
#include <unistd.h>

View file

@ -25,6 +25,7 @@ char disable_linker_warning_about_empty_file_kqueue_cpp TD_UNUSED;
#include "td/utils/logging.h"
#include "td/utils/Status.h"
#include <cerrno>
#include <utility>
#include <sys/time.h>

View file

@ -38,7 +38,9 @@ namespace td {
class FdSet {
public:
void on_create_fd(NativeFd::Fd fd) {
CHECK(is_valid(fd));
if (!is_valid(fd)) {
return;
}
if (is_stdio(fd)) {
return;
}
@ -64,7 +66,9 @@ class FdSet {
}
void on_close_fd(NativeFd::Fd fd) {
CHECK(is_valid(fd));
if (!is_valid(fd)) {
return;
}
if (is_stdio(fd)) {
return;
}
@ -112,7 +116,7 @@ FdSet &get_fd_set() {
Status NativeFd::validate() const {
#if TD_FD_DEBUG
return get_fd_set().validate(fd_.get());
return get_fd_set().validate(fd_);
#else
return Status::OK();
#endif
@ -121,13 +125,13 @@ Status NativeFd::validate() const {
NativeFd::NativeFd(Fd fd) : fd_(fd) {
VLOG(fd) << *this << " create";
#if TD_FD_DEBUG
get_fd_set().on_create_fd(fd_.get());
get_fd_set().on_create_fd(fd_);
#endif
}
NativeFd::NativeFd(Fd fd, bool nolog) : fd_(fd) {
#if TD_FD_DEBUG
get_fd_set().on_create_fd(fd_.get());
get_fd_set().on_create_fd(fd_);
#endif
}
@ -135,18 +139,26 @@ NativeFd::NativeFd(Fd fd, bool nolog) : fd_(fd) {
NativeFd::NativeFd(Socket socket) : fd_(reinterpret_cast<Fd>(socket)), is_socket_(true) {
VLOG(fd) << *this << " create";
#if TD_FD_DEBUG
get_fd_set().on_create_fd(fd_.get());
get_fd_set().on_create_fd(fd_);
#endif
}
#endif
NativeFd &NativeFd::operator=(NativeFd &&from) {
CHECK(this != &from);
close();
fd_ = std::move(from.fd_);
NativeFd::NativeFd(NativeFd &&other) : fd_(other.fd_) {
#if TD_PORT_WINDOWS
is_socket_ = from.is_socket_;
is_socket_ = other.is_socket_;
#endif
other.fd_ = empty_fd();
}
NativeFd &NativeFd::operator=(NativeFd &&other) {
CHECK(this != &other);
close();
fd_ = other.fd_;
#if TD_PORT_WINDOWS
is_socket_ = other.is_socket_;
#endif
other.fd_ = empty_fd();
return *this;
}
@ -155,7 +167,7 @@ NativeFd::~NativeFd() {
}
NativeFd::operator bool() const {
return fd_.get() != empty_fd();
return fd_ != empty_fd();
}
NativeFd::Fd NativeFd::empty_fd() {
@ -167,7 +179,7 @@ NativeFd::Fd NativeFd::empty_fd() {
}
NativeFd::Fd NativeFd::fd() const {
return fd_.get();
return fd_;
}
NativeFd::Socket NativeFd::socket() const {
@ -175,7 +187,7 @@ NativeFd::Socket NativeFd::socket() const {
return fd();
#elif TD_PORT_WINDOWS
CHECK(is_socket_);
return reinterpret_cast<Socket>(fd_.get());
return reinterpret_cast<Socket>(fd_);
#endif
}
@ -239,13 +251,13 @@ void NativeFd::close() {
auto error = OS_ERROR("Close fd");
LOG(ERROR) << error;
}
fd_ = {};
fd_ = empty_fd();
}
NativeFd::Fd NativeFd::release() {
VLOG(fd) << *this << " release";
auto res = fd_.get();
fd_ = {};
auto res = fd_;
fd_ = empty_fd();
#if TD_FD_DEBUG
get_fd_set().on_close_fd(res);
#endif

View file

@ -21,7 +21,6 @@
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#include "td/utils/MovableValue.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
@ -37,8 +36,6 @@ class NativeFd {
using Socket = SOCKET;
#endif
NativeFd() = default;
NativeFd(NativeFd &&) = default;
NativeFd &operator=(NativeFd &&);
explicit NativeFd(Fd fd);
NativeFd(Fd fd, bool nolog);
#if TD_PORT_WINDOWS
@ -46,12 +43,12 @@ class NativeFd {
#endif
NativeFd(const NativeFd &) = delete;
NativeFd &operator=(const NativeFd &) = delete;
NativeFd(NativeFd &&other);
NativeFd &operator=(NativeFd &&other);
~NativeFd();
explicit operator bool() const;
static Fd empty_fd();
Fd fd() const;
Socket socket() const;
@ -67,10 +64,10 @@ class NativeFd {
Status validate() const;
private:
#if TD_PORT_POSIX
MovableValue<Fd, -1> fd_;
#elif TD_PORT_WINDOWS
MovableValue<Fd, INVALID_HANDLE_VALUE> fd_;
static Fd empty_fd();
Fd fd_ = empty_fd();
#if TD_PORT_WINDOWS
bool is_socket_{false};
#endif
};

View file

@ -28,6 +28,8 @@ char disable_linker_warning_about_empty_file_poll_cpp TD_UNUSED;
#include "td/utils/ScopeGuard.h"
#include "td/utils/Status.h"
#include <cerrno>
namespace td {
namespace detail {

View file

@ -29,7 +29,6 @@
#include <atomic>
#include <memory>
#include <type_traits>
namespace td {
@ -220,30 +219,6 @@ inline const NativeFd &PollableFd::native_fd() const {
return fd_info_->native_fd();
}
#if TD_PORT_POSIX
namespace detail {
template <class F>
auto skip_eintr(F &&f) {
decltype(f()) res;
static_assert(std::is_integral<decltype(res)>::value, "integral type expected");
do {
errno = 0; // just in case
res = f();
} while (res < 0 && errno == EINTR);
return res;
}
template <class F>
auto skip_eintr_cstr(F &&f) {
char *res;
do {
errno = 0; // just in case
res = f();
} while (res == nullptr && errno == EINTR);
return res;
}
} // namespace detail
#endif
template <class FdT>
bool can_read(const FdT &fd) {
return fd.get_poll_info().get_flags().can_read() || fd.get_poll_info().get_flags().has_pending_error();

View file

@ -0,0 +1,114 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library 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.
TON Blockchain Library 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 TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/utils/port/detail/ThreadPthread.h"
char disable_linker_warning_about_empty_file_thread_pthread_cpp TD_UNUSED;
#if TD_THREAD_PTHREAD
#include "td/utils/misc.h"
#include <pthread.h>
#include <sched.h>
#if TD_FREEBSD || TD_OPENBSD || TD_NETBSD
#include <sys/sysctl.h>
#endif
#include <unistd.h>
namespace td {
namespace detail {
unsigned ThreadPthread::hardware_concurrency() {
// Linux and macOS
#if defined(_SC_NPROCESSORS_ONLN)
{
auto res = sysconf(_SC_NPROCESSORS_ONLN);
if (res > 0) {
return narrow_cast<unsigned>(res);
}
}
#endif
#if TD_FREEBSD || TD_OPENBSD || TD_NETBSD
#if defined(HW_AVAILCPU) && defined(CTL_HW)
{
int mib[2] = {CTL_HW, HW_AVAILCPU};
int res{0};
size_t len = sizeof(res);
if (sysctl(mib, 2, &res, &len, nullptr, 0) == 0 && res != 0) {
return res;
}
}
#endif
#if defined(HW_NCPU) && defined(CTL_HW)
{
int mib[2] = {CTL_HW, HW_NCPU};
int res{0};
size_t len = sizeof(res);
if (sysctl(mib, 2, &res, &len, nullptr, 0) == 0 && res != 0) {
return res;
}
}
#endif
#endif
// Just in case
return 8;
}
void ThreadPthread::set_name(CSlice name) {
#if defined(_GNU_SOURCE) && defined(__GLIBC_PREREQ)
#if __GLIBC_PREREQ(2, 12)
pthread_setname_np(thread_, name.c_str());
#endif
#endif
}
void ThreadPthread::join() {
if (is_inited_.get()) {
is_inited_ = false;
pthread_join(thread_, nullptr);
}
}
void ThreadPthread::detach() {
if (is_inited_.get()) {
is_inited_ = false;
pthread_detach(thread_);
}
}
int ThreadPthread::do_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *),
void *arg) {
return pthread_create(thread, attr, start_routine, arg);
}
namespace this_thread_pthread {
void yield() {
sched_yield();
}
ThreadPthread::id get_id() {
return pthread_self();
}
} // namespace this_thread_pthread
} // namespace detail
} // namespace td
#endif

View file

@ -34,8 +34,7 @@
#include <type_traits>
#include <utility>
#include <pthread.h>
#include <sched.h>
#include <sys/types.h>
namespace td {
namespace detail {
@ -44,7 +43,8 @@ class ThreadPthread {
ThreadPthread() = default;
ThreadPthread(const ThreadPthread &other) = delete;
ThreadPthread &operator=(const ThreadPthread &other) = delete;
ThreadPthread(ThreadPthread &&) = default;
ThreadPthread(ThreadPthread &&other) noexcept : is_inited_(std::move(other.is_inited_)), thread_(other.thread_) {
}
ThreadPthread &operator=(ThreadPthread &&other) {
join();
is_inited_ = std::move(other.is_inited_);
@ -58,36 +58,20 @@ class ThreadPthread {
invoke_tuple(std::move(args));
clear_thread_locals();
});
pthread_create(&thread_, nullptr, run_thread, func.release());
do_pthread_create(&thread_, nullptr, run_thread, func.release());
is_inited_ = true;
}
void set_name(CSlice name) {
#if defined(_GNU_SOURCE) && defined(__GLIBC_PREREQ)
#if __GLIBC_PREREQ(2, 12)
pthread_setname_np(thread_, name.c_str());
#endif
#endif
}
void join() {
if (is_inited_.get()) {
is_inited_ = false;
pthread_join(thread_, nullptr);
}
}
void detach() {
if (is_inited_.get()) {
is_inited_ = false;
pthread_detach(thread_);
}
}
~ThreadPthread() {
join();
}
static unsigned hardware_concurrency() {
return 8;
}
void set_name(CSlice name);
void join();
void detach();
static unsigned hardware_concurrency();
using id = pthread_t;
@ -100,6 +84,8 @@ class ThreadPthread {
return std::forward<T>(v);
}
int do_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
static void *run_thread(void *ptr) {
ThreadIdGuard thread_id_guard;
auto func = unique_ptr<Destructor>(static_cast<Destructor *>(ptr));
@ -108,12 +94,8 @@ class ThreadPthread {
};
namespace this_thread_pthread {
inline void yield() {
sched_yield();
}
inline ThreadPthread::id get_id() {
return pthread_self();
}
void yield();
ThreadPthread::id get_id();
} // namespace this_thread_pthread
} // namespace detail
} // namespace td

View file

@ -0,0 +1,52 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library 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.
TON Blockchain Library 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 TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include <cerrno>
#include <type_traits>
namespace td {
#if TD_PORT_POSIX
namespace detail {
template <class F>
auto skip_eintr(F &&f) {
decltype(f()) res;
static_assert(std::is_integral<decltype(res)>::value, "integral type expected");
do {
errno = 0; // just in case
res = f();
} while (res < 0 && errno == EINTR);
return res;
}
template <class F>
auto skip_eintr_cstr(F &&f) {
char *res;
do {
errno = 0; // just in case
res = f();
} while (res == nullptr && errno == EINTR);
return res;
}
} // namespace detail
#endif
} // namespace td

View file

@ -22,7 +22,7 @@
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/detail/skip_eintr.h"
#include "td/utils/ScopeGuard.h"
#if TD_PORT_WINDOWS
@ -55,6 +55,7 @@
#include <sys/syslimits.h>
#endif
#include <cerrno>
#include <cstdlib>
#include <string>
@ -68,8 +69,7 @@ Status set_temporary_dir(CSlice dir) {
input_dir += TD_DIR_SLASH;
}
TRY_STATUS(mkpath(input_dir, 0750));
TRY_RESULT(real_dir, realpath(input_dir));
temporary_dir = std::move(real_dir);
TRY_RESULT_ASSIGN(temporary_dir, realpath(input_dir));
return Status::OK();
}
@ -431,8 +431,7 @@ Result<string> realpath(CSlice slice, bool ignore_access_denied) {
return OS_ERROR(PSLICE() << "GetFullPathNameW failed for \"" << slice << '"');
}
} else {
TRY_RESULT(t_res, from_wstring(buf));
res = std::move(t_res);
TRY_RESULT_ASSIGN(res, from_wstring(buf));
}
if (res.empty()) {
return Status::Error("Empty path");

View file

@ -14,14 +14,19 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2019-2020 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "rlimit.h"
#if TD_LINUX || TD_ANDROID
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include "td/utils/port/rlimit.h"
#include "td/utils/port/config.h"
#include "td/utils/misc.h"
#if TD_PORT_POSIX
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#endif
namespace td {

View file

@ -16,10 +16,10 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/port/platform.h"
#include "td/utils/common.h"
#include "td/utils/Status.h"
namespace td {
@ -28,5 +28,4 @@ enum class RlimitType { nofile, rss };
td::Status change_rlimit(RlimitType rlim_type, td::uint64 value, td::uint64 cap = 0);
td::Status change_maximize_rlimit(RlimitType rlim, td::uint64 value);
} // namespace td

View file

@ -301,7 +301,7 @@ void signal_safe_write_pointer(void *p, bool add_header) {
char *ptr = end;
*--ptr = '\n';
do {
*--ptr = td::format::hex_digit(addr % 16);
*--ptr = format::hex_digit(addr % 16);
addr /= 16;
} while (addr != 0);
*--ptr = 'x';
@ -318,6 +318,7 @@ static void block_stdin() {
}
static void default_failure_signal_handler(int sig) {
Stacktrace::init();
signal_safe_write_signal_number(sig);
Stacktrace::PrintOptions options;

View file

@ -65,10 +65,12 @@ void print_backtrace_gdb(void) {
name_buf[res] = 0;
#if TD_LINUX
#if defined(PR_SET_DUMPABLE)
if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
signal_safe_write("Can't set dumpable\n");
return;
}
#endif
#if defined(PR_SET_PTRACER)
// We can't use event fd because we are in a signal handler
int fds[2];
@ -120,10 +122,18 @@ void print_backtrace_gdb(void) {
} // namespace
void Stacktrace::print_to_stderr(const PrintOptions &options) {
print_backtrace();
if (options.use_gdb) {
print_backtrace_gdb();
}
print_backtrace();
}
void Stacktrace::init() {
#if __GLIBC__
// backtrace needs to be called once to ensure that next calls are async-signal-safe
void *buffer[1];
backtrace(buffer, 1);
#endif
}
} // namespace td

View file

@ -28,6 +28,8 @@ class Stacktrace {
}
};
static void print_to_stderr(const PrintOptions &options = PrintOptions());
static void init();
};
} // namespace td

View file

@ -0,0 +1,172 @@
#include "td/utils/port/uname.h"
#include "td/utils/port/config.h"
#include "td/utils/common.h"
#include "td/utils/filesystem.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/Stat.h"
#if TD_PORT_POSIX
#if TD_ANDROID
#include <sys/system_properties.h>
#else
#if TD_DARWIN
#include <sys/sysctl.h>
#include <sys/types.h>
#endif
#include <sys/utsname.h>
#endif
#endif
namespace td {
#if TD_DARWIN || TD_LINUX
static string read_os_name(CSlice os_version_file_path, CSlice prefix, CSlice suffix) {
auto r_stat = stat(os_version_file_path);
if (r_stat.is_ok() && r_stat.ok().is_reg_ && r_stat.ok().size_ < (1 << 16)) {
auto r_file = read_file_str(os_version_file_path, r_stat.ok().size_);
if (r_file.is_ok()) {
auto begin_pos = r_file.ok().find(prefix.c_str());
if (begin_pos != string::npos) {
begin_pos += prefix.size();
auto end_pos = r_file.ok().find(suffix.c_str(), begin_pos);
if (end_pos != string::npos) {
auto os_version = trim(r_file.ok().substr(begin_pos, end_pos - begin_pos));
if (os_version.find("\n") == string::npos) {
return os_version;
}
}
}
}
}
return string();
}
#endif
Slice get_operating_system_version() {
static string result = []() -> string {
#if TD_DARWIN
char version[256];
size_t size = sizeof(version);
string os_version;
if (sysctlbyname("kern.osproductversion", version, &size, nullptr, 0) == 0) {
os_version = trim(string(version, size));
}
if (os_version.empty()) {
os_version = read_os_name("/System/Library/CoreServices/SystemVersion.plist",
"<key>ProductUserVisibleVersion</key>\n\t<string>", "</string>\n");
}
if (!os_version.empty()) {
os_version = " " + os_version;
}
#if TD_DARWIN_IOS
return "iOS" + os_version;
#elif TD_DARWIN_TV_OS
return "tvOS" + os_version;
#elif TD_DARWIN_WATCH_OS
return "watchOS" + os_version;
#elif TD_DARWIN_MAC
return "macOS" + os_version;
#else
return "Darwin" + os_version;
#endif
#elif TD_PORT_POSIX
#if TD_ANDROID
char version[PROP_VALUE_MAX + 1];
int length = __system_property_get("ro.build.version.release", version);
if (length > 0) {
return "Android " + string(version, length);
}
#else
#if TD_LINUX
auto os_name = read_os_name("/etc/os-release", "PRETTY_NAME=\"", "\"\n");
if (!os_name.empty()) {
return os_name;
}
#endif
utsname name;
int err = uname(&name);
if (err == 0) {
auto os_name = trim(PSTRING() << name.sysname << " " << name.release);
if (!os_name.empty()) {
return os_name;
}
}
#endif
LOG(ERROR) << "Failed to identify OS name; use generic one";
#if TD_ANDROID
return "Android";
#elif TD_TIZEN
return "Tizen";
#elif TD_LINUX
return "Linux";
#elif TD_FREEBSD
return "FreeBSD";
#elif TD_OPENBSD
return "OpenBSD";
#elif TD_NETBSD
return "NetBSD";
#elif TD_CYGWIN
return "Cygwin";
#elif TD_EMSCRIPTEN
return "Emscripten";
#else
return "Unix";
#endif
#else
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
auto handle = GetModuleHandle(L"ntdll.dll");
if (handle != nullptr) {
using RtlGetVersionPtr = LONG(WINAPI *)(PRTL_OSVERSIONINFOEXW);
RtlGetVersionPtr RtlGetVersion = reinterpret_cast<RtlGetVersionPtr>(GetProcAddress(handle, "RtlGetVersion"));
if (RtlGetVersion != nullptr) {
RTL_OSVERSIONINFOEXW os_version_info = {};
os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
if (RtlGetVersion(&os_version_info) == 0) {
auto major = os_version_info.dwMajorVersion;
auto minor = os_version_info.dwMinorVersion;
bool is_server = os_version_info.wProductType != VER_NT_WORKSTATION;
if (major == 10 && minor >= 0) {
if (is_server) {
return os_version_info.dwBuildNumber >= 17623 ? "Windows Server 2019" : "Windows Server 2016";
}
return "Windows 10";
}
if (major == 6 && minor == 3) {
return is_server ? "Windows Server 2012 R2" : "Windows 8.1";
}
if (major == 6 && minor == 2) {
return is_server ? "Windows Server 2012" : "Windows 8";
}
if (major == 6 && minor == 1) {
return is_server ? "Windows Server 2008 R2" : "Windows 7";
}
if (major == 6 && minor == 0) {
return is_server ? "Windows Server 2008" : "Windows Vista";
}
return is_server ? "Windows Server" : "Windows";
}
}
}
#elif TD_WINRT
return "Windows 10";
#endif
LOG(ERROR) << "Failed to identify OS name; use generic one";
return "Windows";
#endif
}();
return result;
}
} // namespace td

View file

@ -0,0 +1,9 @@
#pragma once
#include "td/utils/Slice.h"
namespace td {
Slice get_operating_system_version();
}

View file

@ -16,44 +16,53 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#include "user.h"
#if TD_LINUX
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include "td/utils/port/user.h"
#include "td/utils/port/config.h"
#if TD_PORT_POSIX
#include <grp.h>
#include <pwd.h>
#if TD_DARWIN || TD_FREEBSD || TD_NETBSD
#include <sys/param.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#endif
namespace td {
#if TD_LINUX
td::Status change_user(td::Slice user) {
struct passwd *pw;
if (getuid() != 0 || geteuid() != 0) {
return td::Status::PosixError(errno, "cannot setuid() as not root");
}
if ((pw = getpwnam(user.str().c_str())) == 0) {
return td::Status::PosixError(errno, PSTRING() << "bad user '" << user << "'");
Status change_user(CSlice username, CSlice groupname) {
#if TD_PORT_POSIX
passwd *pw = getpwnam(username.c_str());
if (pw == nullptr) {
return OS_ERROR(PSTRING() << "Can't find the user '" << username << "' to switch to");
}
uid_t uid = pw->pw_uid;
gid_t gid = pw->pw_gid;
if (setgroups(1, &gid) < 0) {
return td::Status::PosixError(errno, "failed to clear supplementary groups list");
if (setgroups(1, &gid) == -1) {
return OS_ERROR("Failed to clear supplementary group list");
}
if (initgroups(user.str().c_str(), gid) != 0) {
return td::Status::PosixError(errno, "failed to load groups of user");
if (!groupname.empty()) {
group *g = getgrnam(groupname.c_str());
if (g == nullptr) {
return OS_ERROR("Can't find the group to switch to");
}
gid = g->gr_gid;
} else if (initgroups(username.c_str(), gid) == -1) {
return OS_ERROR("Failed to load groups of user");
}
if (setgid(pw->pw_gid) < 0) {
return td::Status::PosixError(errno, "failed to setgid()");
if (setgid(gid) == -1) {
return OS_ERROR("failed to set effective group ID");
}
if (setuid(pw->pw_uid) < 0) {
return td::Status::PosixError(errno, "failed to setuid()");
if (setuid(uid) == -1) {
return OS_ERROR("failed to set effective user ID");
}
return td::Status::OK();
}
return Status::OK();
#else
td::Status change_user(td::Slice username) {
return td::Status::Error("not implemented");
}
return Status::Error("Changing effective user is not supported");
#endif
}
} // namespace td

View file

@ -16,14 +16,12 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/config.h"
#include "td/utils/port/platform.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
td::Status change_user(td::Slice username);
Status change_user(CSlice username, CSlice groupname = CSlice());
}

View file

@ -33,10 +33,7 @@ Result<std::wstring> to_wstring(CSlice slice) {
return Status::Error("Wrong encoding");
}
size_t wstring_len = 0;
for (auto c : slice) {
wstring_len += ((c & 0xc0) != 0x80) + ((c & 0xf8) == 0xf0);
}
size_t wstring_len = utf8_utf16_length(slice);
std::wstring result(wstring_len, static_cast<wchar_t>(0));
if (wstring_len) {