Files
MontaukOS/programs/src/init/main.cpp
T

240 lines
6.7 KiB
C++

/*
* main.cpp
* Init system for MontaukOS (PID 0)
* Discovers and starts system services from 0:/config/init.toml.
* Copyright (c) 2026 Daniel Hammer
*/
#include <montauk/syscall.h>
#include <montauk/config.h>
#include <libc/stdio.h>
// ---- ANSI color codes ----
#define C_RESET "\033[0m"
#define C_BOLD "\033[1m"
#define C_DIM "\033[2m"
#define C_RED "\033[31m"
#define C_GREEN "\033[32m"
#define C_YELLOW "\033[33m"
#define C_BLUE "\033[34m"
#define C_CYAN "\033[36m"
#define C_WHITE "\033[37m"
// ---- Process scan ----
static bool system_already_initialized() {
/* A valid init will always be PID 0 */
if (montauk::getpid() != 0) return true;
return false;
}
// ---- Logging ----
static void log_timestamp(char* buf, int size) {
montauk::abi::DateTime dt;
montauk::gettime(&dt);
snprintf(buf, size, "%02d:%02d:%02d", dt.Hour, dt.Minute, dt.Second);
}
enum LogLevel { LOG_OK, LOG_INFO, LOG_WARN, LOG_ERR };
static void log(LogLevel level, const char* msg) {
char line[512];
char ts[16];
log_timestamp(ts, sizeof(ts));
const char* tag;
const char* color;
switch (level) {
case LOG_OK: tag = "OK:"; color = C_GREEN; break;
case LOG_INFO: tag = "INFO:"; color = C_CYAN; break;
case LOG_WARN: tag = "WARN:"; color = C_YELLOW; break;
case LOG_ERR: tag = "FAIL:"; color = C_RED; break;
}
snprintf(line, sizeof(line),
C_DIM "%s" C_RESET " %s%s" C_RESET " %s",
ts, color, tag, msg);
montauk::write_log("init", line);
}
static void log_ok(const char* msg) { log(LOG_OK, msg); }
static void log_info(const char* msg) { log(LOG_INFO, msg); }
static void log_warn(const char* msg) { log(LOG_WARN, msg); }
static void log_err(const char* msg) { log(LOG_ERR, msg); }
// ---- Service runner ----
static bool service_installed(const char* path) {
int fd = montauk::open(path);
if (fd < 0) return false;
montauk::close(fd);
return true;
}
static bool run_service(const char* path, const char* name, bool wait = true) {
char msg[128];
snprintf(msg, sizeof(msg), "Starting %s", name);
log_info(msg);
int pid = montauk::spawn(path);
if (pid < 0) {
snprintf(msg, sizeof(msg), "Failed to start %s", name);
log_err(msg);
return false;
}
if (wait) {
montauk::waitpid(pid);
snprintf(msg, sizeof(msg), "%s finished (pid %d)", name, pid);
log_ok(msg);
} else {
snprintf(msg, sizeof(msg), "%s spawned in background (pid %d)", name, pid);
log_ok(msg);
}
return true;
}
// ---- Service configuration ----
static constexpr const char* SERVICE_PREFIX = "services.";
static bool is_service_table(const montauk::toml::Value* value) {
if (!value || value->type != montauk::toml::Type::Table || !value->key)
return false;
if (!montauk::starts_with(value->key, SERVICE_PREFIX))
return false;
// Only [services.<id>] declares a service. Nested tables belong to that
// service and must not be treated as additional services.
const char* id = value->key + montauk::slen(SERVICE_PREFIX);
if (!*id) return false;
while (*id) {
if (*id == '.') return false;
id++;
}
return true;
}
static void service_key(char* out, int out_size, const char* id, const char* field) {
snprintf(out, out_size, "%s%s.%s", SERVICE_PREFIX, id, field);
}
static montauk::toml::Value* find_service(const montauk::toml::Doc& doc, const char* id) {
char table_key[256];
snprintf(table_key, sizeof(table_key), "%s%s", SERVICE_PREFIX, id);
return doc.get_table(table_key);
}
static bool run_configured_service(const montauk::toml::Doc& doc,
montauk::toml::Value* service,
int fallback_depth = 0) {
const char* id = service->key + montauk::slen(SERVICE_PREFIX);
char key[256];
char msg[192];
service_key(key, sizeof(key), id, "name");
const char* name = doc.get_string(key, id);
service_key(key, sizeof(key), id, "enabled");
if (!doc.get_bool(key, true)) {
snprintf(msg, sizeof(msg), "%s disabled, skipping", name);
log_info(msg);
return true;
}
service_key(key, sizeof(key), id, "path");
const char* path = doc.get_string(key, "");
if (!path[0]) {
snprintf(msg, sizeof(msg), "Service %s has no path, skipping", id);
log_err(msg);
return false;
}
service_key(key, sizeof(key), id, "optional");
bool optional = doc.get_bool(key, false);
if (optional && !service_installed(path)) {
snprintf(msg, sizeof(msg), "%s not installed, skipping", name);
log_info(msg);
return true;
}
service_key(key, sizeof(key), id, "wait");
bool wait = doc.get_bool(key, true);
if (run_service(path, name, wait)) return true;
service_key(key, sizeof(key), id, "fallback");
const char* fallback = doc.get_string(key, "");
if (!fallback[0]) return false;
if (fallback_depth >= 8) {
log_err("Service fallback chain is too deep");
return false;
}
auto* fallback_service = find_service(doc, fallback);
if (!fallback_service) {
snprintf(msg, sizeof(msg), "Fallback service %s is not configured", fallback);
log_err(msg);
return false;
}
snprintf(msg, sizeof(msg), "%s failed, falling back to %s", name, fallback);
log_warn(msg);
return run_configured_service(doc, fallback_service, fallback_depth + 1);
}
const char* getReleaseString(char* buffer, int n) {
montauk::abi::SysInfo info;
montauk::get_info(&info);
snprintf(buffer, n, "The Montauk Operating System, version %s (build %u)",
info.osVersion, info.buildNumber);
return buffer;
}
// ---- Main ----
extern "C" void _start() {
if (system_already_initialized()) {
montauk::exit(1);
}
char releaseString[128] = "";
getReleaseString(releaseString, 128);
log_info(releaseString);
auto config = montauk::config::load("init");
int discovered = 0;
// Table order is file order, making the config itself the startup order.
for (int i = 0; i < config.entries.count; i++) {
auto* service = config.entries.items[i];
if (!is_service_table(service)) continue;
discovered++;
const char* id = service->key + montauk::slen(SERVICE_PREFIX);
char key[256];
service_key(key, sizeof(key), id, "fallback_only");
if (config.get_bool(key, false)) continue;
run_configured_service(config, service);
}
if (discovered == 0)
log_err("No services found in 0:/config/init.toml");
config.destroy();
log_warn("All services exited");
for (;;) {
montauk::yield();
}
}