mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
feat: daemonize
This commit is contained in:
+25
-4
@@ -44,6 +44,7 @@
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
std::atomic<bool> Application::s_shutdownRequested{false};
|
||||
|
||||
@@ -51,6 +52,7 @@ namespace {
|
||||
|
||||
constexpr Logger kLog("app");
|
||||
constexpr bool kLockKeysEnabled = true;
|
||||
constexpr std::string_view kPolkitAuthorityBusName = "org.freedesktop.PolicyKit1";
|
||||
|
||||
template <typename Factory>
|
||||
auto makeWithStartupBackoff(std::string_view label, Factory&& factory) -> decltype(factory()) {
|
||||
@@ -216,12 +218,28 @@ void Application::syncPolkitAgent() {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!m_systemBus->nameHasOwner(kPolkitAuthorityBusName)) {
|
||||
kLog.warn("polkit agent disabled: {} is not running", kPolkitAuthorityBusName);
|
||||
m_polkitPollSource.reset();
|
||||
m_polkitAgent.reset();
|
||||
return;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
kLog.warn("polkit agent disabled: failed to query {} owner: {}", kPolkitAuthorityBusName, e.what());
|
||||
m_polkitPollSource.reset();
|
||||
m_polkitAgent.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_polkitAgent = std::make_unique<PolkitAgent>(*m_systemBus);
|
||||
m_polkitAgent->setReadyCallback([this](bool ok, const std::string& error) {
|
||||
if (!ok) {
|
||||
kLog.warn("polkit agent disabled: {}", error);
|
||||
m_polkitPollSource.reset();
|
||||
m_polkitAgent.reset();
|
||||
DeferredCall::callLater([this]() {
|
||||
m_polkitPollSource.reset();
|
||||
m_polkitAgent.reset();
|
||||
});
|
||||
return;
|
||||
}
|
||||
kLog.info("polkit authentication agent active");
|
||||
@@ -253,7 +271,7 @@ void Application::syncPolkitAgent() {
|
||||
m_polkitAgent->start();
|
||||
}
|
||||
|
||||
void Application::run() {
|
||||
void Application::run(std::function<void()> startupReadyCallback) {
|
||||
initLogFile();
|
||||
kLog.info("noctalia {}", noctalia::build_info::displayVersion());
|
||||
runStartupPhase("initServices", [this]() { initServices(); });
|
||||
@@ -273,9 +291,12 @@ void Application::run() {
|
||||
#endif
|
||||
|
||||
m_trayInitTimer.start(std::chrono::milliseconds(500), [this]() { startTrayService(); });
|
||||
m_polkitInitTimer.start(std::chrono::milliseconds(0), [this]() { syncPolkitAgent(); });
|
||||
m_polkitInitTimer.start(std::chrono::milliseconds(1000), [this]() { syncPolkitAgent(); });
|
||||
|
||||
m_mainLoop = std::make_unique<MainLoop>(m_wayland, m_bar, [this]() { return currentPollSources(); });
|
||||
if (startupReadyCallback) {
|
||||
startupReadyCallback();
|
||||
}
|
||||
m_mainLoop->run();
|
||||
kLog.info("shutdown");
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
#include "wayland/workspace_poll_source.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -99,7 +100,7 @@ public:
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
void run();
|
||||
void run(std::function<void()> startupReadyCallback = {});
|
||||
|
||||
// Public for signal handler
|
||||
static std::atomic<bool> s_shutdownRequested;
|
||||
|
||||
@@ -11,11 +11,13 @@
|
||||
#include <glib-object.h>
|
||||
#include <glib.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <poll.h>
|
||||
#include <pwd.h>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -253,6 +255,13 @@ struct PolkitAgent::Impl {
|
||||
PolkitAgentSession* session = nullptr;
|
||||
GMainContext* context = nullptr;
|
||||
GCancellable* registerCancellable = nullptr;
|
||||
std::thread registerThread;
|
||||
mutable std::mutex registerMutex;
|
||||
gpointer pendingRegistrationHandle = nullptr;
|
||||
bool registrationComplete = false;
|
||||
bool registrationOk = false;
|
||||
bool registrationShutdown = false;
|
||||
std::string registrationError;
|
||||
bool starting = false;
|
||||
bool registered = false;
|
||||
std::unique_ptr<InternalAuthRequest> pending;
|
||||
@@ -280,9 +289,22 @@ struct PolkitAgent::Impl {
|
||||
clearPending("PolkitAgent is being destroyed", true);
|
||||
if (registerCancellable != nullptr) {
|
||||
g_cancellable_cancel(registerCancellable);
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(registerMutex);
|
||||
registrationShutdown = true;
|
||||
}
|
||||
if (registerThread.joinable()) {
|
||||
registerThread.join();
|
||||
}
|
||||
if (registerCancellable != nullptr) {
|
||||
g_object_unref(registerCancellable);
|
||||
registerCancellable = nullptr;
|
||||
}
|
||||
if (pendingRegistrationHandle != nullptr) {
|
||||
polkit_agent_listener_unregister(pendingRegistrationHandle);
|
||||
pendingRegistrationHandle = nullptr;
|
||||
}
|
||||
if (listener != nullptr) {
|
||||
listener->owner = nullptr;
|
||||
listener->initiate = nullptr;
|
||||
@@ -305,51 +327,133 @@ struct PolkitAgent::Impl {
|
||||
return;
|
||||
}
|
||||
starting = true;
|
||||
|
||||
// Prefer the session id exported by the user's login/session manager. The
|
||||
// process lookup path below is asynchronous in API shape, but it still can
|
||||
// enter GLib's global worker pool before returning.
|
||||
const char* sessionId = std::getenv("XDG_SESSION_ID");
|
||||
registerCancellable = g_cancellable_new();
|
||||
if (sessionId != nullptr && sessionId[0] != '\0') {
|
||||
PolkitSubject* subject = polkit_unix_session_new(sessionId);
|
||||
beginRegisterSubject(subject, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
polkit_unix_session_new_for_process(::getpid(), registerCancellable, &Impl::sessionReadyTrampoline, this);
|
||||
}
|
||||
|
||||
void onSessionReady(GObject* /*source*/, GAsyncResult* result) {
|
||||
starting = false;
|
||||
GError* error = nullptr;
|
||||
PolkitSubject* subject = polkit_unix_session_new_for_process_finish(result, &error);
|
||||
|
||||
PolkitSubject* pidSubject = polkit_unix_session_new_for_process_finish(result, &error);
|
||||
|
||||
// If we were cancelled (destruction), bail out without touching members
|
||||
// beyond the cancellable cleanup that the destructor already handled.
|
||||
if (error != nullptr && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||||
g_clear_error(&error);
|
||||
if (subject != nullptr) {
|
||||
g_object_unref(subject);
|
||||
if (pidSubject != nullptr) {
|
||||
g_object_unref(pidSubject);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
beginRegisterSubject(pidSubject, error);
|
||||
}
|
||||
|
||||
void beginRegisterSubject(PolkitSubject* subject, GError* error) {
|
||||
if (subject == nullptr || error != nullptr) {
|
||||
std::string message = error != nullptr ? error->message : "failed to create polkit session subject";
|
||||
g_clear_error(&error);
|
||||
if (subject != nullptr) {
|
||||
g_object_unref(subject);
|
||||
}
|
||||
setRegistrationResult(nullptr, false, std::move(message));
|
||||
return;
|
||||
}
|
||||
|
||||
registerThread = std::thread([this, subject]() {
|
||||
GError* registerError = nullptr;
|
||||
gpointer handle =
|
||||
polkit_agent_listener_register(POLKIT_AGENT_LISTENER(listener), POLKIT_AGENT_REGISTER_FLAGS_NONE, subject,
|
||||
k_agentObjectPath, registerCancellable, ®isterError);
|
||||
g_object_unref(subject);
|
||||
|
||||
std::string message;
|
||||
if (registerError != nullptr) {
|
||||
message = registerError->message;
|
||||
g_clear_error(®isterError);
|
||||
} else if (handle == nullptr) {
|
||||
message = "polkit listener registration returned no handle";
|
||||
}
|
||||
|
||||
setRegistrationResult(handle, handle != nullptr && message.empty(), std::move(message));
|
||||
});
|
||||
}
|
||||
|
||||
void setRegistrationResult(gpointer handle, bool ok, std::string error) {
|
||||
bool shouldUnregister = false;
|
||||
{
|
||||
std::lock_guard lock(registerMutex);
|
||||
if (registrationShutdown) {
|
||||
shouldUnregister = handle != nullptr;
|
||||
} else {
|
||||
pendingRegistrationHandle = handle;
|
||||
registrationOk = ok;
|
||||
registrationError = std::move(error);
|
||||
registrationComplete = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldUnregister) {
|
||||
polkit_agent_listener_unregister(handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool registrationReady() const {
|
||||
std::lock_guard lock(registerMutex);
|
||||
return registrationComplete;
|
||||
}
|
||||
|
||||
void finishRegistrationIfReady() {
|
||||
gpointer handle = nullptr;
|
||||
bool ok = false;
|
||||
std::string error;
|
||||
{
|
||||
std::lock_guard lock(registerMutex);
|
||||
if (!registrationComplete) {
|
||||
return;
|
||||
}
|
||||
handle = pendingRegistrationHandle;
|
||||
pendingRegistrationHandle = nullptr;
|
||||
ok = registrationOk;
|
||||
error = std::move(registrationError);
|
||||
registrationComplete = false;
|
||||
registrationOk = false;
|
||||
}
|
||||
|
||||
if (registerThread.joinable()) {
|
||||
registerThread.join();
|
||||
}
|
||||
if (registerCancellable != nullptr) {
|
||||
g_object_unref(registerCancellable);
|
||||
registerCancellable = nullptr;
|
||||
}
|
||||
|
||||
if (subject == nullptr || error != nullptr) {
|
||||
std::string message = error != nullptr ? error->message : "failed to create polkit session subject";
|
||||
g_clear_error(&error);
|
||||
starting = false;
|
||||
if (!ok) {
|
||||
if (handle != nullptr) {
|
||||
polkit_agent_listener_unregister(handle);
|
||||
}
|
||||
if (readyCallback) {
|
||||
readyCallback(false, message);
|
||||
readyCallback(false, error.empty() ? "polkit listener registration failed" : error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
listener->registration_handle = polkit_agent_listener_register(
|
||||
POLKIT_AGENT_LISTENER(listener), POLKIT_AGENT_REGISTER_FLAGS_NONE, subject, k_agentObjectPath, nullptr, &error);
|
||||
g_object_unref(subject);
|
||||
|
||||
if (error != nullptr) {
|
||||
std::string message = error->message;
|
||||
g_clear_error(&error);
|
||||
if (readyCallback) {
|
||||
readyCallback(false, message);
|
||||
}
|
||||
return;
|
||||
if (listener->registration_handle != nullptr) {
|
||||
polkit_agent_listener_unregister(listener->registration_handle);
|
||||
}
|
||||
listener->registration_handle = handle;
|
||||
if (listener->registration_handle == nullptr) {
|
||||
if (readyCallback) {
|
||||
readyCallback(false, "polkit listener registration returned no handle");
|
||||
@@ -575,9 +679,18 @@ struct PolkitAgent::Impl {
|
||||
g_main_context_release(context);
|
||||
}
|
||||
|
||||
int pollTimeoutMs() const { return glibPollTimeoutMs; }
|
||||
int pollTimeoutMs() const {
|
||||
if (registrationReady()) {
|
||||
return 0;
|
||||
}
|
||||
if (starting) {
|
||||
return glibPollTimeoutMs < 0 ? 100 : std::min(glibPollTimeoutMs, 100);
|
||||
}
|
||||
return glibPollTimeoutMs;
|
||||
}
|
||||
|
||||
void dispatch(const std::vector<pollfd>& fds, std::size_t startIdx) {
|
||||
finishRegistrationIfReady();
|
||||
if (!g_main_context_acquire(context)) {
|
||||
return;
|
||||
}
|
||||
@@ -603,6 +716,7 @@ struct PolkitAgent::Impl {
|
||||
while (g_main_context_pending(context)) {
|
||||
g_main_context_iteration(context, FALSE);
|
||||
}
|
||||
finishRegistrationIfReady();
|
||||
}
|
||||
|
||||
PolkitRequest pendingRequest() const {
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
#include "dbus/system_bus.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
constexpr auto kDbusInterface = "org.freedesktop.DBus";
|
||||
const sdbus::ServiceName kDbusName{kDbusInterface};
|
||||
const sdbus::ObjectPath kDbusPath{"/org/freedesktop/DBus"};
|
||||
} // namespace
|
||||
|
||||
SystemBus::SystemBus() : m_connection(sdbus::createSystemBusConnection()) {}
|
||||
|
||||
sdbus::IConnection::PollData SystemBus::getPollData() const { return m_connection->getEventLoopPollData(); }
|
||||
|
||||
bool SystemBus::nameHasOwner(std::string_view name) const {
|
||||
auto proxy = sdbus::createProxy(*m_connection, kDbusName, kDbusPath);
|
||||
bool hasOwner = false;
|
||||
proxy->callMethod("NameHasOwner")
|
||||
.onInterface(kDbusInterface)
|
||||
.withArguments(std::string{name})
|
||||
.storeResultsTo(hasOwner);
|
||||
return hasOwner;
|
||||
}
|
||||
|
||||
void SystemBus::processPendingEvents() {
|
||||
while (m_connection->processPendingEvent()) {
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string_view>
|
||||
|
||||
class SystemBus {
|
||||
public:
|
||||
@@ -9,6 +10,7 @@ public:
|
||||
|
||||
[[nodiscard]] sdbus::IConnection& connection() noexcept { return *m_connection; }
|
||||
[[nodiscard]] sdbus::IConnection::PollData getPollData() const;
|
||||
[[nodiscard]] bool nameHasOwner(std::string_view name) const;
|
||||
void processPendingEvents();
|
||||
|
||||
private:
|
||||
|
||||
+227
-8
@@ -6,11 +6,17 @@
|
||||
#include "ipc/ipc_client.h"
|
||||
#include "theme/cli.h"
|
||||
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <clocale>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#if __has_include(<jemalloc/jemalloc.h>)
|
||||
@@ -23,18 +29,112 @@
|
||||
|
||||
namespace {
|
||||
|
||||
enum class SpawnResult { Parent, Error };
|
||||
|
||||
constexpr const char* kDaemonPipeEnv = "NOCTALIA_DAEMON_PIPE_FD";
|
||||
int g_daemonPipe = -1;
|
||||
|
||||
void closeFd(int& fd) {
|
||||
if (fd == -1) {
|
||||
return;
|
||||
}
|
||||
(void)::close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
bool writeAll(int fd, const void* data, std::size_t size) {
|
||||
const char* bytes = static_cast<const char*>(data);
|
||||
while (size > 0) {
|
||||
const ssize_t written = ::write(fd, bytes, size);
|
||||
if (written < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (written == 0) {
|
||||
return false;
|
||||
}
|
||||
bytes += written;
|
||||
size -= static_cast<std::size_t>(written);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readAll(int fd, void* data, std::size_t size) {
|
||||
char* bytes = static_cast<char*>(data);
|
||||
while (size > 0) {
|
||||
const ssize_t received = ::read(fd, bytes, size);
|
||||
if (received < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (received == 0) {
|
||||
return false;
|
||||
}
|
||||
bytes += received;
|
||||
size -= static_cast<std::size_t>(received);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool redirectStdioToNull() {
|
||||
int fd = ::open("/dev/null", O_RDWR);
|
||||
if (fd == -1) {
|
||||
std::perror("open(\"/dev/null\")");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
if (::dup2(fd, STDIN_FILENO) == -1) {
|
||||
std::perror("dup2(stdin)");
|
||||
ok = false;
|
||||
}
|
||||
if (::dup2(fd, STDOUT_FILENO) == -1) {
|
||||
std::perror("dup2(stdout)");
|
||||
ok = false;
|
||||
}
|
||||
if (::dup2(fd, STDERR_FILENO) == -1) {
|
||||
std::perror("dup2(stderr)");
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (fd > STDERR_FILENO) {
|
||||
(void)::close(fd);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void completeDaemonStartup(int code) {
|
||||
if (g_daemonPipe == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int result = code;
|
||||
if (!writeAll(g_daemonPipe, &result, sizeof(result))) {
|
||||
std::fprintf(stderr, "error: failed to notify daemon parent: %s\n", std::strerror(errno));
|
||||
}
|
||||
closeFd(g_daemonPipe);
|
||||
if (code == 0 && !redirectStdioToNull()) {
|
||||
std::fprintf(stderr, "error: failed to redirect daemon stdio\n");
|
||||
}
|
||||
}
|
||||
|
||||
int runTopLevelFlag(const char* flag) {
|
||||
if (std::strcmp(flag, "--version") == 0) {
|
||||
if (std::strcmp(flag, "--version") == 0 || std::strcmp(flag, "-v") == 0) {
|
||||
const std::string version = noctalia::build_info::displayVersion();
|
||||
std::printf("noctalia %s\n", version.c_str());
|
||||
return 0;
|
||||
}
|
||||
if (std::strcmp(flag, "--help") == 0) {
|
||||
if (std::strcmp(flag, "--help") == 0 || std::strcmp(flag, "-h") == 0) {
|
||||
std::puts("Usage: noctalia [OPTIONS]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" --help Show this help message\n"
|
||||
" --version Show version information\n"
|
||||
" -h, --help Show this help message\n"
|
||||
" -v, --version Show version information\n"
|
||||
" -d, --daemon Run in background\n"
|
||||
"\n"
|
||||
"Subcommands:\n"
|
||||
" msg <command> Send a command to the running instance\n"
|
||||
@@ -51,16 +151,90 @@ namespace {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool takeDaemonPipeFromEnv() {
|
||||
const char* value = std::getenv(kDaemonPipeEnv);
|
||||
if (value == nullptr || value[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
char* end = nullptr;
|
||||
const long fd = std::strtol(value, &end, 10);
|
||||
(void)::unsetenv(kDaemonPipeEnv);
|
||||
if (errno != 0 || end == value || *end != '\0' || fd < 0) {
|
||||
std::fprintf(stderr, "error: invalid %s value: %s\n", kDaemonPipeEnv, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_daemonPipe = static_cast<int>(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
SpawnResult daemonize(pid_t* outPid, int* parentPipe, char* const argv[]) {
|
||||
auto pipeFds = std::array<int, 2>{-1, -1};
|
||||
if (::pipe(pipeFds.data()) == -1) {
|
||||
std::perror("pipe");
|
||||
return SpawnResult::Error;
|
||||
}
|
||||
|
||||
pid_t pid = ::fork();
|
||||
if (pid < 0) {
|
||||
std::perror("fork");
|
||||
closeFd(pipeFds[0]);
|
||||
closeFd(pipeFds[1]);
|
||||
return SpawnResult::Error;
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
if (outPid)
|
||||
*outPid = pid;
|
||||
if (parentPipe)
|
||||
*parentPipe = pipeFds[0];
|
||||
closeFd(pipeFds[1]);
|
||||
return SpawnResult::Parent;
|
||||
}
|
||||
|
||||
closeFd(pipeFds[0]);
|
||||
// Match v4's early daemon boundary, but exec before shell startup so GLib,
|
||||
// D-Bus, and polkit start from a normal process image rather than a raw
|
||||
// post-fork child.
|
||||
if (::setsid() == -1) {
|
||||
std::perror("setsid");
|
||||
const int daemonResult = 1;
|
||||
(void)writeAll(pipeFds[1], &daemonResult, sizeof(daemonResult));
|
||||
closeFd(pipeFds[1]);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
const std::string pipeFd = std::to_string(pipeFds[1]);
|
||||
if (::setenv(kDaemonPipeEnv, pipeFd.c_str(), 1) == -1) {
|
||||
std::perror("setenv");
|
||||
const int daemonResult = 1;
|
||||
(void)writeAll(pipeFds[1], &daemonResult, sizeof(daemonResult));
|
||||
closeFd(pipeFds[1]);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
::execvp(argv[0], argv);
|
||||
std::perror("execvp");
|
||||
const int daemonResult = 1;
|
||||
(void)writeAll(pipeFds[1], &daemonResult, sizeof(daemonResult));
|
||||
closeFd(pipeFds[1]);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
int runShell() {
|
||||
if (IpcClient::isRunning()) {
|
||||
std::fputs("error: noctalia is already running\n", stderr);
|
||||
completeDaemonStartup(1);
|
||||
return 1;
|
||||
}
|
||||
try {
|
||||
Application app;
|
||||
app.run();
|
||||
app.run([]() { completeDaemonStartup(0); });
|
||||
} catch (const std::exception& e) {
|
||||
logError("fatal: {}", e.what());
|
||||
completeDaemonStartup(1);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -79,6 +253,23 @@ int main(int argc, char* argv[]) {
|
||||
#endif
|
||||
|
||||
std::setlocale(LC_ALL, "");
|
||||
|
||||
const bool isDaemonChild = takeDaemonPipeFromEnv();
|
||||
bool shouldDaemonize = false;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (std::strcmp(argv[i], "--daemon") == 0 || std::strcmp(argv[i], "--daemonize") == 0 ||
|
||||
std::strcmp(argv[i], "-d") == 0) {
|
||||
shouldDaemonize = true;
|
||||
for (int j = i; j < argc - 1; ++j) {
|
||||
argv[j] = argv[j + 1];
|
||||
}
|
||||
--argc;
|
||||
argv[argc] = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc >= 2) {
|
||||
if (std::strcmp(argv[1], "theme") == 0)
|
||||
return noctalia::theme::runCli(argc, argv);
|
||||
@@ -89,9 +280,37 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const int rc = runTopLevelFlag(argv[i]);
|
||||
if (rc >= 0)
|
||||
return rc;
|
||||
if (argv[i][0] == '-') {
|
||||
const int rc = runTopLevelFlag(argv[i]);
|
||||
if (rc >= 0)
|
||||
return rc;
|
||||
|
||||
std::fprintf(stderr, "error: unknown option: %s\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldDaemonize && !isDaemonChild) {
|
||||
pid_t pid = -1;
|
||||
int parentPipe = -1;
|
||||
SpawnResult result = daemonize(&pid, &parentPipe, argv);
|
||||
|
||||
if (result == SpawnResult::Error) {
|
||||
return 1;
|
||||
}
|
||||
if (result == SpawnResult::Parent) {
|
||||
int daemonResult = 1;
|
||||
const bool receivedResult = readAll(parentPipe, &daemonResult, sizeof(daemonResult));
|
||||
closeFd(parentPipe);
|
||||
if (!receivedResult) {
|
||||
std::fputs("error: failed to wait for daemon startup\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
if (daemonResult == 0) {
|
||||
std::printf("noctalia started [pid: %d]\n", pid);
|
||||
}
|
||||
return daemonResult;
|
||||
}
|
||||
}
|
||||
|
||||
return runShell();
|
||||
|
||||
Reference in New Issue
Block a user