[fix] apply macOS daemon settings immediately

This commit is contained in:
dijunkun
2026-11-10 22:53:08 +08:00
parent fadc8f15ab
commit 81fc8463dc
12 changed files with 417 additions and 396 deletions
-76
View File
@@ -1,76 +0,0 @@
#include "daemon.h"
#include <chrono>
#include <iostream>
#include <thread>
#include <utility>
#include "platform/daemon_backend.h"
namespace {
constexpr int kRestartDelayMs = 1000;
} // namespace
Daemon::Daemon(const std::string& name) : name_(name), running_(false) {}
void Daemon::stop() {
running_.store(false);
crossdesk::platform::RequestDaemonStop();
}
bool Daemon::isRunning() const {
return running_.load() && !crossdesk::platform::DaemonStopRequested();
}
bool Daemon::start(MainLoopFunc loop) {
crossdesk::platform::ResetDaemonStop();
if (!crossdesk::platform::PrepareDaemon()) return false;
running_.store(true);
return runWithRestart(std::move(loop));
}
bool Daemon::runWithRestart(MainLoopFunc loop) {
int restart_count = 0;
const std::string executable_path =
crossdesk::platform::GetDaemonExecutablePath();
if (executable_path.empty()) {
std::cerr
<< "Failed to get executable path, falling back to direct execution"
<< std::endl;
while (isRunning()) {
try {
loop();
break;
} catch (...) {
++restart_count;
std::cerr << "Exception caught, restarting... (attempt "
<< restart_count << ")" << std::endl;
std::this_thread::sleep_for(
std::chrono::milliseconds(kRestartDelayMs));
}
}
return true;
}
while (isRunning()) {
const crossdesk::platform::DaemonChildResult result =
crossdesk::platform::RunDaemonChild(
executable_path, [this] { return isRunning(); });
if (!isRunning() || result.normal_exit) break;
++restart_count;
if (!result.started) {
std::cerr << "Failed to create child process";
} else if (result.terminated_by_signal) {
std::cerr << "Child process crashed with signal " << result.exit_code;
} else {
std::cerr << "Child process exited with code " << result.exit_code;
}
std::cerr << ", restarting... (attempt " << restart_count << ")"
<< std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(kRestartDelayMs));
}
return true;
}
-33
View File
@@ -1,33 +0,0 @@
/*
* @Author: DI JUNKUN
* @Date: 2025-11-19
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _DAEMON_H_
#define _DAEMON_H_
#include <atomic>
#include <functional>
#include <string>
class Daemon {
public:
using MainLoopFunc = std::function<void()>;
Daemon(const std::string& name);
bool start(MainLoopFunc loop);
void stop();
bool isRunning() const;
private:
std::string name_;
bool runWithRestart(MainLoopFunc loop);
std::atomic<bool> running_;
};
#endif
+22 -49
View File
@@ -28,7 +28,6 @@
#endif
#include "config_center.h"
#include "daemon.h"
#include "path_manager.h"
#include "platform/instance_activation.h"
#include "platform/single_instance.h"
@@ -47,6 +46,10 @@
#include "platform/linux/runtime_daemon.h"
#endif
#ifdef __APPLE__
#include "platform/macos/runtime_daemon.h"
#endif
#ifdef _WIN32
namespace {
@@ -275,9 +278,7 @@ static int ExitForExistingInstance(
return result == crossdesk::platform::InstanceResult::kAlreadyRunning ? 0 : 1;
}
static int RunApplication(
int argc, char* argv[],
crossdesk::platform::SingleInstanceGuard& client_instance) {
static int RunApplication(int argc, char* argv[]) {
// The instance guards are held before any configuration or logger is opened.
auto path_manager = std::make_unique<crossdesk::PathManager>("CrossDesk");
crossdesk::InitLogger(path_manager->GetLogPath().string());
@@ -289,7 +290,7 @@ static int RunApplication(
#endif
if (IsDaemonChild(argc, argv)) {
// child process: run render directly
// Compatibility with a supervisor from an older, still-running build.
crossdesk::Render render;
return render.Run();
}
@@ -327,62 +328,34 @@ static int RunApplication(
runtime_daemon.Initialize(
argc, argv, !std::getenv("CROSSDESK_HEADLESS_ACTIVE") &&
!crossdesk::HeadlessConsole::Instance().active());
std::string daemon_error;
if (!runtime_daemon.SetEnabled(enable_daemon, daemon_error)) {
crossdesk::get_logger()->warn("Could not start daemon: {}", daemon_error);
}
crossdesk::Render render;
const int result = render.Run();
runtime_daemon.Shutdown(result != 0);
return result;
#elif defined(_WIN32)
auto& runtime_daemon = crossdesk::platform::WindowsRuntimeDaemon::Instance();
runtime_daemon.Initialize();
#elif defined(__APPLE__)
auto& runtime_daemon = crossdesk::platform::MacRuntimeDaemon::Instance();
runtime_daemon.Initialize(argc, argv);
#endif
#if defined(__linux__) || defined(_WIN32) || defined(__APPLE__)
std::string daemon_error;
if (!runtime_daemon.SetEnabled(enable_daemon, daemon_error)) {
crossdesk::get_logger()->warn("Could not start daemon: {}", daemon_error);
}
#endif
crossdesk::Render render;
const int result = render.Run();
#if defined(__linux__) || defined(_WIN32) || defined(__APPLE__)
runtime_daemon.Shutdown(result != 0);
return result;
#else
if (enable_daemon) {
// The launcher lease stays in main for the supervisor's entire lifetime.
// Each --child must acquire its own client lease, so it is neither blocked
// by its supervisor nor able to bypass the single-client restriction.
client_instance.Release();
// start daemon with restart monitoring
Daemon daemon("CrossDesk");
// define main loop function: run render and stop daemon on normal exit
Daemon::MainLoopFunc main_loop = [&daemon, &client_instance]() {
// Executable discovery can fail and make the supervisor run the GUI in
// process. This fallback must retain the same client ownership rule.
if (AcquireInstance(client_instance,
crossdesk::platform::InstanceRole::kClient) !=
crossdesk::platform::InstanceResult::kAcquired) {
daemon.stop();
return;
}
crossdesk::Render render;
render.Run();
daemon.stop();
};
// start daemon and return result
bool success = daemon.start(main_loop);
return success ? 0 : 1;
}
// run without daemon: direct execution
crossdesk::Render render;
return render.Run();
#endif
return result;
}
int main(int argc, char* argv[]) {
#ifdef __APPLE__
if (argc >= 2 &&
std::strcmp(argv[1], crossdesk::platform::kMacDaemonArgument) == 0) {
return crossdesk::platform::RunMacRuntimeDaemon(argc, argv);
}
#endif
#ifdef _WIN32
if (argc >= 2 &&
std::strcmp(argv[1], crossdesk::platform::kWindowsDaemonArgument) == 0) {
@@ -503,7 +476,7 @@ int main(int argc, char* argv[]) {
#ifdef __linux__
const int exit_code = crossdesk::RunWithLinuxDisplay(
argc, argv, [&] { return RunApplication(argc, argv, client_instance); }, [&] {
argc, argv, [&] { return RunApplication(argc, argv); }, [&] {
crossdesk::PathManager paths("CrossDesk");
if (console.Enable(paths.GetLogPath(), paths.GetCachePath() / "config.ini")) return true;
std::cerr << "无法初始化日志或控制台,启动失败。\n";
@@ -516,6 +489,6 @@ int main(int argc, char* argv[]) {
}
return exit_code;
#else
return RunApplication(argc, argv, client_instance);
return RunApplication(argc, argv);
#endif
}
@@ -17,6 +17,10 @@
#include "platform/windows/service/autostart_control.h"
#endif
#ifdef __APPLE__
#include "platform/macos/runtime_daemon.h"
#endif
#if defined(__linux__) && !defined(__OHOS__)
#include "platform/linux/autostart.h"
#include "platform/linux/runtime_daemon.h"
@@ -459,15 +463,17 @@ int ConfigCenter::SetAutostart(bool enabled) {
}
int ConfigCenter::SetDaemon(bool enabled) {
#if defined(_WIN32) || (defined(__linux__) && !defined(__OHOS__))
#if defined(_WIN32) || defined(__APPLE__) || (defined(__linux__) && !defined(__OHOS__))
const bool previous = enable_daemon_;
#endif
if (StoreValues({{"enable_daemon", enabled ? "true" : "false"}}) != 0) return -1;
enable_daemon_ = enabled;
#if defined(_WIN32) || (defined(__linux__) && !defined(__OHOS__))
#if defined(_WIN32) || defined(__APPLE__) || (defined(__linux__) && !defined(__OHOS__))
std::string error;
#ifdef _WIN32
auto& daemon = platform::WindowsRuntimeDaemon::Instance();
#elif defined(__APPLE__)
auto& daemon = platform::MacRuntimeDaemon::Instance();
#else
auto& daemon = platform::LinuxRuntimeDaemon::Instance();
#endif
@@ -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
+13 -8
View File
@@ -533,6 +533,8 @@ function setup_targets()
add_deps("linux_runtime_daemon")
elseif is_os("windows") then
add_deps("windows_runtime_daemon")
elseif is_os("macosx") then
add_deps("macos_runtime_daemon")
end
add_files("apps/desktop/src/config_center/*.cpp")
add_includedirs("apps/desktop/src", "apps/desktop/src/config_center",
@@ -547,6 +549,15 @@ function setup_targets()
add_syslinks("dl", {public = true})
end
if is_os("macosx") then
target("macos_runtime_daemon")
set_kind("static")
add_includedirs("apps/desktop/src", {public = true})
add_files("apps/desktop/src/platform/macos/runtime_daemon.mm")
add_mxxflags("-fobjc-arc")
add_frameworks("AppKit", "CoreFoundation", {public = true})
end
target("assets")
set_kind("headeronly")
add_includedirs("apps/desktop/src/gui/assets/localization",
@@ -775,8 +786,7 @@ function setup_targets()
add_files("apps/desktop/src/app/*.cpp")
add_includedirs("apps/desktop/src", "apps/desktop/src/app", {public = true})
if is_os("windows") then
add_files("apps/desktop/src/platform/windows/daemon_backend.cpp",
"apps/desktop/src/platform/windows/service/autostart_control.cpp")
add_files("apps/desktop/src/platform/windows/service/autostart_control.cpp")
add_files("apps/desktop/src/platform/windows/service/service_host.cpp",
"apps/desktop/src/platform/windows/service/rdp_session_handoff.cpp")
add_includedirs("apps/desktop/src/platform/windows/service",
@@ -784,13 +794,8 @@ function setup_targets()
add_links("Advapi32", "Wtsapi32", "Ole32", "Userenv")
add_deps("wgc_plugin", "crossdesk_service", "crossdesk_session_helper")
add_files(crossdesk_windows_resource, {defines = windows_resource_defines})
elseif is_os("macosx") then
add_files("apps/desktop/src/platform/common/posix_daemon_backend.cpp",
"apps/desktop/src/platform/macos/daemon_backend.cpp")
elseif is_os("linux") then
add_files("apps/desktop/src/platform/common/posix_daemon_backend.cpp",
"apps/desktop/src/platform/linux/daemon_backend.cpp",
"apps/desktop/src/platform/linux/headless/headless_session.cpp")
add_files("apps/desktop/src/platform/linux/headless/headless_session.cpp")
end
add_rules("crossdesk.slint_runtime")
after_build(copy_usbmmidd_package)