Compare commits

..

No commits in common. "753164b39396b31868417fbc46fb1f06f569e94f" and "4c1480d36485437bdbeb6ee1a262617f7f252a84" have entirely different histories.

12 changed files with 67 additions and 188 deletions

View File

@ -22,7 +22,6 @@ add_executable(nanosm
src/Timer.cpp
src/WordExp.cpp
src/Process.cpp
src/RestartingProcess.cpp
)
target_compile_definitions(nanosm PRIVATE

View File

@ -2,7 +2,7 @@
A nano-sized not-really-a session manager intended to replace less-than-robust Bash/etc scripts.
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).
Written in C++20.
# 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.
A more robust solution that's still small and easy to setup (read: Not written in bash) is clearly a better idea.
A more robust solution that's still small and easy to setup is clearly a better idea.
# Installation

View File

@ -1,6 +1,6 @@
# 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.)
set(NANOSM_CORE_COMPILE_ARGS "-Wall -Wformat=2 -Wimplicit-fallthrough")
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_LINKER_ARGS "-fuse-ld=${NANOSM_LINKER}")
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release") # OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"

View File

@ -1,20 +1,20 @@
# The nanosm(1) configuration file.
[nanosm]
# The window manager you want to use. This is the first application
# launched
window-manager="/path/to/wm/binary --any-additional-args-here"
# Controls if nanosm should be more verbose. Defaults to false.
verbose = false
# Enable verbose debug logging. Only useful for debugging issues.
verbose=false
# The time in seconds nanosm will wait before restarting any app which exits.
restart-time = 1
# Restart delay in seconds.
restart-delay=1
# Any applications you want to run at startup.
# Any applications besides your window manager you want to run at startup.
# 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
# effectively in a psuedorandom order).
[nanosm.apps]
# The window manager you want to use.
window-manager = { command = "/path/to/wm/binary --any-additional-args-here" }
lxpanel = { command = "lxpanel" }
pcmanfm-desktop = { command = "pcmanfm --desktop" }

View File

@ -4,6 +4,37 @@
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() {
epollFd = epoll_create1(EPOLL_CLOEXEC);

View File

@ -30,11 +30,11 @@ namespace nanosm {
/// Returns the file descriptor for this IO object.
virtual int GetFD() const = 0;
/// Returns the raw event mask declaring what events the IO object is interested in
/// Return raw event mask
virtual int InterestedEvents() const = 0;
/// Called when the object is ready (do any i/o or handling here)
/// [eventMask] is the raw epoll event mask, which can be used to pick what event to fire/do
/// [eventMask] is the raw epoll event mask
virtual void OnReady(int eventMask) = 0;
protected:
@ -44,10 +44,9 @@ namespace nanosm {
EventLoop();
~EventLoop();
/// Add an object to the event loop.
/// Add an object to the epoll event loop.
void AddObject(IoObject::Ptr obj);
/// Removes a IO object from the event loop
void RemoveObject(IoObject::Ptr obj);
/// Posts a function to run after the epoll events are dispatched.
@ -58,17 +57,13 @@ namespace nanosm {
/// Runs the main loop.
void Run();
/// Requests the mainloop to stop.
void Stop();
private:
int epollFd {};
bool shouldStop { false };
/// All tracked IO objects.
std::set<std::shared_ptr<IoObject>> ioObjects {};
/// FIFO queue of functions to run semi-asynchronously
std::deque<PostFn> postCallbacks {};
};

View File

@ -53,8 +53,6 @@ namespace nanosm {
} else {
// Parent: monitor FD
eventLoop.AddObject(IoObject::Ptr(shared_from_this()));
if(onProcessSpawn)
eventLoop.Post(onProcessSpawn);
}
}
@ -65,12 +63,12 @@ namespace nanosm {
// Post a callback to call the user's exit callback (if one exists)
eventLoop.Post([self = shared_from_this()]() {
if(self->onProcessExit)
self->onProcessExit(self->siginfo.si_status);
// Prepare for re-attaching, or etc.
self->eventLoop.RemoveObject(self);
self->Reset();
if(self->onProcessExit)
self->onProcessExit(self->siginfo.si_status);
});
}
@ -79,10 +77,6 @@ namespace nanosm {
kill(pid, SIGTERM);
}
void Process::SetSpawnCallback(std::function<void()> f) {
onProcessSpawn = f;
}
void Process::SetExitCallback(std::function<void(int)> f) {
onProcessExit = f;
}

View File

@ -25,12 +25,6 @@ namespace nanosm {
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);
private:
@ -41,7 +35,6 @@ namespace nanosm {
siginfo_t siginfo {};
std::string commLine;
std::function<void()> onProcessSpawn;
std::function<void(int)> onProcessExit;
};

View File

@ -1,65 +0,0 @@
#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

View File

@ -1,42 +0,0 @@
#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

View File

@ -1,6 +1,5 @@
#pragma once
#include <cstdint>
#include <memory>
using u8 = std::uint8_t;
using i8 = std::int8_t;
@ -12,38 +11,3 @@ using u64 = std::uint64_t;
using i64 = std::int64_t;
using usize = std::size_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

View File

@ -4,24 +4,34 @@
#include <memory>
#include "EventLoop.hpp"
#include "Process.hpp"
#include "RestartingProcess.hpp"
#include "WordExp.hpp"
#include "Timer.hpp"
#include "Process.hpp"
nanosm::EventLoop ev;
auto process = std::make_shared<nanosm::RestartingProcess>(ev);
// tests stuff :)
auto timer = std::make_shared<nanosm::Timer>(ev);
auto process = std::make_shared<nanosm::Process>(ev);
void test() {
process->SetSpawnCallback([](pid_t pid) {
printf("\"%s\" spawned as pid %d successfully\n", process->GetCommandLine().c_str(), pid);
// Do magic
process->SetExitCallback([](pid_t pid, int exitCode) {
printf("\"%s\" pid %d exited with %d exitcode\n", process->GetCommandLine().c_str(), pid, exitCode);
// Do magic
process->SetExitCallback([p = process](int exitCode) {
printf("exited with %d exitcode\n", 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");
}