13 #ifndef __STOUT_WINDOWS_OS_HPP__
14 #define __STOUT_WINDOWS_OS_HPP__
16 #include <sys/utime.h>
67 COMPUTER_NAME_FORMAT
format = ComputerNamePhysicalDnsHostname;
69 if (::GetComputerNameExW(format,
nullptr, &size) == 0) {
70 if (::GetLastError() != ERROR_MORE_DATA) {
75 std::vector<wchar_t> buffer;
78 if (::GetComputerNameExW(format, buffer.data(), &
size) == 0) {
82 return stringify(std::wstring(buffer.data()));
94 DWORD max_items = 4096;
107 processes.resize(max_items);
108 size_in_bytes = processes.size() *
sizeof(
pid_t);
109 CHECK_LE(size_in_bytes, MAXDWORD);
111 BOOL result = ::EnumProcesses(
113 static_cast<DWORD
>(size_in_bytes),
117 return WindowsError(
"os::pids: Call to `EnumProcesses` failed");
121 }
while (bytes_returned >= size_in_bytes);
123 std::set<pid_t> pids_set(processes.begin(), processes.end());
145 const std::string& key,
146 const std::string& value,
147 bool overwrite =
true)
156 ::GetEnvironmentVariableW(wide_stringify(key).data(),
nullptr, 0) != 0 &&
157 ::GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
162 ::SetEnvironmentVariableW(
163 wide_stringify(key).data(), wide_stringify(value).data());
169 inline void unsetenv(
const std::string& key)
173 ::SetEnvironmentVariableW(wide_stringify(key).data(),
nullptr);
214 const bool wait_for_child = (options &
WNOHANG) == 0;
220 "os::waitpid: Value of pid is '" +
stringify(pid) +
221 "'; the Windows implementation currently does not allow values <= 0");
222 }
else if (options != 0 && options !=
WNOHANG) {
227 "os::waitpid: Only flag `WNOHANG` is implemented on Windows");
235 PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
237 static_cast<DWORD>(pid));
239 if (process ==
nullptr) {
240 return WindowsError(
"os::waitpid: Failed to open process for pid '" +
248 const DWORD wait_time = wait_for_child ? INFINITE : 0;
249 const DWORD wait_results = ::WaitForSingleObject(
250 scoped_process.get(),
254 const bool state_signaled = wait_results == WAIT_OBJECT_0;
255 if (options == 0 && !state_signaled) {
260 "os::waitpid: Failed to wait for pid '" +
stringify(pid) +
261 "'. `::WaitForSingleObject` should have waited for child process to " +
262 "exit, but returned code '" +
stringify(wait_results) +
264 }
else if (wait_for_child && !state_signaled &&
265 wait_results != WAIT_TIMEOUT) {
271 "os::waitpid: Failed to wait for pid '" +
stringify(pid) +
272 "'. `ENOHANG` flag was passed in, so `::WaitForSingleObject` should " +
273 "have either returned `WAIT_OBJECT_0` or `WAIT_TIMEOUT` (the " +
274 "timeout was set to 0, because we are not waiting for the child), " +
275 "but instead returned code '" +
stringify(wait_results) +
"'");
278 if (!wait_for_child && wait_results == WAIT_TIMEOUT) {
286 DWORD child_exit_code = 0;
287 if (!::GetExitCodeProcess(scoped_process.get(), &child_exit_code)) {
290 "os::waitpid: Successfully waited on child process with pid '" +
291 std::to_string(pid) +
"', but could not retrieve exit code");
294 if (status !=
nullptr) {
295 *status = child_exit_code;
303 inline std::string
hstrerror(
int err) =
delete;
309 const std::string& path,
310 bool recursive) =
delete;
317 const std::string& path,
328 ::Sleep(static_cast<DWORD>(duration.
ms()));
343 SYSTEM_INFO sys_info;
344 ::GetSystemInfo(&sys_info);
345 return static_cast<long>(sys_info.dwNumberOfProcessors);
358 "Failed to determine system load averages");
367 MEMORYSTATUSEX memory_status;
368 memory_status.dwLength =
sizeof(MEMORYSTATUSEX);
369 if (!::GlobalMemoryStatusEx(&memory_status)) {
370 return WindowsError(
"os::memory: Call to `GlobalMemoryStatusEx` failed");
373 memory.total =
Bytes(memory_status.ullTotalPhys);
374 memory.free =
Bytes(memory_status.ullAvailPhys);
375 memory.totalSwap =
Bytes(memory_status.ullTotalPageFile);
376 memory.freeSwap =
Bytes(memory_status.ullAvailPageFile);
389 inline tm*
gmtime_r(
const time_t* timep, tm* result)
391 return ::gmtime_s(result, timep) == ERROR_SUCCESS ? result :
nullptr;
400 HANDLE snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, pid);
401 if (snapshot_handle == INVALID_HANDLE_VALUE) {
403 "os::process_entry: Call to `CreateToolhelp32Snapshot` failed");
406 SharedHandle safe_snapshot_handle(snapshot_handle, ::CloseHandle);
410 memset(&process_entry, 0,
sizeof(process_entry));
415 SetLastError(ERROR_SUCCESS);
416 BOOL has_next = Process32First(safe_snapshot_handle.get(), &
process_entry);
417 if (has_next == FALSE) {
422 if (::GetLastError() != ERROR_SUCCESS) {
423 return WindowsError(
"os::process_entry: Call to `Process32First` failed");
425 return Error(
"os::process_entry: Call to `Process32First` failed");
430 while (has_next == TRUE) {
431 if (process_entry.th32ProcessID == pid) {
436 has_next = Process32Next(safe_snapshot_handle.get(), &
process_entry);
437 if (has_next == FALSE) {
438 DWORD last_error = ::GetLastError();
439 if (last_error != ERROR_NO_MORE_FILES && last_error != ERROR_SUCCESS) {
441 "os::process_entry: Call to `Process32Next` failed");
459 return Error(
"os::process: Invalid parameter: pid == 0");
467 }
else if (entry.
isNone()) {
471 HANDLE process_handle = ::OpenProcess(
472 PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ,
477 if (process_handle ==
nullptr) {
478 return WindowsError(
"os::process: Call to `OpenProcess` failed");
481 SharedHandle safe_process_handle(process_handle, ::CloseHandle);
484 PROCESS_MEMORY_COUNTERS proc_mem_counters;
485 BOOL get_process_memory_info = ::GetProcessMemoryInfo(
486 safe_process_handle.get_handle(),
488 sizeof(proc_mem_counters));
490 if (!get_process_memory_info) {
491 return WindowsError(
"os::process: Call to `GetProcessMemoryInfo` failed");
496 BOOL process_id_to_session_id = ::ProcessIdToSessionId(pid, &session_id);
498 if (!process_id_to_session_id) {
499 return WindowsError(
"os::process: Call to `ProcessIdToSessionId` failed");
503 FILETIME create_filetime, exit_filetime, kernel_filetime, user_filetime;
504 BOOL get_process_times = ::GetProcessTimes(
505 safe_process_handle.get_handle(),
511 if (!get_process_times) {
512 return WindowsError(
"os::process: Call to `GetProcessTimes` failed");
516 ULARGE_INTEGER lKernelTime, lUserTime;
517 lKernelTime.HighPart = kernel_filetime.dwHighDateTime;
518 lKernelTime.LowPart = kernel_filetime.dwLowDateTime;
519 lUserTime.HighPart = user_filetime.dwHighDateTime;
520 lUserTime.LowPart = user_filetime.dwLowDateTime;
527 entry.
get().th32ParentProcessID,
530 Bytes(proc_mem_counters.WorkingSetSize),
554 return wide_stringify(alpha_pid.
get());
564 const DWORD desired_access,
565 const BOOL inherit_handles,
566 const std::wstring&
name)
577 "os::open_job: Call to `OpenJobObject` failed for job: " +
587 const DWORD desired_access,
588 const BOOL inherit_handles,
596 return open_job(desired_access, inherit_handles, name.
get());
616 "os::create_job: Call to `CreateJobObject` failed for job: " +
620 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info = {};
627 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
629 const BOOL result = ::SetInformationJobObject(
631 JobObjectExtendedLimitInformation,
635 if (result == FALSE) {
637 "os::create_job: `SetInformationJobObject` failed for job: " +
660 JOBOBJECT_BASIC_ACCOUNTING_INFORMATION info = {};
662 BOOL result = ::QueryInformationJobObject(
664 JobObjectBasicAccountingInformation,
668 if (result == FALSE) {
670 "os::get_job_info: call to `QueryInformationJobObject` failed");
677 template <
size_t max_p
ids>
684 DWORD NumberOfAssignedProcesses;
685 DWORD NumberOfProcessIdsInList;
686 DWORD ProcessIdList[max_pids];
689 BOOL result = ::QueryInformationJobObject(
691 JobObjectBasicProcessIdList,
692 reinterpret_cast<JOBOBJECT_BASIC_PROCESS_ID_LIST*
>(&pid_list),
697 if (result == FALSE && ::GetLastError() == ERROR_MORE_DATA) {
701 if (result == FALSE) {
703 "os::_get_job_processes: call to `QueryInformationJobObject` failed");
707 for (DWORD i = 0; i < pid_list.NumberOfProcessIdsInList; ++i) {
710 processes.insert(process.
get());
732 os::_get_job_processes<32>(job_handle.
get());
735 }
else if (result.
isSome()) {
739 result = os::_get_job_processes<32*32>(job_handle.
get());
742 }
else if (result.
isSome()) {
746 result = os::_get_job_processes<32*32*32>(job_handle.
get());
749 }
else if (result.
isSome()) {
755 return Error(
"os::get_job_processes: failed to get processes");
765 return std::accumulate(
766 processes.
get().cbegin(),
767 processes.
get().cend(),
774 return bytes +
process.rss.get();
786 JOBOBJECT_CPU_RATE_CONTROL_INFORMATION control_info = {};
787 control_info.ControlFlags =
788 JOB_OBJECT_CPU_RATE_CONTROL_ENABLE |
789 JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP;
799 control_info.CpuRate =
800 static_cast<DWORD
>((cpus / total_cpus.
get()) * 100 * 100);
802 if (control_info.CpuRate < 1) {
803 control_info.CpuRate = 1;
807 JOB_OBJECT_SET_ATTRIBUTES,
814 BOOL result = ::SetInformationJobObject(
816 JobObjectCpuRateControlInformation,
818 sizeof(control_info));
819 if (result == FALSE) {
821 "os::set_job_cpu_limit: call to `SetInformationJobObject` failed");
835 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info = {};
836 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_JOB_MEMORY;
837 info.JobMemoryLimit = limit.
bytes();
840 JOB_OBJECT_SET_ATTRIBUTES,
847 BOOL result = ::SetInformationJobObject(
849 JobObjectExtendedLimitInformation,
852 if (result == FALSE) {
854 "os::set_job_mem_limit: call to `SetInformationJobObject` failed");
869 PROCESS_SET_QUOTA | PROCESS_TERMINATE,
876 "os::assign_job: Call to `OpenProcess` failed");
879 const BOOL result = ::AssignProcessToJobObject(
883 if (result == FALSE) {
885 "os::assign_job: Call to `AssignProcessToJobObject` failed");
897 const BOOL result = ::TerminateJobObject(
902 if (result == FALSE) {
904 "os::kill_job: Call to `TerminateJobObject` failed");
916 if (::GetAllUsersProfileDirectoryW(
nullptr, &size)) {
920 "os::var: `GetAllUsersProfileDirectoryW` succeeded unexpectedly");
923 std::vector<wchar_t> buffer;
924 buffer.reserve(static_cast<size_t>(size));
925 if (!::GetAllUsersProfileDirectoryW(buffer.data(), &
size)) {
926 return WindowsError(
"os::var: `GetAllUsersProfileDirectoryW` failed");
929 return stringify(std::wstring(buffer.data()));
941 const std::string system_root = system_root_env.
isSome()
942 ? system_root_env.
get()
949 path::join(system_root,
"System32",
"WindowsPowerShell",
"v1.0"));
954 #endif // __STOUT_WINDOWS_OS_HPP__
bool isNone() const
Definition: result.hpp:112
Try< uid_t > uid(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:182
Definition: nothing.hpp:16
Definition: errorbase.hpp:35
Try< Bytes > size(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:100
HANDLE get_handle() const
Definition: windows.hpp:96
Definition: windows.hpp:78
std::stringstream & join(std::stringstream &stream, const std::string &separator, T &&...args)
Definition: strings.hpp:306
Try< Nothing > chmod(const std::string &path, int mode)
Definition: os.hpp:210
Try< Nothing > sleep(const Duration &duration)
Definition: os.hpp:234
static Result< T > error(const std::string &message)
Definition: result.hpp:53
Try< std::list< Process > > processes()
Definition: os.hpp:182
Try< Nothing > mknod(const std::string &path, mode_t mode, dev_t dev)
Definition: os.hpp:220
Result< ProcessStatus > status(pid_t pid)
Definition: proc.hpp:166
Definition: error.hpp:106
#define WNOHANG
Definition: windows.hpp:407
double ms() const
Definition: duration.hpp:100
Definition: errorbase.hpp:49
std::string join(const std::string &path1, const std::string &path2, const char _separator=os::PATH_SEPARATOR)
Definition: path.hpp:56
void setenv(const std::string &key, const std::string &value, bool overwrite=true)
Definition: os.hpp:157
Try< std::wstring > name_job(pid_t pid)
Definition: os.hpp:549
void unsetenv(const std::string &key)
Definition: os.hpp:167
Definition: duration.hpp:32
Definition: result.hpp:40
Try< Nothing > set_job_mem_limit(pid_t pid, Bytes limit)
Definition: os.hpp:833
std::string host_default_path()
Definition: os.hpp:489
bool isSome() const
Definition: option.hpp:115
Try< Nothing > assign_job(SharedHandle job_handle, pid_t pid)
Definition: os.hpp:864
Try< SharedHandle > open_job(const DWORD desired_access, const BOOL inherit_handles, const std::wstring &name)
Definition: os.hpp:563
Try< Load > loadavg()
Definition: os.hpp:295
Try< std::string > nodename()
Definition: os.hpp:58
DWORD pid_t
Definition: windows.hpp:187
Definition: process.hpp:32
int mode_t
Definition: windows.hpp:183
Try< dev_t > dev(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:139
Try< SharedHandle > create_job(const std::wstring &name)
Definition: os.hpp:605
constexpr Handle HANDLE
Definition: ingress.hpp:37
Try< Nothing > utime(const std::string &path)
Definition: utime.hpp:32
Try< long > cpus()
Definition: os.hpp:280
std::string hstrerror(int err)=delete
Try< Nothing > kill_job(SharedHandle job_handle)
Definition: os.hpp:895
int uid_t
Definition: windows.hpp:189
static Option< T > none()
Definition: option.hpp:31
bool isSome() const
Definition: try.hpp:70
const T & get() const &
Definition: option.hpp:118
Option< std::string > getenv(const std::string &key)
Definition: getenv.hpp:29
int random()
Definition: os.hpp:538
Try< Version > release()
Definition: os.hpp:393
const T & get() const
Definition: result.hpp:115
Result< pid_t > waitpid(pid_t pid, int *status, int options)
Definition: os.hpp:141
Try< UTSInfo > uname()
Definition: os.hpp:312
static Try error(const E &e)
Definition: try.hpp:42
Result< Process > process(pid_t pid)
Definition: freebsd.hpp:30
bool isError() const
Definition: try.hpp:71
Try< Bytes > get_job_mem(pid_t pid)
Definition: os.hpp:759
Result< std::set< Process > > _get_job_processes(const SharedHandle &job_handle)
Definition: os.hpp:678
uint64_t bytes() const
Definition: bytes.hpp:79
Try< std::set< Process > > get_job_processes(pid_t pid)
Definition: os.hpp:718
Try< Nothing > chown(uid_t uid, gid_t gid, const std::string &path, bool recursive)
Definition: chown.hpp:30
Try< mode_t > mode(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:126
Try< JOBOBJECT_BASIC_ACCOUNTING_INFORMATION > get_job_info(pid_t pid)
Definition: os.hpp:650
bool isSome() const
Definition: result.hpp:111
bool isError() const
Definition: result.hpp:113
Try< Nothing > set_job_cpu_limit(pid_t pid, double cpus)
Definition: os.hpp:784
int gid_t
Definition: windows.hpp:190
Try< std::string > format(const std::string &fmt, va_list args)
Definition: format.hpp:68
Result< PROCESSENTRY32W > process_entry(pid_t pid)
Definition: os.hpp:395
Definition: duration.hpp:217
Try< std::string > var()
Definition: os.hpp:422
std::string stringify(int flags)
Try< Memory > memory()
Definition: freebsd.hpp:78
Try< std::list< std::string > > glob(const std::string &pattern)
Definition: os.hpp:254
Try< std::set< pid_t > > pids()
Definition: freebsd.hpp:62
const T & get() const
Definition: try.hpp:73
constexpr const char * name
Definition: shell.hpp:41
tm * gmtime_r(const time_t *timep, tm *result)
Definition: os.hpp:389