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

782 lines
34 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,
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;
} INIT_SERVICE;
static INIT_SERVICE_CONFIG gServiceConfig;
static INIT_SERVICE gServices[INIT_SERVICE_MAX_COUNT];
// 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]));
}
}
// Keeps the named READY exports for subsequent dependent services. Ownership of
// an export handle passes to the catalog only when retainHandles is true.
static KSTATUS register_ready_exports(const INIT_SERVICE_DESCRIPTOR* provider,
const NTL_SERVICE_CONTROL_MESSAGE* message,
const SYS_HANDLE* handles, uint32_t count,
bool retainHandles) {
if (!provider || !message || count != message->ExportCount) 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 i = 0; i < count; ++i) {
KSTATUS status = add_grant_source(names[i], handles[i],
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT,
provider, retainHandles);
if (!KSUCCESS(status)) {
// All names and available capacity were validated before mutation.
forget_service_grants(provider);
return status;
}
}
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))
return NTLSTATUS_FAILURE;
service->State = InitServiceStarting;
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;
status = NtlCreateProcessAt(&create);
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,
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 = service->Descriptor->ReadyTimeoutMilliseconds;
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;
}
// Every service uses the same process, READY, export and failure path.
static KSTATUS start_managed_service(SYS_HANDLE systemVolume,
SYS_HANDLE securityContext, INIT_SERVICE* service) {
KSTATUS status = start_service_process(systemVolume, securityContext, service);
if (!KSUCCESS(status)) {
debug_print("[init] process or startup grant creation failed for ");
debug_print(service->Descriptor->Id);
debug_print(": ");
debug_status(status);
debug_print("\n");
return status;
}
SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES];
uint32_t count = 0;
NTL_SERVICE_CONTROL_MESSAGE ready;
status = wait_service_ready(service, 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, true);
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;
}
// 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);
send.TimeoutMilliseconds = service->Descriptor->StopTimeoutMilliseconds;
(void)SysSendPort(&send);
KSTATUS status = SysWaitSingle(service->Process, SYS_WAIT_FLAG_NONE,
service->Descriptor->StopTimeoutMilliseconds);
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]]);
}
// 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;
}
// Before restarting a provider, quiesce every transitive consumer in reverse order.
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 eligible descendants again using the dependency-first startup order.
static KSTATUS restart_service_dependents(uint16_t providerIndex, SYS_HANDLE volume,
SYS_HANDLE context) {
for (uint32_t order = 0; order < gServiceConfig.StartupCount; ++order) {
uint16_t index = gServiceConfig.StartupOrder[order];
if (!service_depends_on(index, providerIndex)) continue;
KSTATUS status = start_managed_service(volume, context, &gServices[index]);
if (!KSUCCESS(status)) return status;
}
return NTLSTATUS_SUCCESS;
}
// Starts the configured native session process from the system-volume capability.
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 = SysGetPeb()->CommandLine;
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);
}
uint32_t autoCount = gServiceConfig.StartupCount;
if (autoCount + 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]);
for (uint32_t order = 0; order < gServiceConfig.StartupCount; ++order) {
uint16_t index = gServiceConfig.StartupOrder[order];
INIT_SERVICE* service = &gServices[index];
status = start_managed_service(systemVolume, unprivilegedServiceContext, service);
debug_print("[init] service ");
debug_print(service->Descriptor->Id);
if (KSUCCESS(status)) debug_print(" READY\n");
else {
debug_print(" startup failed: ");
debug_status(status);
debug_print("\n");
if (service->Descriptor->Critical) {
stop_all_services();
exit_now(1);
}
}
}
INIT_RESTART_BUDGET restartBudgets[INIT_SERVICE_MAX_COUNT] = {0};
uint64_t tickFrequency = 0;
status = SysGetTickFrequency(&tickFrequency);
if (!KSUCCESS(status) || tickFrequency == 0u) {
debug_print("[init] FATAL: monotonic clock unavailable for supervision\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 process started\n");
bool restartChild = false;
for (;;) {
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].State != InitServiceReady ||
gServices[i].Process == SYS_HANDLE_INVALID) continue;
waits[waitCount] = 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;
wait.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
status = SysWaitMany(&wait);
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];
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;
if (failed->Descriptor->RestartPolicy == InitServiceRestartOnFailure &&
InitConsumeRestartBudget(&restartBudgets[failedIndex],
SysGetTick(), tickFrequency)) {
status = start_managed_service(systemVolume, unprivilegedServiceContext, failed);
if (KSUCCESS(status))
status = restart_service_dependents(failedIndex, systemVolume, unprivilegedServiceContext);
if (KSUCCESS(status)) continue;
debug_print("[init] service restart failed\n");
}
// Any affected critical service is a fatal dependency loss.
bool fatal = failed->Descriptor->Critical;
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i)
if (gServices[i].Descriptor->Critical && service_depends_on((uint16_t)i, failedIndex))
fatal = true;
if (fatal) {
(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);
}