Compare commits
No commits in common. "f71cfbdfe9c620e59aba0100a19e07790adf9aad" and "dd0280a1e5c66a5bee20aea8c8e4331bb506cbad" have entirely different histories.
f71cfbdfe9
...
dd0280a1e5
|
@ -21,7 +21,6 @@ BinPackParameters: true
|
||||||
BreakConstructorInitializers: BeforeColon
|
BreakConstructorInitializers: BeforeColon
|
||||||
BreakStringLiterals: false
|
BreakStringLiterals: false
|
||||||
|
|
||||||
ColumnLimit: 0
|
|
||||||
CompactNamespaces: false
|
CompactNamespaces: false
|
||||||
|
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "third_party/tomlplusplus"]
|
|
||||||
path = third_party/tomlplusplus
|
|
||||||
url = https://github.com/marzer/tomlplusplus
|
|
|
@ -1,37 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.15)
|
|
||||||
|
|
||||||
project(nanosm
|
|
||||||
LANGUAGES CXX
|
|
||||||
)
|
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
|
||||||
|
|
||||||
include(Policies)
|
|
||||||
include(ProjectFuncs)
|
|
||||||
include(CompilerFlags)
|
|
||||||
|
|
||||||
add_subdirectory(third_party/tomlplusplus)
|
|
||||||
|
|
||||||
add_executable(nanosm
|
|
||||||
src/main.cpp
|
|
||||||
|
|
||||||
src/tomlpp_src.cpp
|
|
||||||
|
|
||||||
# glue code
|
|
||||||
src/EventLoop.cpp
|
|
||||||
src/Timer.cpp
|
|
||||||
src/WordExp.cpp
|
|
||||||
src/Process.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_definitions(nanosm PRIVATE
|
|
||||||
-DTOML_HEADER_ONLY=0
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(nanosm PRIVATE
|
|
||||||
tomlplusplus::tomlplusplus
|
|
||||||
)
|
|
||||||
|
|
||||||
nanosm_target(nanosm)
|
|
||||||
|
|
||||||
# todo: cmake install sex
|
|
|
@ -1,67 +0,0 @@
|
||||||
# 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 -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"
|
|
||||||
# If on Release use link-time optimizations.
|
|
||||||
# On clang we use ThinLTO for even better build performance.
|
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
|
||||||
set(NANOSM_CORE_COMPILE_ARGS "${NANOSM_CORE_COMPILE_ARGS} -flto=thin")
|
|
||||||
set(NANOSM_CORE_LINKER_ARGS "${NANOSM_CORE_LINKER_ARGS} -flto=thin")
|
|
||||||
else()
|
|
||||||
set(NANOSM_CORE_COMPILE_ARGS "${NANOSM_CORE_COMPILE_ARGS} -flto")
|
|
||||||
set(NANOSM_CORE_LINKER_ARGS "${NANOSM_CORE_LINKER_ARGS} -flto")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(_NANOSM_CORE_WANTED_SANITIZERS "")
|
|
||||||
|
|
||||||
if("asan" IN_LIST NANOSM_SANITIZERS)
|
|
||||||
# Error if someone's trying to mix asan and tsan together since they aren't compatible.
|
|
||||||
if("tsan" IN_LIST NANOSM_SANITIZERS)
|
|
||||||
message(FATAL_ERROR "ASAN and TSAN cannot be used together.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message(STATUS "Enabling ASAN because it was in NANOSM_SANITIZERS")
|
|
||||||
list(APPEND _NANOSM_CORE_WANTED_SANITIZERS "address")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("tsan" IN_LIST NANOSM_SANITIZERS)
|
|
||||||
if("asan" IN_LIST NANOSM_SANITIZERS)
|
|
||||||
message(FATAL_ERROR "ASAN and TSAN cannot be used together.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message(STATUS "Enabling TSAN because it was in NANOSM_SANITIZERS")
|
|
||||||
list(APPEND _NANOSM_CORE_WANTED_SANITIZERS "thread")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("ubsan" IN_LIST NANOSM_SANITIZERS)
|
|
||||||
message(STATUS "Enabling UBSAN because it was in NANOSM_SANITIZERS")
|
|
||||||
list(APPEND _NANOSM_CORE_WANTED_SANITIZERS "undefined")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
list(LENGTH _NANOSM_CORE_WANTED_SANITIZERS _NANOSM_CORE_WANTED_SANITIZERS_LENGTH)
|
|
||||||
if(NOT _NANOSM_CORE_WANTED_SANITIZERS_LENGTH EQUAL 0)
|
|
||||||
list(JOIN _NANOSM_CORE_WANTED_SANITIZERS "," _NANOSM_CORE_WANTED_SANITIZERS_ARG)
|
|
||||||
message(STATUS "Enabled sanitizers: ${_NANOSM_CORE_WANTED_SANITIZERS_ARG}")
|
|
||||||
set(NANOSM_CORE_COMPILE_ARGS "${NANOSM_CORE_COMPILE_ARGS} -fsanitize=${_NANOSM_CORE_WANTED_SANITIZERS_ARG}")
|
|
||||||
set(NANOSM_CORE_LINKER_ARGS "${NANOSM_CORE_LINKER_ARGS} -fsanitize=${_NANOSM_CORE_WANTED_SANITIZERS_ARG}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Set core CMake toolchain variables so that they get applied to all projects.
|
|
||||||
# A bit nasty, but /shrug, this way our third party libraries can be mostly sanitized/etc as well.
|
|
||||||
# We do NOT do this for CMake compiler features however.
|
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${NANOSM_CORE_COMPILE_ARGS}")
|
|
||||||
set(CMAKE_CXX_FLAGS "${NANOSM_CORE_COMPILE_ARGS}")
|
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -O0 -g3")
|
|
||||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -O3 -g3")
|
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O3 -s")
|
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -O0 -g3")
|
|
||||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -O3 -g3")
|
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3 -s")
|
|
||||||
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${NANOSM_CORE_LINKER_ARGS} -Wl,-z,noexecstack,-z,relro,-z,now")
|
|
|
@ -1,9 +0,0 @@
|
||||||
# Core compile arguments used for the whole project
|
|
||||||
#
|
|
||||||
# This is the driver, we include compiler/platform specific files here
|
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
||||||
include(CompilerFlags-GNU)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Unsupported (for now?) compiler ${CMAKE_CXX_COMPILER_ID}")
|
|
||||||
endif()
|
|
|
@ -1,22 +0,0 @@
|
||||||
# CMake policy configuration
|
|
||||||
|
|
||||||
# Macro to enable new CMake policy.
|
|
||||||
# Makes this file a *LOT* shorter.
|
|
||||||
macro (_new_cmake_policy policy)
|
|
||||||
if(POLICY ${policy})
|
|
||||||
#message(STATUS "Enabling new policy ${policy}")
|
|
||||||
cmake_policy(SET ${policy} NEW)
|
|
||||||
endif()
|
|
||||||
endmacro()
|
|
||||||
|
|
||||||
_new_cmake_policy(CMP0026) # CMake 3.0: Disallow use of the LOCATION property for build targets.
|
|
||||||
_new_cmake_policy(CMP0042) # CMake 3.0+ (2.8.12): MacOS "@rpath" in target's install name
|
|
||||||
_new_cmake_policy(CMP0046) # warn about non-existent dependencies
|
|
||||||
_new_cmake_policy(CMP0048) # CMake 3.0+: project() command now maintains VERSION
|
|
||||||
_new_cmake_policy(CMP0054) # CMake 3.1: Only interpret if() arguments as variables or keywords when unquoted.
|
|
||||||
_new_cmake_policy(CMP0056) # try_compile() linker flags
|
|
||||||
_new_cmake_policy(CMP0066) # CMake 3.7: try_compile(): use per-config flags, like CMAKE_CXX_FLAGS_RELEASE
|
|
||||||
_new_cmake_policy(CMP0067) # CMake 3.8: try_compile(): honor language standard variables (like C++11)
|
|
||||||
_new_cmake_policy(CMP0068) # CMake 3.9+: `RPATH` settings on macOS do not affect `install_name`.
|
|
||||||
_new_cmake_policy(CMP0075) # CMake 3.12+: Include file check macros honor `CMAKE_REQUIRED_LIBRARIES`
|
|
||||||
_new_cmake_policy(CMP0077) # CMake 3.13+: option() honors normal variables.
|
|
|
@ -1,26 +0,0 @@
|
||||||
function(nanosm_target target)
|
|
||||||
target_compile_definitions(${target} PRIVATE "$<$<CONFIG:DEBUG>:NANOSM_DEBUG>")
|
|
||||||
target_compile_features(${target} PUBLIC cxx_std_20)
|
|
||||||
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
function(nanosm_set_alternate_linker)
|
|
||||||
find_program(LINKER_EXECUTABLE ld.${NANOSM_LINKER} ${NANOSM_LINKER})
|
|
||||||
if(LINKER_EXECUTABLE)
|
|
||||||
message(STATUS "Using ${NANOSM_LINKER} as argument to -fuse-ld=")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Linker ${NANOSM_LINKER} does not exist on your system. Please specify one which does or omit this option from your configure command.")
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# set the default linker based on compiler id, if one is not provided
|
|
||||||
# This is provided so that it can be overridden
|
|
||||||
if(NOT NANOSM_LINKER AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
|
||||||
set(NANOSM_LINKER "lld")
|
|
||||||
elseif(NOT NANOSM_LINKER)
|
|
||||||
set(NANOSM_LINKER "bfd")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
nanosm_set_alternate_linker()
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
[nanosm]
|
[nanosm]
|
||||||
# The window manager you want to use. This is the first application
|
# The window manager you want to use. This is the first application
|
||||||
# launched
|
# launched, and this will always be true
|
||||||
window-manager="/path/to/wm/binary --any-additional-args-here"
|
window-manager="/path/to/wm/binary --any-additional-args-here"
|
||||||
|
|
||||||
# Enable verbose debug logging. Only useful for debugging issues.
|
# Enable verbose debug logging. Only useful for debugging issues.
|
||||||
|
@ -12,9 +12,7 @@ verbose=false
|
||||||
restart-delay=1
|
restart-delay=1
|
||||||
|
|
||||||
# Any applications besides your window manager 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
|
# Note that applications are executed in the order they are declared.
|
||||||
# this will not hold true if any (or all apps) crash (they will be restarted
|
|
||||||
# effectively in a psuedorandom order).
|
|
||||||
[nanosm.apps]
|
[nanosm.apps]
|
||||||
lxpanel = { command = "lxpanel" }
|
lxpanel = { command = "lxpanel" }
|
||||||
pcmanfm-desktop = { command = "pcmanfm --desktop" }
|
pcmanfm-desktop = { command = "pcmanfm --desktop" }
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
#include "EventLoop.hpp"
|
|
||||||
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if(epollFd == -1) {
|
|
||||||
perror("You Banned From Epoll, Rules");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EventLoop::~EventLoop() {
|
|
||||||
close(epollFd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventLoop::Post(PostFn func) {
|
|
||||||
if(!func)
|
|
||||||
return;
|
|
||||||
postCallbacks.push_back(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventLoop::AddObject(IoObject::Ptr obj) {
|
|
||||||
if(!obj)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(obj->GetFD() == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ioObjects.insert(obj);
|
|
||||||
|
|
||||||
epoll_event ev {};
|
|
||||||
|
|
||||||
ev.events = obj->InterestedEvents();
|
|
||||||
ev.data.fd = obj->GetFD();
|
|
||||||
|
|
||||||
epoll_ctl(epollFd, EPOLL_CTL_ADD, obj->GetFD(), &ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventLoop::RemoveObject(IoObject::Ptr obj) {
|
|
||||||
if(!obj)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ioObjects.erase(obj);
|
|
||||||
epoll_event ev {};
|
|
||||||
|
|
||||||
ev.events = obj->InterestedEvents();
|
|
||||||
ev.data.fd = obj->GetFD();
|
|
||||||
|
|
||||||
epoll_ctl(epollFd, EPOLL_CTL_DEL, obj->GetFD(), &ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventLoop::Run() {
|
|
||||||
UniqueArray<epoll_event> events { 16 };
|
|
||||||
|
|
||||||
while(!shouldStop) {
|
|
||||||
auto nevents = epoll_wait(epollFd, events.Get(), events.Size(), 10);
|
|
||||||
if(nevents == -1) {
|
|
||||||
perror("epoll_wait");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// All OK, let's check for events now
|
|
||||||
|
|
||||||
for(int i = 0; i < nevents; ++i) {
|
|
||||||
for(auto pollable : ioObjects) {
|
|
||||||
if(auto fd = pollable->GetFD(); fd != -1) {
|
|
||||||
// Signal any events that occur for this
|
|
||||||
if(events[i].data.fd == fd)
|
|
||||||
pollable->OnReady(events[i].events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the topmost callback once every event loop iteration.
|
|
||||||
if(!postCallbacks.empty()) {
|
|
||||||
auto& frontCallback = postCallbacks.front();
|
|
||||||
frontCallback();
|
|
||||||
postCallbacks.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EventLoop::Stop() {
|
|
||||||
shouldStop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nanosm
|
|
|
@ -1,70 +1,34 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <functional>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "Types.hpp"
|
|
||||||
|
|
||||||
namespace nanosm {
|
namespace nanosm {
|
||||||
|
|
||||||
/// The nanosm event loop. Not that barren, but basically
|
/// The nanosm event loop. Pretty barren.
|
||||||
/// just enough to have the program working.
|
|
||||||
struct EventLoop {
|
struct EventLoop {
|
||||||
using PostFn = std::function<void()>;
|
|
||||||
|
|
||||||
/// A IO object.
|
/// A pollable object.
|
||||||
struct IoObject {
|
struct Pollable {
|
||||||
using Ptr = std::shared_ptr<IoObject>;
|
virtual ~Pollable() = default;
|
||||||
|
|
||||||
explicit IoObject(EventLoop& assoc)
|
/// Returns the FD. May return nullopt if there is no active file descriptor
|
||||||
: eventLoop(assoc) {
|
/// for this polled object (this simply means you won't get events until there is one)
|
||||||
}
|
virtual std::optional<int> GetFD() const = 0;
|
||||||
|
|
||||||
virtual ~IoObject() = default;
|
|
||||||
|
|
||||||
/// Returns the file descriptor for this IO object.
|
|
||||||
virtual int GetFD() const = 0;
|
|
||||||
|
|
||||||
/// Return raw event mask
|
|
||||||
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
|
virtual void OnReady() = 0;
|
||||||
virtual void OnReady(int eventMask) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
EventLoop& eventLoop;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
EventLoop();
|
EventLoop();
|
||||||
~EventLoop();
|
~EventLoop();
|
||||||
|
|
||||||
/// Add an object to the epoll event loop.
|
/// Add an object to the epoll event loop.
|
||||||
void AddObject(IoObject::Ptr obj);
|
void AddObject(int fd, std::shared_ptr<Pollable> obj);
|
||||||
|
|
||||||
void RemoveObject(IoObject::Ptr obj);
|
|
||||||
|
|
||||||
/// Posts a function to run after the epoll events are dispatched.
|
|
||||||
/// This queue of functions is mutually exclusive, so is also usable by
|
|
||||||
/// I/O objects to post completion notifications/callbacks.
|
|
||||||
void Post(PostFn func);
|
|
||||||
|
|
||||||
/// Runs the main loop.
|
/// Runs the main loop.
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int epollFd {};
|
int epollFd{};
|
||||||
bool shouldStop { false };
|
|
||||||
|
|
||||||
std::set<std::shared_ptr<IoObject>> ioObjects {};
|
|
||||||
std::deque<PostFn> postCallbacks {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nanosm
|
}
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
#include "Process.hpp"
|
|
||||||
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <sched.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "WordExp.hpp"
|
|
||||||
|
|
||||||
namespace nanosm {
|
|
||||||
|
|
||||||
inline static pid_t Clone3(const clone_args& args) {
|
|
||||||
return syscall(SYS_clone3, &args, sizeof(clone_args));
|
|
||||||
}
|
|
||||||
|
|
||||||
Process::~Process() {
|
|
||||||
Kill();
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Process::GetFD() const {
|
|
||||||
return pidFd;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Process::InterestedEvents() const {
|
|
||||||
/// Pidfd only returns EPOLLIN.
|
|
||||||
return EPOLLIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Process::Spawn(const std::string& commandLine) {
|
|
||||||
commLine = commandLine;
|
|
||||||
Respawn();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Process::Respawn() {
|
|
||||||
pid = Clone3({
|
|
||||||
.flags = CLONE_PIDFD,
|
|
||||||
.pidfd = std::bit_cast<u64>(&pidFd),
|
|
||||||
.exit_signal = SIGCHLD,
|
|
||||||
});
|
|
||||||
|
|
||||||
if(pid < 0)
|
|
||||||
perror("Clone3()");
|
|
||||||
|
|
||||||
if(pid == 0) {
|
|
||||||
// Forked from the parent successfully, execute the given thing
|
|
||||||
auto exp = nanosm::WordExp::Expand(commLine);
|
|
||||||
std::vector<char*> argv(exp.words.size());
|
|
||||||
for(usize i = 0; i < exp.words.size(); ++i)
|
|
||||||
argv[i] = exp.words[i].data();
|
|
||||||
|
|
||||||
execvp(exp.words[0].data(), argv.data());
|
|
||||||
} else {
|
|
||||||
// Parent: monitor FD
|
|
||||||
eventLoop.AddObject(IoObject::Ptr(shared_from_this()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Process::OnReady(int eventMask) {
|
|
||||||
// In our case, any readiness signaled by the pidfd means the process exited
|
|
||||||
// so this will never block (or really, wait).
|
|
||||||
waitid(P_PIDFD, pidFd, &siginfo, WNOHANG);
|
|
||||||
|
|
||||||
// Post a callback to call the user's exit callback (if one exists)
|
|
||||||
eventLoop.Post([self = shared_from_this()]() {
|
|
||||||
// Prepare for re-attaching, or etc.
|
|
||||||
self->eventLoop.RemoveObject(self);
|
|
||||||
self->Reset();
|
|
||||||
|
|
||||||
if(self->onProcessExit)
|
|
||||||
self->onProcessExit(self->siginfo.si_status);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Process::Kill() {
|
|
||||||
if(pid != -1)
|
|
||||||
kill(pid, SIGTERM);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Process::SetExitCallback(std::function<void(int)> f) {
|
|
||||||
onProcessExit = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Process::Reset() {
|
|
||||||
if(pidFd != -1) {
|
|
||||||
close(pidFd);
|
|
||||||
pidFd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid = -1;
|
|
||||||
siginfo = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nanosm
|
|
|
@ -1,41 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
|
|
||||||
#include "EventLoop.hpp"
|
|
||||||
|
|
||||||
namespace nanosm {
|
|
||||||
|
|
||||||
struct Process : nanosm::EventLoop::IoObject, std::enable_shared_from_this<Process> {
|
|
||||||
// bring ctor in
|
|
||||||
using nanosm::EventLoop::IoObject::IoObject;
|
|
||||||
|
|
||||||
virtual ~Process();
|
|
||||||
|
|
||||||
int GetFD() const override;
|
|
||||||
|
|
||||||
int InterestedEvents() const override;
|
|
||||||
|
|
||||||
void Spawn(const std::string& commandLine);
|
|
||||||
|
|
||||||
void Respawn();
|
|
||||||
|
|
||||||
void OnReady(int eventMask) override;
|
|
||||||
|
|
||||||
void Kill();
|
|
||||||
|
|
||||||
void SetExitCallback(std::function<void(int)> f);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void Reset();
|
|
||||||
|
|
||||||
int pidFd { -1 };
|
|
||||||
pid_t pid { -1 };
|
|
||||||
siginfo_t siginfo {};
|
|
||||||
std::string commLine;
|
|
||||||
|
|
||||||
std::function<void(int)> onProcessExit;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nanosm
|
|
|
@ -1,55 +0,0 @@
|
||||||
#include "Timer.hpp"
|
|
||||||
|
|
||||||
#include <sys/timerfd.h>
|
|
||||||
|
|
||||||
namespace nanosm {
|
|
||||||
|
|
||||||
Timer::Timer(nanosm::EventLoop& ev)
|
|
||||||
: nanosm::EventLoop::IoObject(ev) {
|
|
||||||
timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
|
||||||
if(timerFd < 0)
|
|
||||||
perror("timerfd_create");
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer::~Timer() {
|
|
||||||
Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Timer::Reset() {
|
|
||||||
if(timerFd != -1)
|
|
||||||
close(timerFd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Timer::GetFD() const {
|
|
||||||
return timerFd;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Timer::InterestedEvents() const {
|
|
||||||
/// TimerFD only returns EPOLLIN.
|
|
||||||
return EPOLLIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Timer::OnReady(int eventMask) {
|
|
||||||
u64 expiryCount {};
|
|
||||||
|
|
||||||
// Read the amount of timer expires so it stops screaming.
|
|
||||||
static_cast<void>(read(timerFd, &expiryCount, sizeof(expiryCount)));
|
|
||||||
|
|
||||||
// Post the expiry callback directly into the event loop
|
|
||||||
if(cb)
|
|
||||||
eventLoop.Post(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Timer::SetExpiryCallback(std::function<void()> expire) {
|
|
||||||
cb = expire;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Timer::Arm(u32 durationSeconds) {
|
|
||||||
itimerspec spec {};
|
|
||||||
spec.it_value.tv_sec = durationSeconds;
|
|
||||||
|
|
||||||
// TODO: validate.
|
|
||||||
timerfd_settime(timerFd, 0, &spec, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nanosm
|
|
|
@ -1,25 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "EventLoop.hpp"
|
|
||||||
|
|
||||||
namespace nanosm {
|
|
||||||
|
|
||||||
struct Timer : nanosm::EventLoop::IoObject, std::enable_shared_from_this<Timer> {
|
|
||||||
Timer(nanosm::EventLoop& ev);
|
|
||||||
|
|
||||||
virtual ~Timer();
|
|
||||||
|
|
||||||
void Reset();
|
|
||||||
int GetFD() const override;
|
|
||||||
|
|
||||||
int InterestedEvents() const override;
|
|
||||||
|
|
||||||
void OnReady(int eventMask) override;
|
|
||||||
|
|
||||||
void SetExpiryCallback(std::function<void()> expire);
|
|
||||||
void Arm(u32 durationSeconds);
|
|
||||||
private:
|
|
||||||
int timerFd { -1 };
|
|
||||||
std::function<void()> cb;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nanosm
|
|
|
@ -1,13 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
using u8 = std::uint8_t;
|
|
||||||
using i8 = std::int8_t;
|
|
||||||
using u16 = std::uint16_t;
|
|
||||||
using i16 = std::int16_t;
|
|
||||||
using u32 = std::uint32_t;
|
|
||||||
using i32 = std::int32_t;
|
|
||||||
using u64 = std::uint64_t;
|
|
||||||
using i64 = std::int64_t;
|
|
||||||
using usize = std::size_t;
|
|
||||||
using isize = std::intptr_t;
|
|
|
@ -1,57 +0,0 @@
|
||||||
#include "WordExp.hpp"
|
|
||||||
|
|
||||||
#include "Types.hpp"
|
|
||||||
|
|
||||||
namespace nanosm {
|
|
||||||
WordExp WordExp::Expand(std::string_view string) {
|
|
||||||
WordExp res {};
|
|
||||||
|
|
||||||
usize startIndex {};
|
|
||||||
usize endIndex {};
|
|
||||||
bool inQuotes { false };
|
|
||||||
|
|
||||||
auto len = [&]() { return (endIndex - startIndex); };
|
|
||||||
// auto currentWord = [&]() { return std::string(string.data() + startIndex, len()); };
|
|
||||||
auto addWord = [&](auto nextIndex) {
|
|
||||||
// a bit of a HACK. should be fixed properly, I think
|
|
||||||
if(len() > 1)
|
|
||||||
res.words.emplace_back(string.data() + startIndex, len());
|
|
||||||
|
|
||||||
startIndex = nextIndex;
|
|
||||||
endIndex = startIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
for(usize i = 0; i < string.length(); ++i) {
|
|
||||||
switch(string[i]) {
|
|
||||||
case '"':
|
|
||||||
if(!inQuotes) {
|
|
||||||
inQuotes = true;
|
|
||||||
startIndex = i + 1;
|
|
||||||
} else {
|
|
||||||
inQuotes = false;
|
|
||||||
addWord(i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
endIndex++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ' ':
|
|
||||||
// Spaces are allowed in quoted strings
|
|
||||||
if(!inQuotes)
|
|
||||||
addWord(i + 1);
|
|
||||||
else
|
|
||||||
endIndex++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: endIndex++; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the last word
|
|
||||||
if(len() != 0)
|
|
||||||
addWord(0);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nanosm
|
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace nanosm {
|
|
||||||
|
|
||||||
/// A kind-of reimplementation of wordexp(3) in C++, since it's rife with security issues,
|
|
||||||
/// annoying, and very poorly implemented (one implementation actually
|
|
||||||
/// ends up spawning a shell, that end up running Perl code. I'm not kidding.)
|
|
||||||
struct WordExp {
|
|
||||||
std::vector<std::string> words;
|
|
||||||
|
|
||||||
/// Expand a string (e.g "hello world \"testing 1234\"") into individual parts.
|
|
||||||
/// This function also respects quotation marks.
|
|
||||||
///
|
|
||||||
/// This function does NOT:
|
|
||||||
/// - Expand environment strings (e.g: $PWD or etc.)
|
|
||||||
/// - Expand shell ~~injection~~ strings (eg `uname -r`)
|
|
||||||
///
|
|
||||||
/// For the sample input, the returned object's `words` vector would look like:
|
|
||||||
/// [0]: hello
|
|
||||||
/// [1]: world
|
|
||||||
/// [2]: testing 1234
|
|
||||||
static WordExp Expand(std::string_view string);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nanosm
|
|
37
src/main.cpp
37
src/main.cpp
|
@ -1,42 +1,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include <ctime>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "EventLoop.hpp"
|
#include "EventLoop.hpp"
|
||||||
#include "WordExp.hpp"
|
|
||||||
|
|
||||||
#include "Timer.hpp"
|
|
||||||
#include "Process.hpp"
|
|
||||||
|
|
||||||
nanosm::EventLoop ev;
|
|
||||||
|
|
||||||
// tests stuff :)
|
|
||||||
|
|
||||||
auto timer = std::make_shared<nanosm::Timer>(ev);
|
|
||||||
auto process = std::make_shared<nanosm::Process>(ev);
|
|
||||||
|
|
||||||
void test() {
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
ev.Post(test);
|
nanosm::EventLoop ev;
|
||||||
|
|
||||||
ev.Run();
|
ev.Run();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 89406c77e6480c212b17e0ce7939f9ee833e909d
|
|
Loading…
Reference in New Issue