Compare commits
2 Commits
4c1480d364
...
753164b393
Author | SHA1 | Date |
---|---|---|
Lily Tsuru | 753164b393 | |
Lily Tsuru | ef904f5403 |
|
@ -22,6 +22,7 @@ add_executable(nanosm
|
||||||
src/Timer.cpp
|
src/Timer.cpp
|
||||||
src/WordExp.cpp
|
src/WordExp.cpp
|
||||||
src/Process.cpp
|
src/Process.cpp
|
||||||
|
src/RestartingProcess.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(nanosm PRIVATE
|
target_compile_definitions(nanosm PRIVATE
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
A nano-sized not-really-a session manager intended to replace less-than-robust Bash/etc scripts.
|
A nano-sized not-really-a session manager intended to replace less-than-robust Bash/etc scripts.
|
||||||
|
|
||||||
Written in C++20.
|
Written in C++20, using epoll (without helper libraries), to stay nice and smol (Yes I know io_uring exists, but using it for something tiny that literally just waits and restarts processes would be far more overkill).
|
||||||
|
|
||||||
# Why write this?
|
# Why write this?
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ Because `app &` then `exec wm` is impressively awful. What if your WM crashes, o
|
||||||
|
|
||||||
And if your WM crashes? Your whole xorg server goes with it, meaning so does everything else.
|
And if your WM crashes? Your whole xorg server goes with it, meaning so does everything else.
|
||||||
|
|
||||||
A more robust solution that's still small and easy to setup is clearly a better idea.
|
A more robust solution that's still small and easy to setup (read: Not written in bash) is clearly a better idea.
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# TODO: This currently assumes libstdc++, later on we should *probably* set this with some detection
|
# TODO: This currently assumes libstdc++, later on we should *probably* set this with some detection
|
||||||
# also TODO: Use a list so that this isn't one giant line (list JOIN should help.)
|
# also TODO: Use a list so that this isn't one giant line (list JOIN should help.)
|
||||||
set(NANOSM_CORE_COMPILE_ARGS "-Wall -Wformat=2 -Wimplicit-fallthrough -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -fstrict-flex-arrays=3 -fstack-clash-protection -fstack-protector-strong")
|
set(NANOSM_CORE_COMPILE_ARGS "-Wall -Wformat=2 -Wimplicit-fallthrough")
|
||||||
set(NANOSM_CORE_LINKER_ARGS "-fuse-ld=${NANOSM_LINKER}")
|
set(NANOSM_CORE_LINKER_ARGS "-fuse-ld=${NANOSM_LINKER}")
|
||||||
|
|
||||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release") # OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"
|
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release") # OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
# The nanosm(1) configuration file.
|
# The nanosm(1) configuration file.
|
||||||
|
|
||||||
[nanosm]
|
[nanosm]
|
||||||
# The window manager you want to use. This is the first application
|
|
||||||
# launched
|
|
||||||
window-manager="/path/to/wm/binary --any-additional-args-here"
|
|
||||||
|
|
||||||
# Enable verbose debug logging. Only useful for debugging issues.
|
# Controls if nanosm should be more verbose. Defaults to false.
|
||||||
verbose=false
|
verbose = false
|
||||||
|
|
||||||
# Restart delay in seconds.
|
# The time in seconds nanosm will wait before restarting any app which exits.
|
||||||
restart-delay=1
|
restart-time = 1
|
||||||
|
|
||||||
# Any applications besides your window manager you want to run at startup.
|
|
||||||
|
# Any applications you want to run at startup.
|
||||||
# Note that applications are executed in the order they are declared, but
|
# Note that applications are executed in the order they are declared, but
|
||||||
# this will not hold true if any (or all apps) crash (they will be restarted
|
# this will not hold true if any (or all apps) crash (they will be restarted
|
||||||
# effectively in a psuedorandom order).
|
# effectively in a psuedorandom order).
|
||||||
[nanosm.apps]
|
[nanosm.apps]
|
||||||
|
# The window manager you want to use.
|
||||||
|
window-manager = { command = "/path/to/wm/binary --any-additional-args-here" }
|
||||||
lxpanel = { command = "lxpanel" }
|
lxpanel = { command = "lxpanel" }
|
||||||
pcmanfm-desktop = { command = "pcmanfm --desktop" }
|
pcmanfm-desktop = { command = "pcmanfm --desktop" }
|
||||||
|
|
|
@ -4,37 +4,6 @@
|
||||||
|
|
||||||
namespace nanosm {
|
namespace nanosm {
|
||||||
|
|
||||||
/// A little ergonomic wrapper over
|
|
||||||
/// std::unique_ptr<T[]>, for a "kinda-vector"
|
|
||||||
/// that lives on the heap and is statically sized
|
|
||||||
template <class T>
|
|
||||||
struct UniqueArray final {
|
|
||||||
explicit UniqueArray(usize size)
|
|
||||||
: array(std::make_unique<T[]>(size)),
|
|
||||||
size(size) {
|
|
||||||
}
|
|
||||||
|
|
||||||
UniqueArray(UniqueArray&& move) {
|
|
||||||
array = std::move(move.array);
|
|
||||||
size = move.size;
|
|
||||||
|
|
||||||
// invalidate
|
|
||||||
move.array = nullptr;
|
|
||||||
move.size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator[](usize index) { return Get()[index]; }
|
|
||||||
const T& operator[](usize index) const { return Get()[index]; }
|
|
||||||
|
|
||||||
T* Get() { return array.get(); }
|
|
||||||
const T* Get() const { return array.get(); }
|
|
||||||
usize Size() const { return size; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<T[]> array {};
|
|
||||||
usize size {};
|
|
||||||
};
|
|
||||||
|
|
||||||
EventLoop::EventLoop() {
|
EventLoop::EventLoop() {
|
||||||
epollFd = epoll_create1(EPOLL_CLOEXEC);
|
epollFd = epoll_create1(EPOLL_CLOEXEC);
|
||||||
|
|
||||||
|
|
|
@ -30,11 +30,11 @@ namespace nanosm {
|
||||||
/// Returns the file descriptor for this IO object.
|
/// Returns the file descriptor for this IO object.
|
||||||
virtual int GetFD() const = 0;
|
virtual int GetFD() const = 0;
|
||||||
|
|
||||||
/// Return raw event mask
|
/// Returns the raw event mask declaring what events the IO object is interested in
|
||||||
virtual int InterestedEvents() const = 0;
|
virtual int InterestedEvents() const = 0;
|
||||||
|
|
||||||
/// Called when the object is ready (do any i/o or handling here)
|
/// Called when the object is ready (do any i/o or handling here)
|
||||||
/// [eventMask] is the raw epoll event mask
|
/// [eventMask] is the raw epoll event mask, which can be used to pick what event to fire/do
|
||||||
virtual void OnReady(int eventMask) = 0;
|
virtual void OnReady(int eventMask) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -44,9 +44,10 @@ namespace nanosm {
|
||||||
EventLoop();
|
EventLoop();
|
||||||
~EventLoop();
|
~EventLoop();
|
||||||
|
|
||||||
/// Add an object to the epoll event loop.
|
/// Add an object to the event loop.
|
||||||
void AddObject(IoObject::Ptr obj);
|
void AddObject(IoObject::Ptr obj);
|
||||||
|
|
||||||
|
/// Removes a IO object from the event loop
|
||||||
void RemoveObject(IoObject::Ptr obj);
|
void RemoveObject(IoObject::Ptr obj);
|
||||||
|
|
||||||
/// Posts a function to run after the epoll events are dispatched.
|
/// Posts a function to run after the epoll events are dispatched.
|
||||||
|
@ -57,13 +58,17 @@ namespace nanosm {
|
||||||
/// Runs the main loop.
|
/// Runs the main loop.
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
|
/// Requests the mainloop to stop.
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int epollFd {};
|
int epollFd {};
|
||||||
bool shouldStop { false };
|
bool shouldStop { false };
|
||||||
|
|
||||||
|
/// All tracked IO objects.
|
||||||
std::set<std::shared_ptr<IoObject>> ioObjects {};
|
std::set<std::shared_ptr<IoObject>> ioObjects {};
|
||||||
|
|
||||||
|
/// FIFO queue of functions to run semi-asynchronously
|
||||||
std::deque<PostFn> postCallbacks {};
|
std::deque<PostFn> postCallbacks {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,8 @@ namespace nanosm {
|
||||||
} else {
|
} else {
|
||||||
// Parent: monitor FD
|
// Parent: monitor FD
|
||||||
eventLoop.AddObject(IoObject::Ptr(shared_from_this()));
|
eventLoop.AddObject(IoObject::Ptr(shared_from_this()));
|
||||||
|
if(onProcessSpawn)
|
||||||
|
eventLoop.Post(onProcessSpawn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,12 +65,12 @@ namespace nanosm {
|
||||||
|
|
||||||
// Post a callback to call the user's exit callback (if one exists)
|
// Post a callback to call the user's exit callback (if one exists)
|
||||||
eventLoop.Post([self = shared_from_this()]() {
|
eventLoop.Post([self = shared_from_this()]() {
|
||||||
|
if(self->onProcessExit)
|
||||||
|
self->onProcessExit(self->siginfo.si_status);
|
||||||
|
|
||||||
// Prepare for re-attaching, or etc.
|
// Prepare for re-attaching, or etc.
|
||||||
self->eventLoop.RemoveObject(self);
|
self->eventLoop.RemoveObject(self);
|
||||||
self->Reset();
|
self->Reset();
|
||||||
|
|
||||||
if(self->onProcessExit)
|
|
||||||
self->onProcessExit(self->siginfo.si_status);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +79,10 @@ namespace nanosm {
|
||||||
kill(pid, SIGTERM);
|
kill(pid, SIGTERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Process::SetSpawnCallback(std::function<void()> f) {
|
||||||
|
onProcessSpawn = f;
|
||||||
|
}
|
||||||
|
|
||||||
void Process::SetExitCallback(std::function<void(int)> f) {
|
void Process::SetExitCallback(std::function<void(int)> f) {
|
||||||
onProcessExit = f;
|
onProcessExit = f;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,12 @@ namespace nanosm {
|
||||||
|
|
||||||
void Kill();
|
void Kill();
|
||||||
|
|
||||||
|
auto GetPID() const { return pid; }
|
||||||
|
|
||||||
|
const std::string& GetCommandLine() const { return commLine; }
|
||||||
|
|
||||||
|
void SetSpawnCallback(std::function<void()> f);
|
||||||
|
|
||||||
void SetExitCallback(std::function<void(int)> f);
|
void SetExitCallback(std::function<void(int)> f);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -35,6 +41,7 @@ namespace nanosm {
|
||||||
siginfo_t siginfo {};
|
siginfo_t siginfo {};
|
||||||
std::string commLine;
|
std::string commLine;
|
||||||
|
|
||||||
|
std::function<void()> onProcessSpawn;
|
||||||
std::function<void(int)> onProcessExit;
|
std::function<void(int)> onProcessExit;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include "RestartingProcess.hpp"
|
||||||
|
|
||||||
|
#include "EventLoop.hpp"
|
||||||
|
#include "Process.hpp"
|
||||||
|
#include "Timer.hpp"
|
||||||
|
|
||||||
|
namespace nanosm {
|
||||||
|
|
||||||
|
RestartingProcess::RestartingProcess(nanosm::EventLoop& eventLoop)
|
||||||
|
: eventLoop(eventLoop), process(std::make_shared<nanosm::Process>(eventLoop)), processRestartTimer(std::make_shared<nanosm::Timer>(eventLoop)) {
|
||||||
|
// Add timer to the event loop
|
||||||
|
eventLoop.AddObject(processRestartTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
RestartingProcess::~RestartingProcess() {
|
||||||
|
eventLoop.RemoveObject(processRestartTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestartingProcess::Spawn(const std::string& commandLine) {
|
||||||
|
if(!initialized)
|
||||||
|
Init();
|
||||||
|
|
||||||
|
process->Spawn(commandLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestartingProcess::SetSpawnCallback(std::function<void(pid_t)> f) {
|
||||||
|
spawnCallback = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestartingProcess::SetExitCallback(std::function<void(pid_t, int)> f) {
|
||||||
|
exitCallback = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestartingProcess::SetRestartTime(u32 newRestartTime) {
|
||||||
|
restartTimeSeconds = newRestartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& RestartingProcess::GetCommandLine() const {
|
||||||
|
return process->GetCommandLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestartingProcess::Init() {
|
||||||
|
process->SetSpawnCallback([self = shared_from_this()]() {
|
||||||
|
self->process->SetExitCallback([self](int exitCode) {
|
||||||
|
// TODO: bool to not arm restart timer?
|
||||||
|
// Also instead of hacking this into the RestartingProcess layer,
|
||||||
|
// why not have normal Process expose PID too? (not that it's important)
|
||||||
|
if(self->exitCallback)
|
||||||
|
self->exitCallback(self->process->GetPID(), exitCode);
|
||||||
|
|
||||||
|
self->processRestartTimer->SetExpiryCallback([self]() {
|
||||||
|
self->process->Respawn();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the timer to wait a bit before restarting the process
|
||||||
|
self->processRestartTimer->Arm(self->restartTimeSeconds);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(self->spawnCallback)
|
||||||
|
self->spawnCallback(self->process->GetPID());
|
||||||
|
});
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nanosm
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Types.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
namespace nanosm {
|
||||||
|
|
||||||
|
struct EventLoop;
|
||||||
|
struct Timer;
|
||||||
|
struct Process;
|
||||||
|
|
||||||
|
/// A process which automatically restarts.
|
||||||
|
struct RestartingProcess : public std::enable_shared_from_this<RestartingProcess> {
|
||||||
|
RestartingProcess(nanosm::EventLoop& eventLoop);
|
||||||
|
|
||||||
|
~RestartingProcess();
|
||||||
|
void Spawn(const std::string& commandLine);
|
||||||
|
|
||||||
|
void SetSpawnCallback(std::function<void(pid_t)> f);
|
||||||
|
|
||||||
|
void SetExitCallback(std::function<void(pid_t, int)> f);
|
||||||
|
|
||||||
|
/// Set the restart time.
|
||||||
|
void SetRestartTime(u32 newRestartTime);
|
||||||
|
|
||||||
|
const std::string& GetCommandLine() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
nanosm::EventLoop& eventLoop;
|
||||||
|
std::shared_ptr<nanosm::Process> process;
|
||||||
|
std::shared_ptr<nanosm::Timer> processRestartTimer;
|
||||||
|
bool initialized { false };
|
||||||
|
u32 restartTimeSeconds { 1 };
|
||||||
|
|
||||||
|
std::function<void(pid_t pid)> spawnCallback;
|
||||||
|
std::function<void(pid_t pid, int exitCode)> exitCallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace nanosm
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
using u8 = std::uint8_t;
|
using u8 = std::uint8_t;
|
||||||
using i8 = std::int8_t;
|
using i8 = std::int8_t;
|
||||||
|
@ -11,3 +12,38 @@ using u64 = std::uint64_t;
|
||||||
using i64 = std::int64_t;
|
using i64 = std::int64_t;
|
||||||
using usize = std::size_t;
|
using usize = std::size_t;
|
||||||
using isize = std::intptr_t;
|
using isize = std::intptr_t;
|
||||||
|
|
||||||
|
namespace nanosm {
|
||||||
|
|
||||||
|
/// A little ergonomic wrapper over
|
||||||
|
/// std::unique_ptr<T[]>, for a "kinda-vector"
|
||||||
|
/// that lives on the heap and is statically sized
|
||||||
|
template <class T>
|
||||||
|
struct UniqueArray final {
|
||||||
|
explicit UniqueArray(usize size)
|
||||||
|
: array(std::make_unique<T[]>(size)),
|
||||||
|
size(size) {
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueArray(UniqueArray&& move) {
|
||||||
|
array = std::move(move.array);
|
||||||
|
size = move.size;
|
||||||
|
|
||||||
|
// invalidate
|
||||||
|
move.array = nullptr;
|
||||||
|
move.size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator[](usize index) { return Get()[index]; }
|
||||||
|
const T& operator[](usize index) const { return Get()[index]; }
|
||||||
|
|
||||||
|
T* Get() { return array.get(); }
|
||||||
|
const T* Get() const { return array.get(); }
|
||||||
|
usize Size() const { return size; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<T[]> array {};
|
||||||
|
usize size {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace nanosm
|
||||||
|
|
28
src/main.cpp
28
src/main.cpp
|
@ -4,34 +4,24 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "EventLoop.hpp"
|
#include "EventLoop.hpp"
|
||||||
#include "WordExp.hpp"
|
|
||||||
|
|
||||||
#include "Timer.hpp"
|
|
||||||
#include "Process.hpp"
|
#include "Process.hpp"
|
||||||
|
#include "RestartingProcess.hpp"
|
||||||
|
#include "Timer.hpp"
|
||||||
|
|
||||||
nanosm::EventLoop ev;
|
nanosm::EventLoop ev;
|
||||||
|
|
||||||
|
auto process = std::make_shared<nanosm::RestartingProcess>(ev);
|
||||||
|
|
||||||
// tests stuff :)
|
// tests stuff :)
|
||||||
|
|
||||||
auto timer = std::make_shared<nanosm::Timer>(ev);
|
|
||||||
auto process = std::make_shared<nanosm::Process>(ev);
|
|
||||||
|
|
||||||
void test() {
|
void test() {
|
||||||
|
process->SetSpawnCallback([](pid_t pid) {
|
||||||
|
printf("\"%s\" spawned as pid %d successfully\n", process->GetCommandLine().c_str(), pid);
|
||||||
// Do magic
|
// Do magic
|
||||||
process->SetExitCallback([p = process](int exitCode) {
|
process->SetExitCallback([](pid_t pid, int exitCode) {
|
||||||
printf("exited with %d exitcode\n", exitCode);
|
printf("\"%s\" pid %d exited with %d exitcode\n", process->GetCommandLine().c_str(), pid, exitCode);
|
||||||
|
});
|
||||||
timer->SetExpiryCallback([pp = p]() {
|
|
||||||
printf("Timer elapsed, restarting Nowr\n");
|
|
||||||
pp->Respawn();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start the timer to wait a bit before restarting the process
|
|
||||||
timer->Arm(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
ev.AddObject(timer);
|
|
||||||
process->Spawn("xterm");
|
process->Spawn("xterm");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue