Files
nt-lite/code/user/system/init/init.c
T

1008 lines
44 KiB
C

// Init service manager. The kernel bootstraps only init; init starts and supervises user-mode system services.
#include <stdint.h>
#include <ntlite/bootstrap_capabilities.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/exception.h>
#include <ntlite/input_service.h>
#include <ntlite/console_service.h>
#include <ntlite/desktop.h>
#include <ntlite/process_loader.h>
#include <ntlite/resource.h>
#include <ntlite/security.h>
#include <ntlite/service_control.h>
#include <ntlite/syscall.h>
#include "service_config.h"
#include "restart_budget.h"
typedef enum INIT_SERVICE_STATE {
InitServiceStopped = 0,
InitServiceStarting,
InitServiceRestartPending,
InitServiceReady,
InitServiceStopping,
InitServiceFailed,
} INIT_SERVICE_STATE;
typedef struct INIT_SERVICE {
const INIT_SERVICE_DESCRIPTOR* Descriptor;
SYS_HANDLE Process;
SYS_HANDLE ControlEndpoint;
INIT_SERVICE_STATE State;
uint64_t StartedTick;
uint64_t RestartTick;
} INIT_SERVICE;
static INIT_SERVICE_CONFIG gServiceConfig;
static INIT_SERVICE gServices[INIT_SERVICE_MAX_COUNT];
static uint64_t gTickFrequency;
static INIT_RESTART_BUDGET gRestartBudgets[INIT_SERVICE_MAX_COUNT];
_Static_assert(INIT_SERVICE_MAX_COUNT + 2u <= SYS_WAIT_MANY_MAX_HANDLES,
"Service supervisor exceeds WaitMany capacity");
// Capability sources are referenced by name; the catalog owns READY handles
// and borrows kernel-provided bootstrap handles for the lifetime of init.
typedef struct INIT_GRANT_SOURCE {
char Name[SYS_STARTUP_CAPABILITY_NAME_MAX + 1u];
SYS_HANDLE Handle;
SYS_RIGHTS RightsCeiling;
const INIT_SERVICE_DESCRIPTOR* Provider;
bool OwnsHandle;
} INIT_GRANT_SOURCE;
#define INIT_GRANT_SOURCE_MAX (8u + INIT_SERVICE_MAX_COUNT * SYS_PORT_MAX_TRANSFER_HANDLES)
static INIT_GRANT_SOURCE gGrantSources[INIT_GRANT_SOURCE_MAX];
static uint32_t gGrantSourceCount;
// Records one source and its strict rights ceiling without granting any authority.
static KSTATUS add_grant_source(const char* name, SYS_HANDLE handle, SYS_RIGHTS rights,
const INIT_SERVICE_DESCRIPTOR* provider, bool ownsHandle) {
if (!name || !name[0] || handle == SYS_HANDLE_INVALID || !rights ||
gGrantSourceCount >= INIT_GRANT_SOURCE_MAX) return NTLSTATUS_INVALID_PARAMETER;
size_t length = strlen(name);
if (length > SYS_STARTUP_CAPABILITY_NAME_MAX) return NTLSTATUS_NAME_TOO_LONG;
for (uint32_t i = 0; i < gGrantSourceCount; ++i)
if (strcmp(gGrantSources[i].Name, name) == 0) return NTLSTATUS_OBJECT_NAME_COLLISION;
INIT_GRANT_SOURCE* source = &gGrantSources[gGrantSourceCount++];
memset(source, 0, sizeof(*source));
memcpy(source->Name, name, length + 1u);
source->Handle = handle;
source->RightsCeiling = rights;
source->Provider = provider;
source->OwnsHandle = ownsHandle;
return NTLSTATUS_SUCCESS;
}
// Drops all sources exported by a service generation before its handles expire.
static void forget_service_grants(const INIT_SERVICE_DESCRIPTOR* provider) {
if (!provider) return;
for (uint32_t i = 0; i < gGrantSourceCount;) {
INIT_GRANT_SOURCE* source = &gGrantSources[i];
if (source->Provider != provider) { ++i; continue; }
if (source->OwnsHandle && source->Handle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(source->Handle);
gGrantSources[i] = gGrantSources[--gGrantSourceCount];
memset(&gGrantSources[gGrantSourceCount], 0, sizeof(gGrantSources[0]));
}
}
// READY exports enter the catalog atomically after name and capacity validation.
static KSTATUS register_ready_exports(const INIT_SERVICE_DESCRIPTOR* provider,
const NTL_SERVICE_CONTROL_MESSAGE* message,
const SYS_HANDLE* handles, uint32_t count) {
if (!provider || !message || (count && !handles) || count != message->ExportCount ||
count > SYS_PORT_MAX_TRANSFER_HANDLES) return NTLSTATUS_INVALID_PARAMETER;
if (gGrantSourceCount + count > INIT_GRANT_SOURCE_MAX) return NTLSTATUS_LIMIT_REACHED;
char names[SYS_PORT_MAX_TRANSFER_HANDLES][SYS_STARTUP_CAPABILITY_NAME_MAX + 1u];
for (uint32_t i = 0; i < count; ++i) {
size_t left = strlen(provider->Id), right = strlen(message->ExportNames[i]);
if (left + right + 1u > SYS_STARTUP_CAPABILITY_NAME_MAX ||
handles[i] == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER;
memcpy(names[i], provider->Id, left);
names[i][left] = '.';
memcpy(names[i] + left + 1u, message->ExportNames[i], right + 1u);
for (uint32_t j = 0; j < gGrantSourceCount; ++j)
if (strcmp(names[i], gGrantSources[j].Name) == 0) return NTLSTATUS_OBJECT_NAME_COLLISION;
for (uint32_t j = 0; j < i; ++j)
if (strcmp(names[i], names[j]) == 0) return NTLSTATUS_OBJECT_NAME_COLLISION;
}
for (uint32_t i = 0; i < count; ++i) {
INIT_GRANT_SOURCE* source = &gGrantSources[gGrantSourceCount++];
memset(source, 0, sizeof(*source));
memcpy(source->Name, names[i], strlen(names[i]) + 1u);
source->Handle = handles[i];
source->RightsCeiling = SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
source->Provider = provider;
source->OwnsHandle = true;
}
return NTLSTATUS_SUCCESS;
}
// Populates the trusted bootstrap catalog from kernel-issued, init-owned handles.
static KSTATUS initialize_boot_grants(SYS_HANDLE namespaceVolume, SYS_HANDLE volumeManager,
SYS_HANDLE inputManager, SYS_HANDLE displayManager,
SYS_HANDLE registrationAuthority) {
gGrantSourceCount = 0;
KSTATUS status;
#define ADD_BOOT(name, handle, rights) \
do { status = add_grant_source(name, handle, rights, (const INIT_SERVICE_DESCRIPTOR*)0, false); \
if (!KSUCCESS(status)) return status; } while (0)
ADD_BOOT(NTL_BOOT_CAP_NAMESPACE_ROOT, namespaceVolume,
SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER | SYS_VOLUME_RIGHT_OPEN | SYS_VOLUME_RIGHT_MODIFY);
ADD_BOOT(NTL_BOOT_CAP_VOLUME_MANAGER, volumeManager,
SYS_RIGHT_QUERY | SYS_VOLUME_MANAGER_RIGHT_ENUMERATE |
SYS_VOLUME_MANAGER_RIGHT_OPEN | SYS_VOLUME_MANAGER_RIGHT_WAIT);
ADD_BOOT(NTL_BOOT_CAP_INPUT_MANAGER, inputManager, SYS_RIGHT_QUERY | SYS_INPUT_MANAGER_RIGHT_READ);
ADD_BOOT(NTL_BOOT_CAP_DISPLAY_POLICY, displayManager,
SYS_RIGHT_DERIVE | SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY |
SYS_DISPLAY_MANAGER_RIGHT_ENUMERATE | SYS_DISPLAY_MANAGER_RIGHT_MODESET |
SYS_DISPLAY_MANAGER_RIGHT_CONSOLE);
ADD_BOOT(NTL_BOOT_CAP_DISPLAY_PRESENT, displayManager,
SYS_RIGHT_QUERY | SYS_DISPLAY_MANAGER_RIGHT_ENUMERATE |
SYS_DISPLAY_MANAGER_RIGHT_PRESENT | SYS_DISPLAY_MANAGER_RIGHT_CURSOR);
ADD_BOOT(NTL_BOOT_CAP_RESOURCE_REGISTRATION, registrationAuthority,
SYS_RIGHT_QUERY | SYS_SERVICE_REGISTRATION_RIGHT_REGISTER |
SYS_SERVICE_REGISTRATION_RIGHT_UNREGISTER);
#undef ADD_BOOT
return NTLSTATUS_SUCCESS;
}
// Resolves configured names into attenuated child capabilities before creation.
static KSTATUS resolve_service_grants(const INIT_SERVICE_DESCRIPTOR* descriptor,
NTL_PROCESS_STARTUP_CAPABILITY* resolved,
uint32_t* countOut) {
if (!descriptor || !resolved || !countOut || descriptor->GrantCount >= SYS_STARTUP_CAPABILITY_MAX)
return NTLSTATUS_INVALID_PARAMETER;
*countOut = 0;
for (uint32_t i = 0; i < descriptor->GrantCount; ++i) {
const INIT_GRANT_SOURCE* found = (const INIT_GRANT_SOURCE*)0;
for (uint32_t j = 0; j < gGrantSourceCount; ++j)
if (strcmp(descriptor->Grants[i], gGrantSources[j].Name) == 0) {
found = &gGrantSources[j]; break;
}
if (!found) return NTLSTATUS_NOT_FOUND;
NTL_PROCESS_STARTUP_CAPABILITY* destination = &resolved[i];
memset(destination, 0, sizeof(*destination));
memcpy(destination->Name, found->Name, strlen(found->Name) + 1u);
destination->SourceHandle = found->Handle;
destination->DesiredRights = found->RightsCeiling;
destination->DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
++*countOut;
}
return NTLSTATUS_SUCCESS;
}
// Writes one string through the privileged kernel debug channel.
static void debug_print(const char* text) {
SYS_PRINT_DEBUG_REQUEST request = { sizeof(request), SysSvFromCstr(text) };
(void)SysPrintDebug(&request);
}
// Writes one status value in hexadecimal without pulling in a formatting runtime.
static void debug_status(KSTATUS status) {
static const char digits[] = "0123456789ABCDEF";
char buffer[11] = "0x00000000";
for (uint32_t i = 0; i < 8; ++i)
buffer[2 + i] = digits[(status >> ((7u - i) * 4u)) & 0xFu];
debug_print(buffer);
}
// Writes one unsigned decimal value without pulling in a formatting runtime.
static void debug_u32(uint32_t value) {
char buffer[11];
uint32_t length = 0;
do {
buffer[length++] = (char)('0' + value % 10u);
value /= 10u;
} while (value != 0u);
for (uint32_t i = 0; i < length / 2u; ++i) {
char temporary = buffer[i];
buffer[i] = buffer[length - 1u - i];
buffer[length - 1u - i] = temporary;
}
buffer[length] = '\0';
debug_print(buffer);
}
// Writes one signed decimal value without pulling in a formatting runtime.
static void debug_i32(int32_t value) {
if (value < 0) debug_print("-");
uint32_t magnitude = value < 0 ? (uint32_t)(-(int64_t)value) : (uint32_t)value;
debug_u32(magnitude);
}
// Reports a managed service that terminated before completing its READY contract.
static void debug_early_service_exit(const INIT_SERVICE* service) {
if (!service || !service->Descriptor || service->Process == SYS_HANDLE_INVALID) return;
KSTATUS waitStatus = SysWaitSingle(service->Process, SYS_WAIT_FLAG_NONE, 0);
if (waitStatus != NTLSTATUS_SUCCESS) return;
int32_t exitCode = -1;
if (SysGetProcessExitCode(service->Process, &exitCode) != NTLSTATUS_SUCCESS) return;
debug_print("[init] service ");
debug_print(service->Descriptor->Id);
debug_print(" exited during startup with code ");
debug_i32(exitCode);
debug_print("\n");
}
// Exits init without relying on a compiler runtime return path.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Initializes one managed-service runtime record in the stopped state.
static void initialize_service(INIT_SERVICE* service, const INIT_SERVICE_DESCRIPTOR* descriptor) {
memset(service, 0, sizeof(*service));
service->Descriptor = descriptor;
service->Process = SYS_HANDLE_INVALID;
service->ControlEndpoint = SYS_HANDLE_INVALID;
service->State = InitServiceStopped;
}
// Derives the unprivileged security context used by replaceable user-mode services.
static KSTATUS derive_unprivileged_service_context(SYS_HANDLE* contextOut) {
if (!contextOut) return NTLSTATUS_INVALID_PARAMETER;
*contextOut = SYS_HANDLE_INVALID;
SYS_DERIVE_SECURITY_CONTEXT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.SourceContext = SYS_SECURITY_CONTEXT_CURRENT;
request.EffectivePrivileges = 0;
request.DelegablePrivileges = 0;
request.Flags = SYS_SECURITY_CONTEXT_FLAG_NONE;
request.ContextHandleOut = contextOut;
return SysDeriveSecurityContext(&request);
}
// Creates one private Port listener used only between init and a managed service.
static KSTATUS create_control_listener(SYS_HANDLE* listenerOut) {
SYS_CREATE_PORT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Flags = SYS_PORT_FLAG_NONE;
request.Backlog = 1;
request.HandleOut = listenerOut;
return SysCreatePort(&request);
}
// Connects init to the private control listener delegated to the child.
static KSTATUS connect_control_listener(SYS_HANDLE listener, SYS_HANDLE* endpointOut) {
SYS_CONNECT_PORT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.ListenerHandle = listener;
request.Flags = SYS_PORT_CONNECT_FLAG_NONE;
request.EndpointHandleOut = endpointOut;
return SysConnectPort(&request);
}
// Closes the generic parent-side handles retained for one managed service.
static void close_service_handles(INIT_SERVICE* service) {
if (!service) return;
forget_service_grants(service->Descriptor);
if (service->ControlEndpoint != SYS_HANDLE_INVALID) {
(void)SysDisconnectPort(service->ControlEndpoint);
(void)SysCloseHandle(service->ControlEndpoint);
}
if (service->Process != SYS_HANDLE_INVALID)
(void)SysCloseHandle(service->Process);
service->Process = SYS_HANDLE_INVALID;
service->ControlEndpoint = SYS_HANDLE_INVALID;
}
// Starts one managed service process and establishes its private control connection.
static KSTATUS start_service_process(SYS_HANDLE systemVolume,
SYS_HANDLE securityContext,
INIT_SERVICE* service) {
if (!service || !service->Descriptor || service->Descriptor->ImagePath[0] == '\0' ||
service->Descriptor->GrantCount > SYS_STARTUP_CAPABILITY_MAX - 1u)
return NTLSTATUS_INVALID_PARAMETER;
if (service->Process != SYS_HANDLE_INVALID || service->ControlEndpoint != SYS_HANDLE_INVALID ||
(service->State != InitServiceStopped && service->State != InitServiceFailed &&
service->State != InitServiceRestartPending))
return NTLSTATUS_FAILURE;
service->State = InitServiceStarting;
service->StartedTick = SysGetTick();
SYS_HANDLE controlListener = SYS_HANDLE_INVALID;
KSTATUS status = create_control_listener(&controlListener);
if (!KSUCCESS(status)) {
service->State = InitServiceFailed;
return status;
}
NTL_PROCESS_STARTUP_CAPABILITY startup[SYS_STARTUP_CAPABILITY_MAX];
memset(startup, 0, sizeof(startup));
uint32_t startupCapabilityCount = 0;
status = resolve_service_grants(service->Descriptor, startup, &startupCapabilityCount);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(controlListener);
service->State = InitServiceFailed;
return status;
}
memcpy(startup[startupCapabilityCount].Name, "service.control", sizeof("service.control"));
startup[startupCapabilityCount].SourceHandle = controlListener;
startup[startupCapabilityCount].DesiredRights = SYS_RIGHT_QUERY | SYS_PORT_RIGHT_ACCEPT;
startup[startupCapabilityCount].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
uint32_t capabilityCount = startupCapabilityCount + 1u;
SYS_HANDLE process = SYS_HANDLE_INVALID;
SYS_STRING_VIEW imagePath = SysSvFromCstr(service->Descriptor->ImagePath);
NTL_CREATE_PROCESS_AT_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
create.RootHandle = systemVolume;
create.Path = imagePath;
create.CommandLine = imagePath;
create.SecurityContext = securityContext;
create.ModuleRootHandle = systemVolume;
create.ModuleDirectory = SYS_SV_LITERAL("system");
create.ResolveModuleName = NtlResolveModuleNameIdentity;
create.ResolveContext = (void*)0;
create.StartupCapabilities = startup;
create.StartupCapabilityCount = capabilityCount;
create.ProcessHandleOut = &process;
SYS_HANDLE privilegedContext = SYS_HANDLE_INVALID;
if (service->Descriptor->Privileges) {
SYS_DERIVE_SECURITY_CONTEXT_REQUEST derive = {0};
derive.Size = sizeof(derive);
derive.SourceContext = SYS_SECURITY_CONTEXT_CURRENT;
derive.EffectivePrivileges = service->Descriptor->Privileges;
derive.ContextHandleOut = &privilegedContext;
status = SysDeriveSecurityContext(&derive);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(controlListener);
service->State = InitServiceFailed;
return status;
}
create.SecurityContext = privilegedContext;
}
status = NtlCreateProcessAt(&create);
if (privilegedContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(privilegedContext);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(controlListener);
service->State = InitServiceFailed;
return status;
}
SYS_HANDLE controlEndpoint = SYS_HANDLE_INVALID;
status = connect_control_listener(controlListener, &controlEndpoint);
(void)SysCloseHandle(controlListener);
if (!KSUCCESS(status)) {
(void)SysKillProcess(process, 1);
(void)SysCloseHandle(process);
service->State = InitServiceFailed;
return status;
}
service->Process = process;
service->ControlEndpoint = controlEndpoint;
return NTLSTATUS_SUCCESS;
}
// Waits for one service READY notification and receives its service-defined export handles.
static KSTATUS wait_service_ready(INIT_SERVICE* service, uint64_t timeout,
SYS_HANDLE* exportsOut,
uint32_t exportCapacity,
uint32_t* exportCountOut,
NTL_SERVICE_CONTROL_MESSAGE* messageOut) {
if (!service || !service->Descriptor || !exportCountOut || !messageOut ||
exportCapacity > SYS_PORT_MAX_TRANSFER_HANDLES || (exportCapacity && !exportsOut))
return NTLSTATUS_INVALID_PARAMETER;
for (uint32_t i = 0; i < exportCapacity; ++i) exportsOut[i] = SYS_HANDLE_INVALID;
*exportCountOut = 0;
NTL_SERVICE_CONTROL_MESSAGE message;
SYS_PORT_MESSAGE_INFO info;
memset(&message, 0, sizeof(message));
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_RECEIVE_PORT_REQUEST receive;
memset(&receive, 0, sizeof(receive));
receive.Size = sizeof(receive);
receive.EndpointHandle = service->ControlEndpoint;
receive.Flags = SYS_PORT_FLAG_NONE;
receive.Payload = &message;
receive.PayloadCapacity = sizeof(message);
receive.HandlesOut = exportsOut;
receive.HandleCapacity = exportCapacity;
receive.InfoOut = &info;
receive.TimeoutMilliseconds = timeout;
KSTATUS status = SysReceivePort(&receive);
if (!KSUCCESS(status)) return status;
bool valid = info.Type == SYS_PORT_MESSAGE_NOTIFICATION &&
info.PayloadLength == sizeof(message) &&
info.HandleCount <= exportCapacity &&
NtlServiceControlValid(&message, NTL_SERVICE_CONTROL_READY, info.HandleCount);
if (!valid || !KSUCCESS(message.Status)) {
for (uint32_t i = 0; i < info.HandleCount && i < exportCapacity; ++i) {
if (exportsOut[i] != SYS_HANDLE_INVALID) {
(void)SysCloseHandle(exportsOut[i]);
exportsOut[i] = SYS_HANDLE_INVALID;
}
}
return valid ? message.Status : NTLSTATUS_FAILURE;
}
*exportCountOut = info.HandleCount;
*messageOut = message;
return NTLSTATUS_SUCCESS;
}
// Consumes one READY record, transferring published handles on success.
static KSTATUS finish_service_ready(INIT_SERVICE* service, uint64_t timeout) {
KSTATUS status;
SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES];
uint32_t count = 0;
NTL_SERVICE_CONTROL_MESSAGE ready;
status = wait_service_ready(service, timeout, handles, SYS_PORT_MAX_TRANSFER_HANDLES, &count, &ready);
if (!KSUCCESS(status)) {
debug_print("[init] READY receive failed for ");
debug_print(service->Descriptor->Id);
debug_print(": ");
debug_status(status);
debug_print("\n");
}
if (KSUCCESS(status)) {
const INIT_SERVICE_DESCRIPTOR* descriptor = service->Descriptor;
if (count != descriptor->ExportCount) status = NTLSTATUS_INVALID_PARAMETER;
for (uint32_t i = 0; KSUCCESS(status) && i < count; ++i) {
bool declared = false;
for (uint32_t j = 0; j < descriptor->ExportCount; ++j)
if (strcmp(ready.ExportNames[i], descriptor->Exports[j]) == 0) declared = true;
if (!declared || handles[i] == SYS_HANDLE_INVALID)
status = NTLSTATUS_INVALID_PARAMETER;
}
if (KSUCCESS(status)) {
status = register_ready_exports(descriptor, &ready, handles, count);
if (KSUCCESS(status))
for (uint32_t i = 0; i < count; ++i) handles[i] = SYS_HANDLE_INVALID;
}
}
for (uint32_t i = 0; i < count; ++i)
if (handles[i] != SYS_HANDLE_INVALID) (void)SysCloseHandle(handles[i]);
if (!KSUCCESS(status)) {
debug_early_service_exit(service);
(void)SysKillProcess(service->Process, 1);
(void)SysWait(service->Process, SYS_WAIT_FLAG_NONE);
close_service_handles(service);
service->State = InitServiceFailed;
return status;
}
service->State = InitServiceReady;
return NTLSTATUS_SUCCESS;
}
// Schedules a bounded retry for failed startup or a matching process exit.
static bool queue_service_restart(INIT_SERVICE* service) {
if (!service || service->Descriptor->RestartPolicy == InitServiceRestartNever) return false;
uint32_t index = (uint32_t)(service - gServices);
if (index >= gServiceConfig.ServiceCount ||
!InitConsumeRestartBudget(&gRestartBudgets[index], SysGetTick(), gTickFrequency,
service->Descriptor->RestartLimit,
service->Descriptor->RestartWindowMilliseconds)) return false;
uint64_t now = SysGetTick();
uint64_t delay = (uint64_t)service->Descriptor->RestartDelayMilliseconds * gTickFrequency;
delay = delay / 1000u + (delay % 1000u != 0u);
service->RestartTick = delay <= UINT64_MAX - now ? now + delay : UINT64_MAX;
service->State = InitServiceRestartPending;
return true;
}
// Starts eligible services, leaving READY completion to the supervisor.
static KSTATUS launch_services(bool includeDelayed, SYS_HANDLE volume,
SYS_HANDLE context) {
for (uint32_t order = 0; order < gServiceConfig.StartupCount; ++order) {
uint16_t index = gServiceConfig.StartupOrder[order];
INIT_SERVICE* service = &gServices[index];
const INIT_SERVICE_DESCRIPTOR* descriptor = service->Descriptor;
if (service->State != InitServiceStopped ||
(descriptor->StartPolicy != InitServiceStartAuto &&
(!includeDelayed || descriptor->StartPolicy != InitServiceStartDelayed))) continue;
bool pending = false, failed = false;
for (uint32_t d = 0; d < descriptor->DependencyCount; ++d) {
INIT_SERVICE_STATE state = gServices[descriptor->Dependencies[d]].State;
if (state == InitServiceFailed) failed = true;
else if (state != InitServiceReady) pending = true;
}
if (pending && !failed) continue;
if (!failed) {
for (uint32_t d = 0; d < descriptor->AfterCount; ++d) {
const INIT_SERVICE* predecessor = &gServices[descriptor->After[d]];
INIT_SERVICE_STATE state = predecessor->State;
if (predecessor->Descriptor->StartPolicy == InitServiceStartDisabled) continue;
if (state == InitServiceStopped || state == InitServiceStarting ||
state == InitServiceRestartPending) {
pending = true;
break;
}
}
}
if (pending && !failed) continue;
if (failed) {
service->State = InitServiceFailed;
debug_print("[init] service blocked: ");
debug_print(descriptor->Id);
debug_print("\n");
} else {
KSTATUS status = start_service_process(volume, context, service);
if (KSUCCESS(status)) continue;
debug_print("[init] service launch failed: ");
debug_print(descriptor->Id);
debug_print(" ");
debug_status(status);
debug_print("\n");
}
if (!failed && service->State == InitServiceFailed && queue_service_restart(service)) continue;
if (descriptor->Critical) return NTLSTATUS_FAILURE;
}
return NTLSTATUS_SUCCESS;
}
// Tests a transitive hard dependency in the validated acyclic service graph.
static bool service_depends_on(uint16_t serviceIndex, uint16_t providerIndex) {
const INIT_SERVICE_DESCRIPTOR* desc = &gServiceConfig.Services[serviceIndex];
for (uint32_t i = 0; i < desc->DependencyCount; ++i) {
uint16_t dep = desc->Dependencies[i];
if (dep == providerIndex || service_depends_on(dep, providerIndex)) return true;
}
return false;
}
// Critical consumers make their provider's startup a critical dependency.
static bool critical_service_affected(uint16_t providerIndex) {
if (gServices[providerIndex].Descriptor->Critical) return true;
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i)
if (gServices[i].Descriptor->Critical &&
service_depends_on((uint16_t)i, providerIndex)) return true;
return false;
}
// Advances one completed READY handshake from the existing Port wait event.
static KSTATUS complete_starting_service(INIT_SERVICE* service) {
KSTATUS status = finish_service_ready(service, 0u);
if (KSUCCESS(status)) {
debug_print("service: ready id=");
debug_print(service->Descriptor->Id);
debug_print("\n");
} else {
debug_print("[init] service startup failed: ");
debug_print(service->Descriptor->Id);
debug_print(" ");
debug_status(status);
debug_print("\n");
}
return status;
}
// Uses the original launch timestamp for each independent READY deadline.
static uint64_t starting_timeout_ms(uint64_t now) {
uint64_t nearest = SYS_WAIT_TIMEOUT_INFINITE;
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
const INIT_SERVICE* service = &gServices[i];
if (service->State != InitServiceStarting) continue;
uint64_t elapsed = now >= service->StartedTick ? now - service->StartedTick : 0u;
uint64_t limit = ((uint64_t)service->Descriptor->ReadyTimeoutMilliseconds *
gTickFrequency + 999u) / 1000u;
if (elapsed >= limit) return 0u;
uint64_t remaining = limit - elapsed;
uint64_t ms = (remaining / gTickFrequency) * 1000u +
((remaining % gTickFrequency) * 1000u + gTickFrequency - 1u) / gTickFrequency;
if (ms < nearest) nearest = ms;
}
return nearest;
}
// Starts due restart attempts and reports a failed critical provider.
static KSTATUS start_pending_restarts(SYS_HANDLE volume, SYS_HANDLE context) {
uint64_t now = SysGetTick();
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
INIT_SERVICE* service = &gServices[i];
if (service->State != InitServiceRestartPending || now < service->RestartTick) continue;
KSTATUS status = start_service_process(volume, context, service);
if (KSUCCESS(status)) continue;
debug_print("[init] service restart failed: ");
debug_print(service->Descriptor->Id);
debug_print("\n");
if (!queue_service_restart(service) && critical_service_affected((uint16_t)i))
return NTLSTATUS_FAILURE;
}
return NTLSTATUS_SUCCESS;
}
static uint64_t restart_timeout_ms(uint64_t now) {
uint64_t nearest = SYS_WAIT_TIMEOUT_INFINITE;
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
INIT_SERVICE* service = &gServices[i];
if (service->State != InitServiceRestartPending) continue;
if (now >= service->RestartTick) return 0u;
uint64_t ticks = service->RestartTick - now;
uint64_t ms = (ticks / gTickFrequency) * 1000u +
((ticks % gTickFrequency) * 1000u + gTickFrequency - 1u) / gTickFrequency;
if (ms < nearest) nearest = ms;
}
return nearest;
}
// Expires all overdue handshakes even if another endpoint remains busy.
static KSTATUS expire_starting_services(void) {
uint64_t now = SysGetTick();
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
INIT_SERVICE* service = &gServices[i];
if (service->State != InitServiceStarting) continue;
uint64_t elapsed = now >= service->StartedTick ? now - service->StartedTick : 0u;
uint64_t limit = ((uint64_t)service->Descriptor->ReadyTimeoutMilliseconds *
gTickFrequency + 999u) / 1000u;
if (elapsed < limit) continue;
debug_print("[init] READY timeout: ");
debug_print(service->Descriptor->Id);
debug_print("\n");
(void)SysKillProcess(service->Process, 1);
close_service_handles(service);
service->State = InitServiceFailed;
if (!queue_service_restart(service) && critical_service_affected((uint16_t)i))
return NTLSTATUS_FAILURE;
}
return NTLSTATUS_SUCCESS;
}
// Waits for the automatic graph to settle before the shell starts.
static KSTATUS start_automatic_services(SYS_HANDLE volume, SYS_HANDLE context) {
for (;;) {
KSTATUS status = start_pending_restarts(volume, context);
if (KSUCCESS(status)) status = launch_services(false, volume, context);
if (!KSUCCESS(status)) return status;
SYS_HANDLE waits[INIT_SERVICE_MAX_COUNT];
uint16_t indices[INIT_SERVICE_MAX_COUNT];
uint32_t count = 0;
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
if (gServices[i].State != InitServiceStarting) continue;
waits[count] = gServices[i].ControlEndpoint;
indices[count++] = (uint16_t)i;
}
if (!count) {
uint64_t retry = restart_timeout_ms(SysGetTick());
if (retry == SYS_WAIT_TIMEOUT_INFINITE) return NTLSTATUS_SUCCESS;
if (retry) (void)SysSleep(retry);
continue;
}
uint32_t selected = 0;
SYS_WAIT_MANY_REQUEST request = {0};
request.Size = sizeof(request);
request.Handles = waits;
request.Count = count;
request.SignaledIndexOut = &selected;
uint64_t now = SysGetTick();
uint64_t readyWait = starting_timeout_ms(now);
uint64_t retryWait = restart_timeout_ms(now);
request.TimeoutMilliseconds = readyWait < retryWait ? readyWait : retryWait;
if (request.TimeoutMilliseconds) {
status = SysWaitMany(&request);
if (status != NTLSTATUS_TIMEOUT && !KSUCCESS(status)) return status;
if (KSUCCESS(status)) {
INIT_SERVICE* service = &gServices[indices[selected]];
if (!KSUCCESS(complete_starting_service(service)) &&
!queue_service_restart(service) && critical_service_affected(indices[selected]))
return NTLSTATUS_FAILURE;
}
}
status = expire_starting_services();
if (!KSUCCESS(status)) return status;
}
}
// Requests graceful STOP and bounds the fallback termination of any service.
static void stop_service(INIT_SERVICE* service) {
if (!service || service->Process == SYS_HANDLE_INVALID) return;
service->State = InitServiceStopping;
NTL_SERVICE_CONTROL_MESSAGE message = {0};
message.Size = sizeof(message);
message.Version = NTL_SERVICE_CONTROL_VERSION;
message.Operation = NTL_SERVICE_CONTROL_STOP;
SYS_SEND_PORT_REQUEST send = {0};
send.Size = sizeof(send);
send.EndpointHandle = service->ControlEndpoint;
send.Payload = &message;
send.PayloadLength = sizeof(message);
uint64_t started = SysGetTick();
uint64_t duration = ((uint64_t)service->Descriptor->StopTimeoutMilliseconds *
gTickFrequency + 999u) / 1000u;
send.TimeoutMilliseconds = service->Descriptor->StopTimeoutMilliseconds;
(void)SysSendPort(&send);
uint64_t elapsed = SysGetTick() - started;
uint64_t remaining = elapsed >= duration ? 0u : duration - elapsed;
uint64_t remainingMs = gTickFrequency ?
(remaining / gTickFrequency) * 1000u +
((remaining % gTickFrequency) * 1000u + gTickFrequency - 1u) / gTickFrequency : 0u;
KSTATUS status = SysWaitSingle(service->Process, SYS_WAIT_FLAG_NONE, remainingMs);
if (!KSUCCESS(status)) {
(void)SysKillProcess(service->Process, 1);
status = SysWaitSingle(service->Process, SYS_WAIT_FLAG_NONE, 2000u);
if (!KSUCCESS(status)) debug_print("[init] service termination timed out\n");
}
close_service_handles(service);
service->State = InitServiceStopped;
}
// Shuts down the declared graph in reverse dependency order.
static void stop_all_services(void) {
for (uint32_t order = gServiceConfig.StartupCount; order > 0; --order)
stop_service(&gServices[gServiceConfig.StartupOrder[order - 1u]]);
}
// Quiesces dependent services before the provider generation changes.
static void stop_service_dependents(uint16_t providerIndex) {
for (uint32_t order = gServiceConfig.StartupCount; order > 0; --order) {
uint16_t index = gServiceConfig.StartupOrder[order - 1u];
if (index != providerIndex && service_depends_on(index, providerIndex))
stop_service(&gServices[index]);
}
}
// Starts the configured session image from the system volume.
static KSTATUS start_configured_child(SYS_HANDLE systemVolume, SYS_STRING_VIEW path, SYS_HANDLE* processOut) {
NTL_CREATE_PROCESS_AT_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
create.RootHandle = systemVolume;
create.Path = path;
create.CommandLine = path;
create.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
create.ModuleRootHandle = systemVolume;
create.ModuleDirectory = SYS_SV_LITERAL("system");
create.ResolveModuleName = NtlResolveSystemModuleName;
create.ProcessHandleOut = processOut;
return NtlCreateProcessAt(&create);
}
// Best-effort diagnostic name for init's current thread.
static void init_set_current_thread_name(const char* name) {
SYS_SET_THREAD_NAME_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.ThreadHandle = SYS_HANDLE_INVALID;
request.Name = SysSvFromCstr(name);
(void)SysSetThreadName(&request);
}
// Initializes init state and supervises configured services plus the native session process.
void _start(void) {
KSTATUS status = NtlInitializeExceptionRuntime();
if (!KSUCCESS(status)) exit_now((int)status);
init_set_current_thread_name("init.main");
SYS_HANDLE systemVolume = SysGetStartupCapability(NTL_BOOT_CAP_SYSTEM_VOLUME);
SYS_HANDLE namespaceVolume = SysGetStartupCapability(NTL_BOOT_CAP_NAMESPACE_ROOT);
SYS_HANDLE volumeManager = SysGetStartupCapability(NTL_BOOT_CAP_VOLUME_MANAGER);
SYS_HANDLE inputManager = SysGetStartupCapability(NTL_BOOT_CAP_INPUT_MANAGER);
SYS_HANDLE displayManager = SysGetStartupCapability(NTL_BOOT_CAP_DISPLAY_POLICY);
SYS_HANDLE registrationAuthority = SysGetStartupCapability(NTL_BOOT_CAP_RESOURCE_REGISTRATION);
SYS_HANDLE powerEvent = SYS_HANDLE_INVALID;
status = SysOpenPowerEvent(&powerEvent);
if (!KSUCCESS(status)) {
debug_print("[init] FATAL: power coordinator unavailable\n");
exit_now(1);
}
// Report the missing name so bootstrap ABI mismatches can be diagnosed directly.
const struct {
const char* Name;
SYS_HANDLE Handle;
} requiredBootstrap[] = {
{ NTL_BOOT_CAP_SYSTEM_VOLUME, systemVolume },
{ NTL_BOOT_CAP_NAMESPACE_ROOT, namespaceVolume },
{ NTL_BOOT_CAP_VOLUME_MANAGER, volumeManager },
{ NTL_BOOT_CAP_INPUT_MANAGER, inputManager },
{ NTL_BOOT_CAP_DISPLAY_POLICY, displayManager },
{ NTL_BOOT_CAP_RESOURCE_REGISTRATION, registrationAuthority },
};
for (uint32_t i = 0; i < sizeof(requiredBootstrap) / sizeof(requiredBootstrap[0]); ++i) {
if (requiredBootstrap[i].Handle != SYS_HANDLE_INVALID) continue;
debug_print("[init] FATAL: missing bootstrap capability: ");
debug_print(requiredBootstrap[i].Name);
debug_print("\n");
exit_now(1);
}
status = initialize_boot_grants(namespaceVolume, volumeManager, inputManager,
displayManager, registrationAuthority);
if (!KSUCCESS(status)) {
debug_print("[init] FATAL: bootstrap grant catalog initialization failed\n");
exit_now(1);
}
uint32_t configErrorLine = 0;
status = InitLoadServiceConfig(systemVolume, &gServiceConfig, &configErrorLine);
if (!KSUCCESS(status)) {
debug_print("[init] FATAL: invalid or unreadable services.ini");
if (configErrorLine != 0u) {
debug_print(" at line ");
debug_u32(configErrorLine);
}
debug_print(": ");
debug_status(status);
debug_print("\n");
exit_now(1);
}
SYS_HANDLE unprivilegedServiceContext = SYS_HANDLE_INVALID;
status = derive_unprivileged_service_context(&unprivilegedServiceContext);
if (!KSUCCESS(status)) {
debug_print("[init] FATAL: cannot derive unprivileged service context: ");
debug_status(status);
debug_print("\n");
exit_now(1);
}
SYS_STRING_VIEW childPath = SysSvFromCstr(gServiceConfig.SessionImage);
if (!childPath.Data || childPath.Length == 0) {
debug_print("[init] FATAL: no configured session process\n");
if (unprivilegedServiceContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(unprivilegedServiceContext);
exit_now(1);
}
if (gServiceConfig.StartupCount + 2u > SYS_WAIT_MANY_MAX_HANDLES) {
debug_print("[init] FATAL: service graph exceeds supervision wait capacity\n");
exit_now(1);
}
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i)
initialize_service(&gServices[i], &gServiceConfig.Services[i]);
status = SysGetTickFrequency(&gTickFrequency);
if (!KSUCCESS(status) || gTickFrequency == 0u || gTickFrequency > UINT64_MAX / 600000u) {
debug_print("[init] FATAL: monotonic clock unavailable for supervision\n");
stop_all_services();
exit_now(1);
}
status = start_automatic_services(systemVolume, unprivilegedServiceContext);
if (!KSUCCESS(status)) {
debug_print("[init] FATAL: automatic service startup failed\n");
stop_all_services();
exit_now(1);
}
for (;;) {
SYS_HANDLE child = SYS_HANDLE_INVALID;
status = start_configured_child(systemVolume, childPath, &child);
if (!KSUCCESS(status)) {
debug_print("[init] FATAL: session start failed: ");
debug_status(status);
debug_print("\n");
stop_all_services();
exit_now(1);
}
debug_print("init: session started\n");
status = launch_services(true, systemVolume, unprivilegedServiceContext);
if (!KSUCCESS(status)) {
stop_all_services();
exit_now(1);
}
bool restartChild = false;
for (;;) {
status = start_pending_restarts(systemVolume, unprivilegedServiceContext);
if (KSUCCESS(status)) status = launch_services(true, systemVolume, unprivilegedServiceContext);
if (!KSUCCESS(status) || !KSUCCESS(expire_starting_services())) {
debug_print("[init] FATAL: delayed service dependency failed\n");
stop_all_services();
exit_now(1);
}
SYS_HANDLE waits[SYS_WAIT_MANY_MAX_HANDLES];
uint32_t kinds[SYS_WAIT_MANY_MAX_HANDLES];
uint32_t waitCount = 0;
waits[waitCount] = powerEvent;
kinds[waitCount++] = UINT32_MAX;
waits[waitCount] = child;
kinds[waitCount++] = UINT32_MAX - 1u;
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
if (gServices[i].Process == SYS_HANDLE_INVALID) continue;
if (gServices[i].State != InitServiceReady &&
gServices[i].State != InitServiceStarting) continue;
waits[waitCount] = gServices[i].State == InitServiceStarting ?
gServices[i].ControlEndpoint : gServices[i].Process;
kinds[waitCount++] = i;
}
uint32_t signaled = 0;
SYS_WAIT_MANY_REQUEST wait = {0};
wait.Size = sizeof(wait);
wait.Handles = waits;
wait.Count = waitCount;
wait.SignaledIndexOut = &signaled;
uint64_t now = SysGetTick();
uint64_t readyWait = starting_timeout_ms(now);
uint64_t retryWait = restart_timeout_ms(now);
wait.TimeoutMilliseconds = readyWait < retryWait ? readyWait : retryWait;
status = SysWaitMany(&wait);
if (status == NTLSTATUS_TIMEOUT) continue;
if (!KSUCCESS(status)) {
debug_print("[init] FATAL: service supervision wait failed\n");
(void)SysKillProcess(child, 1);
(void)SysWait(child, SYS_WAIT_FLAG_NONE);
(void)SysCloseHandle(child);
stop_all_services();
exit_now(1);
}
if (kinds[signaled] == UINT32_MAX) {
uint32_t action = 0;
status = SysQuerySystemPower(&action);
if (!KSUCCESS(status) || !action) {
debug_print("[init] FATAL: invalid power request\n");
exit_now(1);
}
(void)SysKillProcess(child, 0);
(void)SysWaitSingle(child, SYS_WAIT_FLAG_NONE, 3000u);
(void)SysCloseHandle(child);
stop_all_services();
(void)SysCloseHandle(unprivilegedServiceContext);
(void)SysCloseHandle(powerEvent);
status = SysCompleteSystemPower(action);
if (!KSUCCESS(status)) {
debug_print("[init] FATAL: power commit rejected\n");
exit_now(1);
}
exit_now(0);
}
if (kinds[signaled] == UINT32_MAX - 1u) {
int32_t exitCode = 1;
(void)SysGetProcessExitCode(child, &exitCode);
(void)SysCloseHandle(child);
if (exitCode == 0) debug_print("[init] session ended; restarting\n");
else debug_print("[init] session crashed; restarting\n");
restartChild = true;
break;
}
uint16_t failedIndex = (uint16_t)kinds[signaled];
INIT_SERVICE* failed = &gServices[failedIndex];
if (failed->State == InitServiceStarting) {
KSTATUS readyStatus = complete_starting_service(failed);
if (!KSUCCESS(readyStatus) && !queue_service_restart(failed) &&
critical_service_affected(failedIndex)) {
debug_print("[init] FATAL: critical service READY failed\n");
(void)SysKillProcess(child, 1);
(void)SysWait(child, SYS_WAIT_FLAG_NONE);
(void)SysCloseHandle(child);
stop_all_services();
exit_now(1);
}
continue;
}
int32_t exitCode = -1;
(void)SysGetProcessExitCode(failed->Process, &exitCode);
debug_print("[init] service exited: ");
debug_print(failed->Descriptor->Id);
debug_print(" code=");
debug_i32(exitCode);
debug_print("\n");
stop_service_dependents(failedIndex);
close_service_handles(failed);
failed->State = InitServiceFailed;
const INIT_SERVICE_RESTART_POLICY restart = failed->Descriptor->RestartPolicy;
if ((restart == InitServiceRestartAlways ||
(restart == InitServiceRestartOnFailure && exitCode != 0)) &&
queue_service_restart(failed)) continue;
// Any affected critical service is a fatal dependency loss.
if (critical_service_affected(failedIndex)) {
(void)SysKillProcess(child, 1);
(void)SysWait(child, SYS_WAIT_FLAG_NONE);
(void)SysCloseHandle(child);
stop_all_services();
debug_print("[init] FATAL: critical service dependency lost\n");
exit_now(1);
}
}
if (!restartChild) break;
}
exit_now(1);
}