mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-11-13 20:25:53 +08:00
[fix] apply macOS daemon settings immediately
This commit is contained in:
1 parent
fadc8f15ab
commit
81fc8463dc
12 files changed
+417
-396
No files matched your search
@@ -1,56 +0,0 @@
|
||||
#include "platform/daemon_backend.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace crossdesk::platform {
|
||||
|
||||
volatile std::sig_atomic_t g_daemon_stop_requested = 0;
|
||||
|
||||
void ResetDaemonStop() { g_daemon_stop_requested = 0; }
|
||||
|
||||
void RequestDaemonStop() { g_daemon_stop_requested = 1; }
|
||||
|
||||
bool DaemonStopRequested() { return g_daemon_stop_requested != 0; }
|
||||
|
||||
DaemonChildResult RunDaemonChild(const std::string& executable_path,
|
||||
const std::function<bool()>& keep_running) {
|
||||
DaemonChildResult result;
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
execl(executable_path.c_str(), executable_path.c_str(), "--child",
|
||||
nullptr);
|
||||
_exit(1);
|
||||
}
|
||||
if (child < 0) return result;
|
||||
|
||||
result.started = true;
|
||||
int status = 0;
|
||||
pid_t waited = -1;
|
||||
while (keep_running()) {
|
||||
waited = waitpid(child, &status, WNOHANG);
|
||||
if (waited == child || (waited < 0 && errno != EINTR)) break;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
}
|
||||
if (!keep_running() && waited != child) {
|
||||
kill(child, SIGTERM);
|
||||
waited = waitpid(child, &status, 0);
|
||||
}
|
||||
if (waited < 0) return result;
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
result.exit_code = WEXITSTATUS(status);
|
||||
result.normal_exit = result.exit_code == 0;
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
result.terminated_by_signal = true;
|
||||
result.exit_code = WTERMSIG(status);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace crossdesk::platform
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef CROSSDESK_PLATFORM_DAEMON_BACKEND_H_
|
||||
#define CROSSDESK_PLATFORM_DAEMON_BACKEND_H_
|
||||
|
||||
#include <csignal>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace crossdesk::platform {
|
||||
|
||||
extern volatile std::sig_atomic_t g_daemon_stop_requested;
|
||||
|
||||
struct DaemonChildResult {
|
||||
bool started = false;
|
||||
bool normal_exit = false;
|
||||
bool terminated_by_signal = false;
|
||||
int exit_code = 0;
|
||||
};
|
||||
|
||||
void ResetDaemonStop();
|
||||
void RequestDaemonStop();
|
||||
bool DaemonStopRequested();
|
||||
bool PrepareDaemon();
|
||||
std::string GetDaemonExecutablePath();
|
||||
DaemonChildResult RunDaemonChild(const std::string& executable_path,
|
||||
const std::function<bool()>& keep_running);
|
||||
|
||||
} // namespace crossdesk::platform
|
||||
|
||||
#endif // CROSSDESK_PLATFORM_DAEMON_BACKEND_H_
|
||||
@@ -1,58 +0,0 @@
|
||||
#include "platform/daemon_backend.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
namespace crossdesk::platform {
|
||||
|
||||
bool PrepareDaemon() {
|
||||
const bool from_terminal =
|
||||
isatty(STDIN_FILENO) != 0 || isatty(STDOUT_FILENO) != 0;
|
||||
pid_t process = fork();
|
||||
if (process < 0) return false;
|
||||
if (process > 0) _exit(0);
|
||||
if (setsid() < 0) return false;
|
||||
|
||||
process = fork();
|
||||
if (process < 0) return false;
|
||||
if (process > 0) _exit(0);
|
||||
|
||||
umask(0);
|
||||
if (chdir("/") != 0) {
|
||||
std::cerr << "Failed to change daemon working directory to /: "
|
||||
<< std::strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const int null_fd = open("/dev/null", O_RDWR);
|
||||
if (null_fd >= 0) {
|
||||
dup2(null_fd, STDIN_FILENO);
|
||||
if (!from_terminal) {
|
||||
dup2(null_fd, STDOUT_FILENO);
|
||||
dup2(null_fd, STDERR_FILENO);
|
||||
}
|
||||
if (null_fd > STDERR_FILENO) close(null_fd);
|
||||
}
|
||||
|
||||
signal(SIGTERM, [](int) { g_daemon_stop_requested = 1; });
|
||||
signal(SIGINT, [](int) { g_daemon_stop_requested = 1; });
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetDaemonExecutablePath() {
|
||||
char path[PATH_MAX] = {};
|
||||
const ssize_t count = readlink("/proc/self/exe", path, sizeof(path) - 1);
|
||||
return count > 0 ? std::string(path, static_cast<size_t>(count))
|
||||
: std::string();
|
||||
}
|
||||
|
||||
} // namespace crossdesk::platform
|
||||
@@ -1,20 +0,0 @@
|
||||
#include "platform/daemon_backend.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace crossdesk::platform {
|
||||
|
||||
bool PrepareDaemon() { return true; }
|
||||
|
||||
std::string GetDaemonExecutablePath() {
|
||||
char path[PATH_MAX] = {};
|
||||
uint32_t size = sizeof(path);
|
||||
if (_NSGetExecutablePath(path, &size) != 0) return {};
|
||||
char resolved[PATH_MAX] = {};
|
||||
return realpath(path, resolved) ? std::string(resolved) : std::string(path);
|
||||
}
|
||||
|
||||
} // namespace crossdesk::platform
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef CROSSDESK_PLATFORM_MACOS_RUNTIME_DAEMON_H_
|
||||
#define CROSSDESK_PLATFORM_MACOS_RUNTIME_DAEMON_H_
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace crossdesk::platform {
|
||||
|
||||
inline constexpr char kMacDaemonArgument[] = "--macos-daemon";
|
||||
|
||||
// Attach a separate crash watcher to the running GUI without restarting it or
|
||||
// forking its threads. The watcher shares the GUI's login session.
|
||||
class MacRuntimeDaemon {
|
||||
public:
|
||||
static MacRuntimeDaemon& Instance();
|
||||
~MacRuntimeDaemon();
|
||||
|
||||
void Initialize(int argc, char* argv[]);
|
||||
bool SetEnabled(bool enabled, std::string& error);
|
||||
// Normal exit disarms the watcher; a failed startup lets it restart after
|
||||
// this process has exited and released its single-instance leases.
|
||||
void Shutdown(bool restart = false);
|
||||
|
||||
private:
|
||||
MacRuntimeDaemon() = default;
|
||||
void Stop();
|
||||
std::mutex mutex_;
|
||||
bool initialized_ = false;
|
||||
std::vector<std::string> arguments_;
|
||||
int socket_ = -1;
|
||||
int child_ = -1;
|
||||
};
|
||||
|
||||
// Dispatch on the main thread before logging, GUI setup or instance locks.
|
||||
int RunMacRuntimeDaemon(int argc, char* argv[]);
|
||||
|
||||
} // namespace crossdesk::platform
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,334 @@
|
||||
#include "platform/macos/runtime_daemon.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
extern char** environ;
|
||||
|
||||
namespace crossdesk::platform {
|
||||
namespace {
|
||||
|
||||
constexpr int kControlSocket = 3;
|
||||
constexpr int kStartupTimeoutMs = 3000;
|
||||
constexpr int kPollIntervalMs = 100;
|
||||
|
||||
struct Descriptor {
|
||||
int value = -1;
|
||||
~Descriptor() { if (value >= 0) close(value); }
|
||||
};
|
||||
|
||||
std::string ExecutablePath() {
|
||||
uint32_t size = 0;
|
||||
_NSGetExecutablePath(nullptr, &size);
|
||||
std::vector<char> path(size);
|
||||
if (!size || _NSGetExecutablePath(path.data(), &size) != 0) return {};
|
||||
char* resolved = realpath(path.data(), nullptr);
|
||||
if (!resolved) return {};
|
||||
const std::string result(resolved);
|
||||
std::free(resolved);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ConfigureSocket(int socket) {
|
||||
const int enabled = 1;
|
||||
return fcntl(socket, F_SETFD, FD_CLOEXEC) == 0 &&
|
||||
setsockopt(socket, SOL_SOCKET, SO_NOSIGPIPE, &enabled,
|
||||
sizeof(enabled)) == 0;
|
||||
}
|
||||
|
||||
bool Send(int socket, char command) {
|
||||
ssize_t count;
|
||||
do {
|
||||
count = send(socket, &command, 1, 0);
|
||||
} while (count < 0 && errno == EINTR);
|
||||
return count == 1;
|
||||
}
|
||||
|
||||
bool Receive(int socket, char expected) {
|
||||
pollfd fd{socket, POLLIN, 0};
|
||||
int result;
|
||||
do {
|
||||
result = poll(&fd, 1, kStartupTimeoutMs);
|
||||
} while (result < 0 && errno == EINTR);
|
||||
if (result <= 0) return false;
|
||||
char reply = 0;
|
||||
return recv(socket, &reply, 1, 0) == 1 && reply == expected;
|
||||
}
|
||||
|
||||
void Reap(int child) {
|
||||
if (child < 0) return;
|
||||
const auto deadline = std::chrono::steady_clock::now() +
|
||||
std::chrono::seconds(2);
|
||||
while (std::chrono::steady_clock::now() < deadline) {
|
||||
const auto result = waitpid(child, nullptr, WNOHANG);
|
||||
if (result == child || (result < 0 && errno != EINTR)) return;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
// Still an unreaped direct child: its PID cannot have been reused.
|
||||
kill(child, SIGKILL);
|
||||
while (waitpid(child, nullptr, 0) < 0 && errno == EINTR) {}
|
||||
}
|
||||
|
||||
class LoginSession {
|
||||
public:
|
||||
LoginSession() {
|
||||
// NSWorkspace does not require creating NSApplication or another Dock icon.
|
||||
// Only logout/poweroff ends monitoring; sleep, lock and user switching do
|
||||
// not end the login session.
|
||||
center_ = NSWorkspace.sharedWorkspace.notificationCenter;
|
||||
const auto ending = ending_;
|
||||
observer_ = [center_ addObserverForName:NSWorkspaceWillPowerOffNotification
|
||||
object:nil queue:nil
|
||||
usingBlock:^(NSNotification*) {
|
||||
ending->store(true);
|
||||
}];
|
||||
}
|
||||
~LoginSession() { if (observer_) [center_ removeObserver:observer_]; }
|
||||
|
||||
bool Alive() {
|
||||
// The watcher has no GUI event loop. Deliver workspace notifications even
|
||||
// while waiting for process teardown or delaying a crash restart.
|
||||
@autoreleasepool {
|
||||
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) ==
|
||||
kCFRunLoopRunHandledSource) {}
|
||||
return observer_ != nil && !ending_->load();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::atomic<bool>> ending_ =
|
||||
std::make_shared<std::atomic<bool>>(false);
|
||||
NSNotificationCenter* center_ = nil;
|
||||
id observer_ = nil;
|
||||
};
|
||||
|
||||
int Watch(int argc, char* argv[]) {
|
||||
Descriptor control{kControlSocket};
|
||||
int type = 0;
|
||||
socklen_t size = sizeof(type);
|
||||
uid_t user = 0;
|
||||
gid_t group = 0;
|
||||
const pid_t owner = getppid();
|
||||
if (owner <= 1 ||
|
||||
getsockopt(control.value, SOL_SOCKET, SO_TYPE, &type, &size) != 0 ||
|
||||
type != SOCK_STREAM || getpeereid(control.value, &user, &group) != 0 ||
|
||||
user != geteuid() || !ConfigureSocket(control.value)) return 1;
|
||||
|
||||
Descriptor events{kqueue()};
|
||||
struct kevent change{};
|
||||
EV_SET(&change, owner, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, nullptr);
|
||||
if (events.value < 0 || fcntl(events.value, F_SETFD, FD_CLOEXEC) != 0 ||
|
||||
kevent(events.value, &change, 1, nullptr, 0, nullptr) != 0) return 1;
|
||||
|
||||
LoginSession session;
|
||||
if (!session.Alive() || !Send(control.value, 'R')) return 1;
|
||||
bool armed = false;
|
||||
for (;;) {
|
||||
if (!session.Alive()) return 0;
|
||||
pollfd fd{control.value, POLLIN, 0};
|
||||
const int ready = poll(&fd, 1, kPollIntervalMs);
|
||||
if (ready < 0 && errno == EINTR) continue;
|
||||
if (ready < 0) return 1;
|
||||
if (ready == 0) continue;
|
||||
char command = 0;
|
||||
const auto count = recv(control.value, &command, 1, 0);
|
||||
if (count < 0 && errno == EINTR) continue;
|
||||
if (count == 0 || (count < 0 && errno == ECONNRESET)) break;
|
||||
if (count < 0) return 1;
|
||||
if (command == 'S') return 0;
|
||||
if (command != 'A' || armed) return 1;
|
||||
armed = true;
|
||||
// Drain a queued stop even if the GUI disappears before receiving this.
|
||||
Send(control.value, 'A');
|
||||
}
|
||||
if (!armed) return 0;
|
||||
|
||||
// Closing the control socket can precede the GUI's destructors. kqueue
|
||||
// watches the original process instance, so PID reuse cannot fool this wait.
|
||||
for (;;) {
|
||||
if (!session.Alive()) return 0;
|
||||
struct kevent event{};
|
||||
const timespec timeout{0, kPollIntervalMs * 1000000};
|
||||
const int count = kevent(events.value, nullptr, 0, &event, 1, &timeout);
|
||||
if (count < 0 && errno == EINTR) continue;
|
||||
if (count < 0 || (count > 0 && (event.flags & EV_ERROR))) return 1;
|
||||
if (count > 0 && (event.fflags & NOTE_EXIT)) break;
|
||||
}
|
||||
const auto restart_at = std::chrono::steady_clock::now() +
|
||||
std::chrono::seconds(1);
|
||||
do {
|
||||
if (!session.Alive()) return 0;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(kPollIntervalMs));
|
||||
} while (std::chrono::steady_clock::now() < restart_at);
|
||||
if (!session.Alive()) return 0;
|
||||
|
||||
std::vector<char*> arguments{argv[0]};
|
||||
for (int i = 2; i < argc; ++i) arguments.push_back(argv[i]);
|
||||
arguments.push_back(nullptr);
|
||||
execv(argv[0], arguments.data());
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MacRuntimeDaemon& MacRuntimeDaemon::Instance() {
|
||||
static MacRuntimeDaemon daemon;
|
||||
return daemon;
|
||||
}
|
||||
|
||||
MacRuntimeDaemon::~MacRuntimeDaemon() { Shutdown(); }
|
||||
|
||||
void MacRuntimeDaemon::Initialize(int argc, char* argv[]) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
Stop();
|
||||
initialized_ = true;
|
||||
arguments_.clear();
|
||||
const auto executable = ExecutablePath();
|
||||
if (executable.empty()) return;
|
||||
arguments_.push_back(executable);
|
||||
arguments_.emplace_back(kMacDaemonArgument);
|
||||
for (int i = 1; i < argc; ++i) arguments_.emplace_back(argv[i]);
|
||||
}
|
||||
|
||||
bool MacRuntimeDaemon::SetEnabled(bool enabled, std::string& error) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
error.clear();
|
||||
// Configuration-only callers do not own a GUI watcher.
|
||||
if (!initialized_) return true;
|
||||
if (!enabled) {
|
||||
Stop();
|
||||
return true;
|
||||
}
|
||||
if (child_ >= 0) {
|
||||
pollfd fd{socket_, POLLIN, 0};
|
||||
if (poll(&fd, 1, 0) == 0) return true;
|
||||
Stop();
|
||||
}
|
||||
if (arguments_.empty()) {
|
||||
error = "Cannot resolve the executable for the daemon";
|
||||
return false;
|
||||
}
|
||||
int sockets[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
|
||||
error = std::strerror(errno);
|
||||
return false;
|
||||
}
|
||||
// Keep IPC away from stdio and the fixed child descriptor, including when
|
||||
// the application was launched with one or more standard streams closed.
|
||||
Descriptor parent{fcntl(sockets[0], F_DUPFD_CLOEXEC, kControlSocket + 1)};
|
||||
Descriptor child_socket{fcntl(sockets[1], F_DUPFD_CLOEXEC, kControlSocket + 1)};
|
||||
const int duplicate_error = errno;
|
||||
close(sockets[0]);
|
||||
close(sockets[1]);
|
||||
if (parent.value < 0 || child_socket.value < 0) {
|
||||
error = std::strerror(duplicate_error);
|
||||
return false;
|
||||
}
|
||||
if (!ConfigureSocket(parent.value) || !ConfigureSocket(child_socket.value)) {
|
||||
error = std::strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
posix_spawn_file_actions_t actions;
|
||||
posix_spawnattr_t attributes;
|
||||
int result = posix_spawn_file_actions_init(&actions);
|
||||
const bool actions_ready = result == 0;
|
||||
bool attributes_ready = false;
|
||||
if (result == 0) {
|
||||
result = posix_spawnattr_init(&attributes);
|
||||
attributes_ready = result == 0;
|
||||
}
|
||||
if (result == 0)
|
||||
result = posix_spawn_file_actions_adddup2(&actions, child_socket.value,
|
||||
kControlSocket);
|
||||
// Keep only the control socket and standard streams. In particular, never
|
||||
// retain GUI sockets, files or single-instance locks after a crash.
|
||||
for (int fd = 0; result == 0 && fd <= STDERR_FILENO; ++fd) {
|
||||
result = fcntl(fd, F_GETFD) >= 0
|
||||
? posix_spawn_file_actions_addinherit_np(&actions, fd)
|
||||
: posix_spawn_file_actions_addopen(&actions, fd, "/dev/null", O_RDWR, 0);
|
||||
}
|
||||
sigset_t empty, defaults;
|
||||
sigemptyset(&empty);
|
||||
sigfillset(&defaults);
|
||||
if (result == 0) result = posix_spawnattr_setsigmask(&attributes, &empty);
|
||||
if (result == 0) result = posix_spawnattr_setsigdefault(&attributes, &defaults);
|
||||
if (result == 0) result = posix_spawnattr_setpgroup(&attributes, 0);
|
||||
if (result == 0)
|
||||
result = posix_spawnattr_setflags(&attributes,
|
||||
POSIX_SPAWN_CLOEXEC_DEFAULT | POSIX_SPAWN_SETPGROUP |
|
||||
POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);
|
||||
std::vector<char*> args;
|
||||
for (auto& argument : arguments_) args.push_back(argument.data());
|
||||
args.push_back(nullptr);
|
||||
pid_t child = -1;
|
||||
if (result == 0)
|
||||
result = posix_spawn(&child, arguments_[0].c_str(), &actions, &attributes,
|
||||
args.data(), environ);
|
||||
if (attributes_ready) posix_spawnattr_destroy(&attributes);
|
||||
if (actions_ready) posix_spawn_file_actions_destroy(&actions);
|
||||
if (result != 0) {
|
||||
error = "Cannot start daemon: " + std::string(std::strerror(result));
|
||||
return false;
|
||||
}
|
||||
close(child_socket.value);
|
||||
child_socket.value = -1;
|
||||
socket_ = parent.value;
|
||||
parent.value = -1;
|
||||
child_ = child;
|
||||
if (!Receive(socket_, 'R') || !Send(socket_, 'A') || !Receive(socket_, 'A')) {
|
||||
error = "Daemon did not acknowledge startup";
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacRuntimeDaemon::Stop() {
|
||||
if (socket_ >= 0) {
|
||||
Send(socket_, 'S');
|
||||
close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
Reap(child_);
|
||||
child_ = -1;
|
||||
}
|
||||
|
||||
void MacRuntimeDaemon::Shutdown(bool restart) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (!restart) {
|
||||
Stop();
|
||||
} else {
|
||||
if (socket_ >= 0) close(socket_);
|
||||
socket_ = -1;
|
||||
child_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int RunMacRuntimeDaemon(int argc, char* argv[]) {
|
||||
if (argc < 2 || std::strcmp(argv[1], kMacDaemonArgument) != 0 ||
|
||||
argv[0][0] != '/') return 1;
|
||||
@autoreleasepool {
|
||||
return Watch(argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crossdesk::platform
|
||||
@@ -1,65 +0,0 @@
|
||||
#include "platform/daemon_backend.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace crossdesk::platform {
|
||||
namespace {
|
||||
|
||||
std::atomic<bool> g_stop_requested{false};
|
||||
|
||||
} // namespace
|
||||
|
||||
void ResetDaemonStop() { g_stop_requested.store(false); }
|
||||
|
||||
void RequestDaemonStop() { g_stop_requested.store(true); }
|
||||
|
||||
bool DaemonStopRequested() { return g_stop_requested.load(); }
|
||||
|
||||
bool PrepareDaemon() { return true; }
|
||||
|
||||
std::string GetDaemonExecutablePath() {
|
||||
wchar_t path[32768] = {};
|
||||
const DWORD length = GetModuleFileNameW(nullptr, path, 32768);
|
||||
return length > 0 && length < 32768
|
||||
? std::filesystem::path(path, path + length).u8string()
|
||||
: std::string();
|
||||
}
|
||||
|
||||
DaemonChildResult RunDaemonChild(const std::string& executable_path,
|
||||
const std::function<bool()>& keep_running) {
|
||||
DaemonChildResult result;
|
||||
STARTUPINFOW startup = {sizeof(startup)};
|
||||
PROCESS_INFORMATION process = {};
|
||||
const auto path = std::filesystem::u8path(executable_path).wstring();
|
||||
std::wstring command = L"\"" + path + L"\" --child";
|
||||
if (!CreateProcessW(path.c_str(), command.data(), nullptr, nullptr, FALSE,
|
||||
0, nullptr, nullptr, &startup, &process)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.started = true;
|
||||
while (keep_running()) {
|
||||
const DWORD wait_result = WaitForSingleObject(process.hProcess, 200);
|
||||
if (wait_result == WAIT_OBJECT_0 || wait_result == WAIT_FAILED) break;
|
||||
}
|
||||
if (!keep_running()) {
|
||||
TerminateProcess(process.hProcess, 1);
|
||||
WaitForSingleObject(process.hProcess, 3000);
|
||||
}
|
||||
|
||||
DWORD exit_code = 0;
|
||||
GetExitCodeProcess(process.hProcess, &exit_code);
|
||||
result.exit_code = static_cast<int>(exit_code);
|
||||
result.normal_exit = exit_code == 0;
|
||||
CloseHandle(process.hProcess);
|
||||
CloseHandle(process.hThread);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace crossdesk::platform
|
||||
Reference in new issue
Block a user